SPACE_SYNC_QUICK_REFERENCE.md 9.6 KB

空间数量同步 - 快速参考指南

📍 快速导航

四个阶段的空间数据来源

阶段 数据来源 查询方法 空间数量获取
🏷️ 订单分配 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️⃣ 订单分配阶段

// 文件: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️⃣ 报价明细

// 文件: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️⃣ 交付执行阶段

// 文件:stage-delivery.component.ts
// 行号:145-188

// 自动同步报价中的缺失空间
private async syncProductsWithQuotation(): Promise<void> {
  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️⃣ 设计师分配弹窗

// 文件: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表中的空间被删除

解决

// 在报价保存时调用
await this.syncQuotationToProducts();

// 或在交付执行时调用
await this.syncProductsWithQuotation();

问题2:设计师分配弹窗显示"未找到项目空间数据"

症状:弹窗打开时显示空间加载错误

原因

  • Product表中没有该项目的空间记录
  • projectId参数错误

解决

// 检查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:设计师空间分配未保存

症状:分配空间后刷新页面,分配信息消失

原因

  • 空间分配结果未持久化到数据库
  • 只保存在组件内存中

解决

// 在确认分配时保存
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

// 查询项目空间
getProjectProductSpaces(projectId: string): Promise<ProductSpace[]>

// 创建空间
createProductSpace(projectId: string, spaceData: Partial<ProductSpace>): Promise<ProductSpace>

// 更新空间
updateProductSpace(spaceId: string, updates: Partial<ProductSpace>): Promise<void>

// 删除空间(软删除)
deleteProductSpace(spaceId: string): Promise<void>

// 计算空间进度
calculateProductProgress(spaceId: string, processTypes: string[]): number

stage-order.component.ts

// 加载空间
loadProjectSpaces(): Promise<void>

// 从报价创建空间
createSpacesFromQuotation(quotationSpaces: any[]): Promise<void>

// 推断空间类型
inferSpaceType(spaceName: string): string

// 计算空间预算
calculateSpaceRate(spaceData: any): number

// 从空间生成报价
regenerateQuotationFromSpaces(): Promise<void>

// 更新空间占比
updateSpaceBreakdown(): void

stage-delivery.component.ts

// 同步报价中的空间到Product表
syncProductsWithQuotation(): Promise<void>

designer-team-assignment-modal.component.ts

// 加载真实空间数据
loadRealProjectSpaces(): Promise<void>

// 获取设计师分配的空间
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?
  • 删除空间时是否同时更新了报价?
  • 修改空间名称时是否同时更新了报价?

📝 实现检查清单

必须实现

  • Product表存储空间数据
  • 订单分配自动创建缺失空间
  • 交付执行自动同步空间
  • 设计师分配加载空间

需要改进

  • 报价保存时同步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()方法
    • 自动修复不一致的数据