# 空间数量同步 - 快速参考指南 ## 📍 快速导航 ### 四个阶段的空间数据来源 | 阶段 | 数据来源 | 查询方法 | 空间数量获取 | |------|---------|---------|------------| | 🏷️ 订单分配 | Product表 | `productSpaceService.getProjectProductSpaces(projectId)` | `projectSpaces.length` | | 💰 报价明细 | Project.data | `project.get('data').quotation.spaces` | `quotation.spaces.length` | | 📦 交付执行 | Product表 | `productSpaceService.getProjectProductSpaces(projectId)` | `projectProducts.length` | | 👥 设计师分配 | Product表 | `productSpaceService.getProjectProductSpaces(projectId)` | `spaceScenes.length` | --- ## 🔑 关键字段存储位置 ### Product 表(Parse Server) ``` Product ├─ id: string // 空间ID ├─ name: string // 空间名称 ├─ type: string // 空间类型 ├─ area: number // 面积 ├─ status: string // 状态 ├─ complexity: string // 复杂度 ├─ estimatedBudget: number // 预算 ├─ order: number // 排序 ├─ project: Pointer // 关联项目 ├─ company: Pointer // 关联公司 └─ isDeleted: boolean // 软删除标记 ``` ### Project 表 → data 字段 ``` Project.data ├─ quotation: { │ ├─ spaces: [{ │ │ ├─ name: string // 空间名称 │ │ ├─ spaceId: string // 关联Product ID │ │ ├─ processes: {...} // 工序明细 │ │ └─ subtotal: number // 小计 │ │}] │ ├─ total: number // 总价 │ └─ spaceBreakdown: [{...}] // 空间占比 ├─ spaceSpecialRequirements: { │ ├─ [spaceId]: { │ │ ├─ content: string // 特殊需求内容 │ │ ├─ updatedAt: Date // 更新时间 │ │ └─ updatedBy: string // 更新人 │ └─ } └─ designerSpaceAssignments: { ├─ [designerId]: string[] // 设计师分配的空间ID列表 └─ } ``` --- ## 🔄 同步流程速查 ### 1️⃣ 订单分配阶段 ```typescript // 文件:stage-order.component.ts // 行号:695-758 // 加载空间 this.projectSpaces = await this.productSpaceService .getProjectProductSpaces(this.project.id); // 自动同步缺失空间 if (this.projectSpaces.length > 0) { const missing = quotationSpaces.filter(s => !existingNames.has(s.name)); for (const spaceData of missing) { await this.productSpaceService.createProductSpace( this.project.id, { name: spaceData.name, type: this.inferSpaceType(spaceData.name) } ); } } // 生成报价 await this.regenerateQuotationFromSpaces(); ``` ### 2️⃣ 报价明细 ```typescript // 文件:quotation-editor.component.ts // 读取报价空间 const quotationSpaces = this.project.get('data').quotation.spaces; // 空间数量 const spaceCount = quotationSpaces.length; // 通过spaceId关联Product quotationSpaces.forEach(space => { const productId = space.spaceId; // 指向Product.id }); ``` ### 3️⃣ 交付执行阶段 ```typescript // 文件:stage-delivery.component.ts // 行号:145-188 // 自动同步报价中的缺失空间 private async syncProductsWithQuotation(): Promise { const quotationSpaces = this.project.get('data').quotation.spaces; const existingProducts = await this.productSpaceService .getProjectProductSpaces(this.project.id); // 找出缺失的空间并创建 const missing = quotationSpaces.filter(s => !existingProducts.some(p => p.name === s.name) ); for (const s of missing) { await this.productSpaceService.createProductSpace( this.project.id, { name: s.name, type: 'other' } ); } } ``` ### 4️⃣ 设计师分配弹窗 ```typescript // 文件:designer-team-assignment-modal.component.ts // 行号:951-987 // 加载真实空间数据 async loadRealProjectSpaces() { this.parseProducts = await this.productSpaceService .getProjectProductSpaces(this.projectId); // 转换为SpaceScene格式 this.spaceScenes = this.parseProducts.map(product => ({ id: product.id, name: product.name, area: product.area, description: this.getProductDescription(product) })); } // 获取设计师分配的空间 getDesignerSpacesText(designerId: string): string { const spaces = this.getDesignerSpaces(designerId); return spaces.map(s => s.name).join(', '); } ``` --- ## ⚠️ 常见问题速查 ### 问题1:空间数量不一致 **症状**:报价显示5个空间,交付执行显示3个空间 **原因**: - 报价中添加了新空间,但未同步到Product表 - 或Product表中的空间被删除 **解决**: ```typescript // 在报价保存时调用 await this.syncQuotationToProducts(); // 或在交付执行时调用 await this.syncProductsWithQuotation(); ``` ### 问题2:设计师分配弹窗显示"未找到项目空间数据" **症状**:弹窗打开时显示空间加载错误 **原因**: - Product表中没有该项目的空间记录 - projectId参数错误 **解决**: ```typescript // 检查projectId是否正确 console.log('projectId:', this.projectId); // 确保Product表中有数据 const spaces = await this.productSpaceService .getProjectProductSpaces(this.projectId); console.log('spaces:', spaces); // 如果为空,从报价创建 if (spaces.length === 0) { await this.createSpacesFromQuotation( this.project.get('data').quotation.spaces ); } ``` ### 问题3:设计师空间分配未保存 **症状**:分配空间后刷新页面,分配信息消失 **原因**: - 空间分配结果未持久化到数据库 - 只保存在组件内存中 **解决**: ```typescript // 在确认分配时保存 async onConfirmAssignment(result: DesignerAssignmentResult) { // 保存空间分配到Project.data const data = this.project.get('data') || {}; data.designerSpaceAssignments = {}; for (const assignment of result.spaceAssignments) { data.designerSpaceAssignments[assignment.designerId] = assignment.spaceIds; } this.project.set('data', data); await this.project.save(); } ``` --- ## 🛠️ 核心方法一览 ### ProductSpaceService ```typescript // 查询项目空间 getProjectProductSpaces(projectId: string): Promise // 创建空间 createProductSpace(projectId: string, spaceData: Partial): Promise // 更新空间 updateProductSpace(spaceId: string, updates: Partial): Promise // 删除空间(软删除) deleteProductSpace(spaceId: string): Promise // 计算空间进度 calculateProductProgress(spaceId: string, processTypes: string[]): number ``` ### stage-order.component.ts ```typescript // 加载空间 loadProjectSpaces(): Promise // 从报价创建空间 createSpacesFromQuotation(quotationSpaces: any[]): Promise // 推断空间类型 inferSpaceType(spaceName: string): string // 计算空间预算 calculateSpaceRate(spaceData: any): number // 从空间生成报价 regenerateQuotationFromSpaces(): Promise // 更新空间占比 updateSpaceBreakdown(): void ``` ### stage-delivery.component.ts ```typescript // 同步报价中的空间到Product表 syncProductsWithQuotation(): Promise ``` ### designer-team-assignment-modal.component.ts ```typescript // 加载真实空间数据 loadRealProjectSpaces(): Promise // 获取设计师分配的空间 getDesignerSpaces(designerId: string): SpaceScene[] // 获取设计师空间文本 getDesignerSpacesText(designerId: string): string // 检查空间是否被选中 isSpaceSelected(designerId: string, spaceId: string): boolean // 切换空间选择 toggleSpaceSelection(designerId: string, spaceId: string): void ``` --- ## 📊 数据一致性检查清单 - [ ] Product表中的空间数量 = quotation.spaces.length? - [ ] 每个quotation.space都有对应的spaceId? - [ ] spaceId是否指向有效的Product记录? - [ ] 设计师空间分配是否已保存到Project.data? - [ ] 删除空间时是否同时更新了报价? - [ ] 修改空间名称时是否同时更新了报价? --- ## 📝 实现检查清单 ### 必须实现 - [x] Product表存储空间数据 - [x] 订单分配自动创建缺失空间 - [x] 交付执行自动同步空间 - [x] 设计师分配加载空间 ### 需要改进 - [ ] 报价保存时同步Product表 - [ ] 设计师空间分配持久化 - [ ] 空间数据一致性验证 - [ ] 删除空间时的级联处理 --- ## 🔗 相关文件快速链接 | 功能 | 文件路径 | 关键行号 | |------|---------|---------| | 空间服务 | `src/modules/project/services/product-space.service.ts` | - | | 订单分配 | `src/modules/project/pages/project-detail/stages/stage-order.component.ts` | 695-1008 | | 交付执行 | `src/modules/project/pages/project-detail/stages/stage-delivery.component.ts` | 145-188 | | 设计师分配 | `src/app/pages/designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component.ts` | 84-88, 296-298, 951-987, 1305-1315 | --- ## 💡 最佳实践 1. **始终通过ProductSpaceService查询空间** - 不要直接查询Product表 - 使用统一的服务方法 2. **在保存报价时同步Product表** - 确保Product表与报价数据一致 - 防止数据不同步 3. **使用spaceId关联Product和报价** - 不要依赖空间名称作为唯一标识 - 名称可能会改变 4. **设计师空间分配要持久化** - 保存到Project.data.designerSpaceAssignments - 支持跨会话恢复 5. **定期验证数据一致性** - 实现validateSpaceSync()方法 - 自动修复不一致的数据