SPACE_DATA_SYNC_FIX.md 12 KB

空间数据同步修复方案(最新版)

🎯 问题描述

用户反馈

不同阶段显示的空间数量和名称不一致:

  • 订单分配阶段:5个空间(卫生间、餐厅、次卧、厨房、餐厅)
  • 确认需求阶段:9个空间(餐厅x2、主卧、次卧、书房、儿童房、厨房、卫生间、阳台)
  • 交付执行阶段:8个空间(餐厅x2、主卧、次卧、书房、儿童房、厨房、卫生间)
  • 确认需求阶段:显示 9个空间(客厅、餐厅、主卧、次卧、儿童房、书房、厨房、卫生间、阳台)
  • 交付执行阶段:显示 9个空间(同上)

结果:各阶段显示的空间数量不一致,数据不同步。


🎯 根本原因分析

数据源不一致

1. 订单分配阶段(quotation-editor)

数据来源:直接查询 Product

// quotation-editor.component.ts 第276-281行
const productQuery = new Parse.Query('Product');
productQuery.equalTo('project', this.project.toPointer());
const results = await productQuery.find();
// ✅ 查询到4条Product记录
this.products = results; // 去重后4个

显示逻辑

  • 从Product表查询 → 4条记录
  • 去重后 → 4个空间
  • 生成报价 → 4个空间显示

2. 确认需求/交付执行阶段

数据来源:从 Project.data.unifiedSpaces 读取

// stage-requirements.component.ts 第366行
const unifiedSpaces = await this.productSpaceService.getUnifiedSpaceData(projectId);
// ✅ 从Project.data.unifiedSpaces读取 → 9个空间

getUnifiedSpaceData() 逻辑

// product-space.service.ts 第571-582行
async getUnifiedSpaceData(projectId: string) {
  const data = project.get('data') || {};
  
  // 优先返回统一空间数据
  if (data.unifiedSpaces && data.unifiedSpaces.length > 0) {
    return data.unifiedSpaces; // ✅ 返回9个空间
  }
  
  // 否则从Product表迁移
  const productSpaces = await this.getProjectProductSpaces(projectId);
  return productSpaces;
}

数据流对比

【订单分配】
    ↓ 查询
Product表 (4条记录)
    ↓ 显示
订单分配界面 (4个空间) ❌

【确认需求/交付执行】
    ↓ 查询
Project.data.unifiedSpaces (9个空间)
    ↓ 显示
确认需求/交付执行界面 (9个空间) ❌

核心问题

  1. Product表只有4条记录
  2. 但Project.data.unifiedSpaces有9个空间
  3. 两个数据源不同步

🔧 解决方案

修复策略

确保订单分配(数据源头)在保存时同步更新 unifiedSpaces

订单分配阶段是数据的唯一入口,其他阶段应该从它同步数据。因此需要:

  1. 订单分配修改Product表
  2. 同时更新 Project.data.unifiedSpaces
  3. 其他阶段从 unifiedSpaces 读取

实现修改

文件quotation-editor.component.ts
位置:第664-713行
方法saveQuotationToProject()

修改前

private async saveQuotationToProject(): Promise<void> {
  if (!this.project) return;

  try {
    const data = this.project.get('data') || {};
    data.quotation = this.quotation;
    this.project.set('data', data);
    await this.project.save();

    this.quotationChange.emit(this.quotation);
  } catch (error) {
    console.error('保存报价失败:', error);
  }
}

问题:只保存了 quotation,没有更新 unifiedSpaces

修改后

private async saveQuotationToProject(): Promise<void> {
  if (!this.project) return;

  try {
    const data = this.project.get('data') || {};
    data.quotation = this.quotation;
    
    // 🔥 关键修复:同步更新 unifiedSpaces,确保各阶段数据一致
    data.unifiedSpaces = this.products.map((product, index) => {
      const quotation = product.get('quotation') || {};
      const space = product.get('space') || {};
      const requirements = product.get('requirements') || {};
      const profile = product.get('profile');
      
      // 从quotation.spaces中查找对应的工序信息
      const quotationSpace = this.quotation.spaces.find((s: any) => s.productId === product.id);
      
      return {
        id: product.id,
        name: product.get('productName') || '',
        type: product.get('productType') || 'other',
        area: space.area || 0,
        priority: space.priority || 5,
        status: product.get('status') || 'not_started',
        complexity: space.complexity || 'medium',
        estimatedBudget: quotation.price || 0,
        order: index,
        quotation: {
          price: quotation.price || 0,
          processes: quotationSpace?.processes || {},
          subtotal: quotationSpace?.subtotal || 0
        },
        requirements: requirements,
        designerId: profile?.id || null,
        progress: [],
        createdAt: product.get('createdAt')?.toISOString() || new Date().toISOString(),
        updatedAt: new Date().toISOString()
      };
    });
    
    console.log(`🔄 [报价编辑器] 同步更新 unifiedSpaces: ${data.unifiedSpaces.length} 个空间`);
    
    this.project.set('data', data);
    await this.project.save();

    this.quotationChange.emit(this.quotation);
  } catch (error) {
    console.error('保存报价失败:', error);
  }
}

修复内容

  1. this.products 数组构建 unifiedSpaces
  2. 包含完整的空间信息(name, type, area, quotation, requirements等)
  3. 同时保存到 Project.data.unifiedSpaces
  4. 添加日志输出,便于调试

数据同步流程(修复后)

订单分配阶段修改空间
    ↓
quotation-editor.addProduct()
    ↓
createProduct() → Product表新增记录
    ↓
loadProjectProducts() → 重新加载Product表
    ↓
generateQuotationFromProducts() → 生成报价
    ↓
saveQuotationToProject() → 保存到Project
    ├─ data.quotation = {...}
    └─ data.unifiedSpaces = [...] 🔥 同步更新
    ↓
this.products (4个) === Project.data.unifiedSpaces (4个) ✅
    ↓
发出 productsUpdated 事件
    ↓
其他阶段重新加载
    ├─ 确认需求:从unifiedSpaces读取 → 4个空间 ✅
    └─ 交付执行:从unifiedSpaces读取 → 4个空间 ✅

🎯 预期效果

修复后的行为

  1. 添加空间

    订单分配点击"添加产品" → 输入"书房"
    ↓
    Product表新增1条记录(总共5条)
    ↓
    saveQuotationToProject() 同步更新 unifiedSpaces(5个空间)
    ↓
    确认需求阶段:显示5个空间 ✅
    交付执行阶段:显示5个空间 ✅
    
  2. 删除空间

    订单分配删除"餐厅"
    ↓
    Product表删除1条记录(总共3条)
    ↓
    saveQuotationToProject() 同步更新 unifiedSpaces(3个空间)
    ↓
    确认需求阶段:显示3个空间 ✅
    交付执行阶段:显示3个空间 ✅
    
  3. 编辑空间

    订单分配修改"客厅"为"大客厅"
    ↓
    Product表更新记录
    ↓
    saveQuotationToProject() 同步更新 unifiedSpaces
    ↓
    确认需求阶段:显示"大客厅" ✅
    交付执行阶段:显示"大客厅" ✅
    

📊 验证步骤

步骤1:清理旧数据(可选)

如果当前项目的 unifiedSpaces 数据不正确(有9个空间),需要先清理:

  1. 在浏览器控制台执行:

    const Parse = window.Parse;
    const query = new Parse.Query('Project');
    const project = await query.get('YOUR_PROJECT_ID');
    const data = project.get('data') || {};
       
    console.log('当前unifiedSpaces:', data.unifiedSpaces?.length);
       
    // 清空旧数据
    delete data.unifiedSpaces;
    project.set('data', data);
    await project.save();
       
    console.log('✅ 已清空unifiedSpaces,等待从订单分配重新生成');
    

步骤2:重新生成unifiedSpaces

  1. 打开项目的"订单分配"阶段
  2. 打开浏览器控制台(F12)
  3. 观察日志:

    🔍 [报价编辑器] Product表查询结果: 4 条记录
     ✅ 保留空间: 客厅 (ID: xxx)
     ✅ 保留空间: 次卧 (ID: xxx)
     ✅ 保留空间: 阳台 (ID: xxx)
     ✅ 保留空间: 餐厅 (ID: xxx)
    ✅ [报价编辑器] 最终加载 4 个唯一空间
    
  4. 页面会自动调用 generateQuotationFromProducts()

  5. 然后调用 saveQuotationToProject()

  6. 观察日志:

    🔄 [报价编辑器] 同步更新 unifiedSpaces: 4 个空间
    

步骤3:验证其他阶段

  1. 切换到"确认需求"阶段
  2. 观察控制台日志:

    ✅ [需求确认] 从统一存储加载到 4 个空间
    ✅ [需求确认] 空间加载完成,共 4 个空间
    
  3. 验证界面显示:应该显示4个空间(与订单分配一致)

  4. 切换到"交付执行"阶段

  5. 观察控制台日志:

    ✅ [交付执行] 从统一存储加载到 4 个空间
    ✅ [交付执行] 空间加载完成,共 4 个空间
    
  6. 验证界面显示:应该显示4个空间(与订单分配一致)

步骤4:测试实时同步

  1. 在"订单分配"阶段点击"添加产品"
  2. 输入"书房",保存
  3. 观察日志:

    ✅ [产品变更] 已添加产品,当前数量: 5
    🔄 [报价编辑器] 同步更新 unifiedSpaces: 5 个空间
    
  4. 切换到"确认需求"或"交付执行"

  5. 观察日志(如果已实现监听器):

    🔄 [确认需求] 检测到产品添加事件,重新加载空间...
    ✅ [确认需求] 空间加载完成,共 5 个空间
    
  6. 验证:所有阶段都显示5个空间


📝 额外说明

关于Product表中只有4条记录的问题

如果确认Product表确实只有4条记录,但界面应该显示9个空间(业务需要),那么需要:

  1. 补充缺失的Product记录

    • 在订单分配阶段点击"添加产品"
    • 手动添加缺失的5个空间(主卧、儿童房、书房、厨房、卫生间)
  2. 或者批量创建

    • 在浏览器控制台执行:

      const Parse = window.Parse;
      const projectId = 'YOUR_PROJECT_ID';
      const projectPointer = { __type: 'Pointer', className: 'Project', objectId: projectId };
         
      const missingSpaces = ['主卧', '儿童房', '书房', '厨房', '卫生间'];
         
      for (const spaceName of missingSpaces) {
      const product = new Parse.Object('Product');
      product.set('project', projectPointer);
      product.set('productName', spaceName);
      product.set('productType', 'other');
      product.set('status', 'not_started');
      product.set('space', {
      spaceName: spaceName,
      area: 0,
      spaceType: '平层',
      styleLevel: '基础风格组',
      complexity: 'medium',
      priority: 5
      });
      product.set('quotation', {
      price: 300,
      basePrice: 300,
      currency: 'CNY',
      status: 'draft'
      });
      await product.save();
      console.log(`✅ 已创建空间: ${spaceName}`);
      }
         
      console.log('✅ 批量创建完成,请刷新页面');
      
  3. 刷新页面

    • F5刷新订单分配页面
    • quotation-editor会重新加载Product表(现在9条记录)
    • 自动生成报价并同步更新unifiedSpaces(9个空间)
    • 其他阶段也会显示9个空间

🎉 修复总结

修改的文件

  1. quotation-editor.component.ts
    • 第282-307行:添加详细日志输出
    • 第664-713行:修复 saveQuotationToProject() 同步更新 unifiedSpaces

核心改进

  1. ✅ 订单分配保存时同步更新 unifiedSpaces
  2. ✅ 确保各阶段从统一数据源读取
  3. ✅ 添加详细日志便于问题排查
  4. ✅ 结合之前的实时同步机制,实现完整的跨阶段数据同步

数据一致性保证

  • 写入:订单分配 → Product表 + unifiedSpaces
  • 读取:其他阶段 → unifiedSpaces
  • 同步:实时事件通知机制

修复完成时间:2024年11月15日 14:45
问题状态:已修复,等待测试验证