项目-空间任务逻辑.md 71 KB

项目多产品任务逻辑设计 (基于Project表统一管理)

1. 多产品场景概述

1.1 业务背景

映三色设计师项目管理系统采用Project表统一管理空间设计产品。每个Project代表一个独立的空间设计产品,支持从单空间到复杂多空间项目的灵活管理。通过Product表的统一架构,实现设计产品化的项目管理模式。

1.2 多产品分布特征

  • 单产品设计项目:占比约70%,主要针对单个空间产品设计(如客厅设计、卧室设计)
  • 双产品设计项目:占比约20%,通常是客餐厅一体化、主卧+衣帽间等组合设计
  • 多产品设计项目:占比约10%,全屋设计、别墅等多空间综合项目设计

1.3 现有系统架构

当前系统采用Project表的统一管理架构:

  • 每个Project代表一个独立的空间设计产品
  • Project表通过productId字段关联具体产品
  • 支持多Project的统一管理和协调
  • 产品化设计流程和标准化交付

2. 基于Project表的数据模型设计

2.1 Project表统一管理

2.1.1 Project 表作为产品设计管理核心

interface Project {
  // 产品基础信息
  id: string;
  name: string;                           // 产品名称:"李总主卧设计"
  type: string;                           // 产品类型:"bedroom", "living_room" 等
  status: 'not_started' | 'in_progress' | 'awaiting_review' | 'completed';
  priority: number;                       // 优先级 1-10
  complexity: 'simple' | 'medium' | 'complex';

  // 空间属性(产品设计维度)
  area?: number;                          // 空间面积
  metadata?: {
    dimensions?: {                       // 空间尺寸
      length: number;
      width: number;
      height: number;
    };
    features?: string[];                  // 空间特征:["朝南", "飘窗", "独立卫浴"]
    constraints?: string[];               // 约束条件:["承重墙不可动"]
    style?: string;                       // 设计风格
    specialRequirements?: string[];       // 特殊要求
  };

  // 产品需求字段
  requirements?: {
    colorRequirement: Object;             // 色彩需求
    spaceStructureRequirement: Object;   // 空间结构需求
    materialRequirement: Object;         // 材质需求
    lightingRequirement: Object;         // 灯光需求
    specificRequirements?: string[];      // 特定需求
  };

  // 产品报价字段
  quotation?: {
    price: number;                        // 产品价格
    currency: string;                     // "CNY"
    breakdown: {
      design: number;                     // 设计费
      modeling: number;                   // 建模费
      rendering: number;                  // 渲染费
      softDecor: number;                  // 软装费
    };
    status: string;                       // "pending" | "approved"
    validUntil: Date;
  };

  // 产品评价字段
  reviews?: Array<Object>;

  // 关联信息
  projectId: string;                      // 所属主项目ID
  designerId?: string;                    // 负责设计师ID
  estimatedBudget?: number;               // 预估预算
  estimatedDuration?: number;             // 预估工期
  order: number;                          // 排序顺序

  // 时间信息
  createdAt: Date;
  updatedAt: Date;
}

enum ProductType {
  LIVING_ROOM = 'living_room',      // 客厅
  BEDROOM = 'bedroom',              // 卧室
  KITCHEN = 'kitchen',              // 厨房
  BATHROOM = 'bathroom',            // 卫生间
  DINING_ROOM = 'dining_room',      // 餐厅
  STUDY = 'study',                  // 书房
  BALCONY = 'balcony',              // 阳台
  CORRIDOR = 'corridor',            // 走廊
  STORAGE = 'storage',              // 储物间
  ENTRANCE = 'entrance',            // 玄关
  OTHER = 'other'                   // 其他
}

2.1.2 产品进度管理

interface ProductProgress {
  productId: string;                       // 关联产品ID
  stage: ProjectStage;                     // 当前阶段
  progress: number;                        // 进度百分比 0-100
  status: ProgressStatus;
  timeline: StageTimeline[];               // 各阶段时间线
  blockers?: string[];                     // 阻碍因素
  estimatedCompletion?: Date;              // 预计完成时间
  actualCompletion?: Date;                 // 实际完成时间
}

interface StageTimeline {
  stage: ProjectStage;
  startTime?: Date;
  endTime?: Date;
  duration?: number;                       // 持续时间(小时)
  status: 'not_started' | 'in_progress' | 'completed' | 'blocked';
  assignee?: string;                       // 负责人ID
  deliverables?: string[];                 // 交付物
  quality?: {
    score: number;                         // 质量评分
    issues: string[];                      // 问题点
  };
}

enum ProgressStatus {
  NOT_STARTED = 'not_started',
  IN_PROGRESS = 'in_progress',
  AWAITING_REVIEW = 'awaiting_review',
  COMPLETED = 'completed',
  BLOCKED = 'blocked',
  DELAYED = 'delayed'
}

2.1.3 产品人员分配

interface ProductAssignment {
  productId: string;                       // 产品ID
  stage: ProjectStage;                     // 阶段
  assigneeId: string;                      // 负责人ID
  assigneeName: string;                    // 负责人姓名
  role: AssignmentRole;                    // 分配角色
  assignedAt: Date;                        // 分配时间
  assignedBy: string;                      // 分配人ID
  status: 'active' | 'completed' | 'reassigned';
  workload: number;                        // 工作量占比 0-1
  notes?: string;                          // 分配备注
  performance?: {
    efficiency: number;                    // 工作效率
    quality: number;                       // 工作质量
    collaboration: number;                // 协作能力
  };
}

enum AssignmentRole {
  PRIMARY_DESIGNER = 'primary_designer',    // 主设计师
  MODELING_DESIGNER = 'modeling_designer',  // 建模师
  RENDERING_DESIGNER = 'rendering_designer',// 渲染师
  SOFT_DECOR_DESIGNER = 'soft_decor_designer', // 软装师
  QUALITY_REVIEWER = 'quality_reviewer'     // 质量审核员
}

2.2 各阶段数据结构适配

2.2.1 订单分配阶段多产品适配

// 扩展 主项目Project 数据结构
interface MultiProductProject {
  id: string;
  title: string;
  status: string;

  // 产品管理
  products: Project[];                     // 产品列表

  // 全局报价信息
  quotation: {
    totalAmount: number;                   // 总金额
    currency: string;                      // 货币单位

    // 按产品分项报价
    productQuotations: ProductQuotation[];

    // 按费用类型汇总
    breakdown: {
      design: number;                     // 设计费
      modeling: number;                   // 建模费
      rendering: number;                  // 渲染费
      softDecor: number;                  // 软装费
      postProcess: number;                // 后期费
    };

    // 折扣信息
    discount?: {
      type: 'percentage' | 'fixed';
      value: number;
      reason: string;
    };
  };

  // 项目协调信息
  coordination: {
    dependencies: ProductDependency[];    // 产品依赖关系
    batchOperations: BatchOperation[];    // 批量操作
    qualityStandards: QualityStandard[];  // 质量标准
  };
}

interface ProductQuotation {
  productId: string;                      // 产品ID
  productName: string;                    // 产品名称
  productType: ProductType;               // 产品类型
  amount: number;                         // 该产品金额
  items: QuotationItem[];                 // 报价项明细
  priority: number;                       // 优先级
  estimatedDays: number;                  // 预估工期
  notes?: string;                         // 备注
}

interface ProductDependency {
  fromProduct: string;                     // 源产品ID
  toProduct: string;                       // 目标产品ID
  type: 'style_reference' | 'color_flow' | 'material_matching' | 'size_reference' | 'functional_connection';
  description: string;                    // 依赖描述
  status: 'pending' | 'satisfied' | 'blocked';
  priority: number;                       // 依赖优先级
}

2.2.2 需求确认阶段多产品适配

// 扩展 ProjectRequirement 数据结构
interface MultiProductRequirement {
  products: ProductRequirement[];         // 产品需求列表
  globalRequirements: GlobalRequirements; // 全局需求
  crossProductRequirements: CrossProductRequirement[]; // 跨产品需求
}

interface ProductRequirement {
  productId: string;                      // 产品ID
  productName: string;                    // 产品名称
  productType: ProductType;               // 产品类型

  // 四大核心需求数据
  colorRequirement: ColorAtmosphereRequirement;
  spaceStructureRequirement: SpaceStructureRequirement;
  materialRequirement: MaterialRequirement;
  lightingRequirement: LightingRequirement;

  // 产品特定需求
  specificRequirements: {
    functional?: string[];                // 功能需求:收纳、展示等
    style?: string[];                     // 风格偏好
    constraints?: string[];               // 限制条件:承重、管道等
    specialFeatures?: string[];           // 特殊功能:智能家居、无障碍设计等
  };

  // 参考资料管理
  referenceImages?: string[];             // 参考图片
  referenceFiles?: any[];                 // 参考文件

  priority: number;                       // 优先级
  complexity: 'simple' | 'medium' | 'complex'; // 复杂度
}

interface GlobalRequirements {
  overallStyle: string;                   // 整体风格
  budget: {
    total: number;
    currency: string;
    breakdown?: Record<string, number>;
  };
  timeline: {
    preferredStartDate?: Date;
    deadline: Date;
    milestones?: Array<{
      date: Date;
      description: string;
    }>;
  };
  familyComposition: string;             // 家庭构成
  lifestyle: string[];                   // 生活习惯
  qualityStandards: string[];            // 质量标准
}

interface CrossProductRequirement {
  type: 'style_consistency' | 'color_flow' | 'material_matching' | 'traffic_flow' | 'functional_connection';
  description: string;                   // 跨产品需求描述
  involvedProducts: string[];            // 涉及的产品ID列表
  priority: number;                      // 优先级
  impact: 'high' | 'medium' | 'low';     // 影响程度
}

2.2.3 交付执行阶段多产品适配

// 扩展现有的 deliveryProcesses 数据结构
interface MultiProductDeliveryProcess {
  processId: string;                      // 流程ID:modeling、softDecor、rendering、postProcess
  processName: string;                    // 流程名称

  // 产品管理(增强版)
  products: DeliveryProduct[];

  // 按产品组织的内容
  content: Record<string, ProductContent>;

  // 跨产品协调
  crossProductCoordination: {
    dependencies: ProductDependency[];    // 产品依赖关系
    batchOperations: BatchOperation[];    // 批量操作
    qualityStandards: QualityStandard[];  // 质量标准
    designReviews: DesignReview[];        // 设计评审
  };

  // 整体进度管理
  overallProgress: {
    total: number;                        // 总体进度
    byProduct: Record<string, number>;    // 各产品进度
    byStage: Record<string, number>;      // 各阶段进度
    estimatedCompletion: Date;
    riskIndicators: RiskIndicator[];      // 风险指标
  };
}

interface DeliveryProduct {
  productId: string;                      // 产品ID
  productName: string;                    // 产品名称
  productType: ProductType;               // 产品类型
  priority: number;                       // 优先级
  status: string;                         // 状态

  // 交付内容
  deliverables: {
    concepts: ConceptDesign[];            // 概念设计
    models: ThreeDModel[];                // 三维模型
    renderings: Rendering[];              // 渲染图
    documents: TechnicalDocument[];       // 技术文档
  };

  // 质量控制
  quality: {
    designReview: DesignReviewResult;     // 设计评审结果
    technicalValidation: ValidationResult; // 技术验证结果
    clientFeedback: FeedbackRecord[];     // 客户反馈
  };
}

interface DesignReview {
  id: string;
  type: 'internal' | 'client' | 'technical';
  participants: string[];                // 参与者ID
  scheduledDate: Date;                   // 计划日期
  duration: number;                      // 评审时长(分钟)
  status: 'scheduled' | 'in_progress' | 'completed' | 'cancelled';

  // 评审内容
  criteria: ReviewCriteria[];            // 评审标准
  findings: ReviewFinding[];             // 评审发现
  decisions: ReviewDecision[];           // 评审决策
  actionItems: ActionItem[];             // 行动项

  // 评审结果
  overallScore: number;                  // 整体评分
  approved: boolean;                     // 是否通过
  nextReviewDate?: Date;                 // 下次评审日期
}

interface BatchOperation {
  id: string;
  type: 'style_sync' | 'color_adjustment' | 'material_update' | 'quality_standard_apply';
  targetProducts: string[];              // 目标产品列表
  operation: any;                        // 具体操作内容
  status: 'pending' | 'in_progress' | 'completed' | 'failed';
  createdBy: string;
  createdAt: Date;
  completedAt?: Date;
  results?: OperationResult[];           // 操作结果
}

2.2.4 售后归档阶段多产品适配

// 扩展售后数据结构
interface MultiProductAfterCare {
  productReviews: ProductReview[];        // 各产品评价
  crossProductAnalysis: CrossProductAnalysis; // 跨产品分析
  productComparison: ProductComparison[]; // 产品对比
  projectSummary: ProjectSummary;         // 项目总结
}

interface ProductReview {
  productId: string;                     // 产品ID
  productName: string;                   // 产品名称
  productType: ProductType;              // 产品类型

  // 客户评价
  customerRating: {
    overall: number;                     // 整体评分 1-5
    aspects: {
      design: number;                    // 设计评分
      functionality: number;             // 功能性评分
      aesthetics: number;                // 美观度评分
      practicality: number;              // 实用性评分
      value: number;                     // 性价比评分
    };
    feedback: string;                    // 具体反馈
    suggestions: string[];               // 改进建议
  };

  // 使用情况跟踪
  usageMetrics: {
    satisfaction: number;                // 满意度 0-100
    usageFrequency: string;              // 使用频率
    adaptations: string[];               // 后续改动
    issues: string[];                    // 问题记录
    maintenance: MaintenanceRecord[];    // 维护记录
  };

  // 经济价值分析
  economicValue: {
    costPerProduct: number;              // 单产品成本
    perceivedValue: number;              // 感知价值
    roi: number;                         // 投资回报率
    marketComparison: MarketComparison;  // 市场对比
  };

  // 专业评价
  professionalReview: {
    technicalQuality: number;            // 技术质量
    innovation: number;                  // 创新性
    sustainability: number;             // 可持续性
    scalability: number;                 // 可扩展性
  };
}

interface CrossProductAnalysis {
  styleConsistency: {
    score: number;                       // 风格一致性评分 0-100
    issues: string[];                    // 不一致问题
    improvements: string[];              // 改进建议
  };

  functionalFlow: {
    score: number;                       // 功能流线评分
    bottlenecks: string[];               // 瓶颈问题
    optimizations: string[];             // 优化建议
  };

  spaceUtilization: {
    efficiency: number;                  // 空间利用率
    recommendations: string[];           // 优化建议
  };

  designCohesion: {
    overallScore: number;                // 设计凝聚力评分
    thematicUnity: number;               // 主题统一性
    transitionQuality: number;           // 过渡质量
  };
}

interface ProjectSummary {
  overallSuccess: {
    score: number;                       // 项目成功度评分
    achievements: string[];              // 主要成就
    challenges: string[];                // 面临挑战
    lessons: string[];                   // 经验教训
  };

  businessMetrics: {
    totalRevenue: number;                // 总收入
    totalCost: number;                   // 总成本
    profitMargin: number;                // 利润率
    clientSatisfaction: number;          // 客户满意度
    referralPotential: number;           // 推荐潜力
  };

  teamPerformance: {
    collaborationScore: number;          // 协作评分
    efficiencyScore: number;             // 效率评分
    qualityScore: number;                // 质量评分
    innovationScore: number;             // 创新评分
  };

  futureOpportunities: {
    followUpProjects: string[];          // 后续项目机会
    serviceExtensions: string[];         // 服务扩展机会
    portfolioAdditions: string[];        // 作品集补充
  };
}

3. 多产品业务流程设计

3.1 产品识别与创建流程

3.1.1 产品识别时机

graph TD
    A[客服接收需求] --> B{是否多产品项目?}
    B -->|否| C[创建单产品项目]
    B -->|是| D[分析产品需求]
    D --> E[识别潜在产品]
    E --> F[创建产品列表]
    F --> G[设置产品优先级]
    G --> H[分配产品ID]
    H --> I[进入订单分配阶段]

    style C fill:#e8f5e9
    style I fill:#e3f2fd

3.1.2 产品识别规则

class ProductIdentifier {
  // 基于关键词识别产品
  identifyProductsFromDescription(description: string): ProductIdentificationResult {
    const result: ProductIdentificationResult = {
      identifiedProducts: [],
      confidence: 0,
      reasoning: '',
      suggestedQuestions: [],
      recommendedProductTypes: []
    };

    // 产品类型关键词映射
    const productKeywords = {
      [ProductType.LIVING_ROOM]: ['客厅', '起居室', '会客厅', '茶室', '待客区'],
      [ProductType.BEDROOM]: ['卧室', '主卧', '次卧', '儿童房', '老人房', '客房', '主人房'],
      [ProductType.KITCHEN]: ['厨房', '开放式厨房', '中西厨', '餐厨一体'],
      [ProductType.BATHROOM]: ['卫生间', '浴室', '洗手间', '盥洗室', '主卫', '次卫'],
      [ProductType.DINING_ROOM]: ['餐厅', '餐厅区', '用餐区', '就餐空间'],
      [ProductType.STUDY]: ['书房', '工作室', '办公室', '学习区', '阅读区'],
      [ProductType.BALCONY]: ['阳台', '露台', '花园阳台', '休闲阳台'],
      [ProductType.CORRIDOR]: ['走廊', '过道', '玄关', '门厅', '入户'],
      [ProductType.STORAGE]: ['储物间', '衣帽间', '杂物间', '收纳空间']
    };

    // 分析描述中的产品关键词
    const foundProducts: Array<{ type: ProductType; keywords: string[]; confidence: number }> = [];

    for (const [productType, keywords] of Object.entries(productKeywords)) {
      const matchedKeywords = keywords.filter(keyword =>
        description.toLowerCase().includes(keyword.toLowerCase())
      );

      if (matchedKeywords.length > 0) {
        foundProducts.push({
          type: productType as ProductType,
          keywords: matchedKeywords,
          confidence: matchedKeywords.length / keywords.length
        });
      }
    }

    // 按置信度排序
    foundProducts.sort((a, b) => b.confidence - a.confidence);

    // 构建识别结果
    result.identifiedProducts = foundProducts.map(fp => ({
      type: fp.type,
      productName: this.getDefaultProductName(fp.type),
      priority: this.calculateProductPriority(fp.type, fp.confidence),
      confidence: fp.confidence,
      identifiedKeywords: fp.keywords,
      estimatedArea: this.estimateProductArea(fp.type),
      suggestedComplexity: this.suggestProductComplexity(fp.type, description)
    }));

    // 计算整体置信度
    result.confidence = foundProducts.length > 0
      ? foundProducts.reduce((sum, fp) => sum + fp.confidence, 0) / foundProducts.length
      : 0;

    // 生成推理说明
    result.reasoning = this.generateIdentificationReasoning(foundProducts);

    // 生成建议问题
    result.suggestedQuestions = this.generateClarifyingQuestions(foundProducts);

    // 推荐相关产品类型
    result.recommendedProductTypes = this.recommendRelatedProductTypes(foundProducts);

    return result;
  }

  // 基于面积和预算推断产品数量
  estimateProductCount(totalArea: number, budget: number): ProductEstimationResult {
    const result: ProductEstimationResult = {
      estimatedProductCount: 1,
      confidence: 0.5,
      reasoning: '',
      possibleProductTypes: [],
      recommendedAllocation: {}
    };

    // 基于面积的产品数量估算
    const areaBasedCount = Math.max(1, Math.floor(totalArea / 20)); // 每20平米一个主要产品

    // 基于预算的产品数量估算
    const budgetBasedCount = Math.max(1, Math.floor(budget / 30000)); // 每3万一个产品

    // 综合判断
    const finalCount = Math.min(areaBasedCount, budgetBasedCount);
    result.estimatedProductCount = finalCount;

    // 推断可能的产品类型
    result.possibleProductTypes = this.inferPossibleProductTypes(totalArea, budget);

    // 推荐预算分配
    result.recommendedAllocation = this.calculateBudgetAllocation(
      result.possibleProductTypes,
      budget
    );

    // 生成推理
    result.reasoning = `基于面积${totalArea}平米和预算${budget}元,估算需要${finalCount}个主要产品设计产品`;

    return result;
  }
}

3.2 多产品报价策略

3.2.1 报价计算逻辑

class MultiProductPricingCalculator {
  calculateProductPricing(
    products: Project[],
    globalRequirements: GlobalRequirements,
    pricingRules: PricingRule[]
  ): MultiProductQuotation {

    const productQuotations: ProductQuotation[] = [];
    let totalAmount = 0;

    for (const product of products) {
      const productQuotation = this.calculateSingleProductPricing(product, globalRequirements, pricingRules);
      productQuotations.push(productQuotation);
      totalAmount += productQuotation.amount;
    }

    // 应用多产品折扣
    const discount = this.calculateMultiProductDiscount(products.length, totalAmount);
    const finalAmount = totalAmount - discount.value;

    return {
      projectId: this.getProjectId(),
      quotationDate: new Date(),
      currency: 'CNY',
      productQuotations,
      totalBaseAmount: totalAmount,
      discountAmount: discount.value,
      finalAmount,
      breakdown: this.calculateBreakdown(productQuotations),
      discount: discount.value > 0 ? discount : undefined
    };
  }

  private calculateSingleProductPricing(
    product: Project,
    globalRequirements: GlobalRequirements,
    pricingRules: PricingRule[]
  ): ProductQuotation {
    const basePrice = this.getBasePriceForProductType(product.type, product.area || 0);
    const complexityMultiplier = this.getComplexityMultiplier(product.complexity);
    const priorityAdjustment = this.getPriorityAdjustment(product.priority);
    const styleFactor = this.getStyleFactor(globalRequirements.overallStyle);

    const items: QuotationItem[] = [
      {
        id: `${product.id}_design`,
        category: 'design',
        description: '概念设计费',
        quantity: 1,
        unitPrice: basePrice * 0.25 * complexityMultiplier * styleFactor,
        totalPrice: basePrice * 0.25 * complexityMultiplier * styleFactor
      },
      {
        id: `${product.id}_modeling`,
        category: 'modeling',
        description: '三维建模费',
        quantity: 1,
        unitPrice: basePrice * 0.30 * complexityMultiplier,
        totalPrice: basePrice * 0.30 * complexityMultiplier
      },
      {
        id: `${product.id}_rendering`,
        category: 'rendering',
        description: '效果图渲染费',
        quantity: 1,
        unitPrice: basePrice * 0.25 * complexityMultiplier,
        totalPrice: basePrice * 0.25 * complexityMultiplier
      },
      {
        id: `${product.id}_soft_decor`,
        category: 'soft_decor',
        description: '软装设计费',
        quantity: 1,
        unitPrice: basePrice * 0.15 * complexityMultiplier,
        totalPrice: basePrice * 0.15 * complexityMultiplier
      },
      {
        id: `${product.id}_technical`,
        category: 'technical',
        description: '技术文档费',
        quantity: 1,
        unitPrice: basePrice * 0.05 * complexityMultiplier,
        totalPrice: basePrice * 0.05 * complexityMultiplier
      }
    ];

    const totalAmount = items.reduce((sum, item) => sum + item.totalPrice, 0);

    return {
      productId: product.id,
      productName: product.name,
      productType: product.type as ProductType,
      amount: totalAmount * priorityAdjustment,
      items,
      priority: product.priority,
      estimatedDays: this.calculateEstimatedDays(product, totalAmount),
      notes: `复杂度系数: ${complexityMultiplier}, 优先级调整: ${priorityAdjustment}, 风格系数: ${styleFactor}`
    };
  }

  private calculateMultiProductDiscount(productCount: number, totalAmount: number): { type: string; value: number; reason: string } {
    if (productCount >= 5) {
      return {
        type: 'percentage',
        value: totalAmount * 0.12, // 12% 折扣
        reason: '5产品以上项目享受12%折扣'
      };
    } else if (productCount >= 3) {
      return {
        type: 'percentage',
        value: totalAmount * 0.08, // 8% 折扣
        reason: '3-4产品项目享受8%折扣'
      };
    } else if (productCount >= 2) {
      return {
        type: 'percentage',
        value: totalAmount * 0.05, // 5% 折扣
        reason: '双产品项目享受5%折扣'
      };
    } else if (totalAmount > 200000) {
      return {
        type: 'fixed',
        value: 5000,
        reason: '高额度项目固定优惠5000元'
      };
    }

    return { type: 'percentage', value: 0, reason: '无折扣' };
  }
}

3.3 多产品需求采集流程

3.3.1 需求采集策略

class MultiProductRequirementCollector {
  async collectRequirements(
    products: Project[],
    globalRequirements: GlobalRequirements
  ): Promise<MultiProductRequirement> {

    const productRequirements: ProductRequirement[] = [];

    // 1. 并行采集各产品需求
    const requirementPromises = products.map(product =>
      this.collectProductRequirements(product, globalRequirements)
    );

    const collectedRequirements = await Promise.all(requirementPromises);
    productRequirements.push(...collectedRequirements);

    // 2. 分析跨产品需求
    const crossProductRequirements = await this.analyzeCrossProductRequirements(productRequirements);

    // 3. 验证需求一致性
    await this.validateRequirementConsistency(productRequirements, crossProductRequirements);

    return {
      products: productRequirements,
      globalRequirements,
      crossProductRequirements
    };
  }

  private async collectProductRequirements(
    product: Project,
    globalRequirements: GlobalRequirements
  ): Promise<ProductRequirement> {

    // 基于产品类型预填充需求模板
    const template = this.getProductRequirementTemplate(product.type);

    // 采集四大核心需求
    const colorRequirement = await this.collectColorRequirement(product, template.colorTemplate);
    const spaceStructureRequirement = await this.collectSpaceStructureRequirement(product, template.structureTemplate);
    const materialRequirement = await this.collectMaterialRequirement(product, template.materialTemplate);
    const lightingRequirement = await this.collectLightingRequirement(product, template.lightingTemplate);

    // 采集产品特定需求
    const specificRequirements = await this.collectSpecificRequirements(product, template.specificTemplate);

    // 管理参考资料
    const referenceImages = await this.collectReferenceImages(product);
    const referenceFiles = await this.collectReferenceFiles(product);

    return {
      productId: product.id,
      productName: product.name,
      productType: product.type as ProductType,
      colorRequirement,
      spaceStructureRequirement,
      materialRequirement,
      lightingRequirement,
      specificRequirements,
      referenceImages,
      referenceFiles,
      priority: product.priority,
      complexity: this.assessProductComplexity(product, specificRequirements)
    };
  }

  private async analyzeCrossProductRequirements(
    productRequirements: ProductRequirement[]
  ): Promise<CrossProductRequirement[]> {

    const crossProductRequirements: CrossProductRequirement[] = [];

    // 分析风格一致性需求
    const styleRequirement = this.analyzeStyleConsistency(productRequirements);
    if (styleRequirement) crossProductRequirements.push(styleRequirement);

    // 分析色彩流线需求
    const colorFlowRequirement = this.analyzeColorFlow(productRequirements);
    if (colorFlowRequirement) crossProductRequirements.push(colorFlowRequirement);

    // 分析材质匹配需求
    const materialMatchingRequirement = this.analyzeMaterialMatching(productRequirements);
    if (materialMatchingRequirement) crossProductRequirements.push(materialMatchingRequirement);

    // 分析功能连接需求
    const functionalConnectionRequirement = this.analyzeFunctionalConnections(productRequirements);
    if (functionalConnectionRequirement) crossProductRequirements.push(functionalConnectionRequirement);

    return crossProductRequirements;
  }
}

3.4 多产品交付协调机制

3.4.1 产品依赖管理

class ProductDependencyManager {
  analyzeProductDependencies(products: Project[]): ProductDependency[] {
    const dependencies: ProductDependency[] = [];

    // 分析风格参考依赖
    const styleDependencies = this.analyzeStyleDependencies(products);
    dependencies.push(...styleDependencies);

    // 分析色彩流线依赖
    const colorDependencies = this.analyzeColorDependencies(products);
    dependencies.push(...colorDependencies);

    // 分析尺寸参考依赖
    const sizeDependencies = this.analyzeSizeDependencies(products);
    dependencies.push(...sizeDependencies);

    // 分析功能连接依赖
    const functionalDependencies = this.analyzeFunctionalDependencies(products);
    dependencies.push(...functionalDependencies);

    return dependencies;
  }

  private analyzeStyleDependencies(products: Project[]): ProductDependency[] {
    const dependencies: ProductDependency[] = [];
    const livingRoom = products.find(p => p.type === ProductType.LIVING_ROOM);

    if (livingRoom) {
      // 客厅通常是风格参考基准
      const otherProducts = products.filter(p => p.id !== livingRoom.id);

      for (const product of otherProducts) {
        dependencies.push({
          fromProduct: livingRoom.id,
          toProduct: product.id,
          type: 'style_reference',
          description: `${product.name}需要与客厅风格保持一致`,
          status: 'pending',
          priority: this.calculateDependencyPriority(livingRoom, product)
        });
      }
    }

    return dependencies;
  }

  async resolveDependency(dependency: ProductDependency): Promise<boolean> {
    switch (dependency.type) {
      case 'style_reference':
        return await this.resolveStyleDependency(dependency);
      case 'color_flow':
        return await this.resolveColorDependency(dependency);
      case 'material_matching':
        return await this.resolveMaterialDependency(dependency);
      case 'size_reference':
        return await this.resolveSizeDependency(dependency);
      case 'functional_connection':
        return await this.resolveFunctionalDependency(dependency);
      default:
        return false;
    }
  }

  private async resolveStyleDependency(dependency: ProductDependency): Promise<boolean> {
    // 实现风格依赖解决逻辑
    // 1. 获取源产品的设计方案
    // 2. 提取关键风格元素
    // 3. 应用到目标产品
    // 4. 验证一致性

    console.log(`解决风格依赖: ${dependency.fromProduct} -> ${dependency.toProduct}`);
    dependency.status = 'satisfied';
    return true;
  }
}

3.4.2 批量操作支持

class ProductBatchOperationManager {
  async executeBatchOperation(operation: BatchOperation): Promise<boolean> {
    try {
      operation.status = 'in_progress';

      switch (operation.type) {
        case 'style_sync':
          return await this.executeStyleSync(operation);
        case 'color_adjustment':
          return await this.executeColorAdjustment(operation);
        case 'material_update':
          return await this.executeMaterialUpdate(operation);
        case 'quality_standard_apply':
          return await this.executeQualityStandardApply(operation);
        default:
          throw new Error(`未知的批量操作类型: ${operation.type}`);
      }
    } catch (error) {
      console.error(`批量操作失败:`, error);
      operation.status = 'failed';
      return false;
    }
  }

  private async executeStyleSync(operation: BatchOperation): Promise<boolean> {
    const { targetProducts, operation: syncData } = operation;

    // 获取风格同步数据
    const sourceStyle = syncData.sourceStyle;
    const styleElements = syncData.elements;

    const results: OperationResult[] = [];

    // 批量应用到目标产品
    for (const productId of targetProducts) {
      try {
        await this.applyStyleToProduct(productId, sourceStyle, styleElements);
        results.push({
          productId,
          success: true,
          message: '风格同步成功'
        });
      } catch (error) {
        results.push({
          productId,
          success: false,
          message: `风格同步失败: ${error.message}`
        });
      }
    }

    operation.results = results;
    operation.status = 'completed';
    operation.completedAt = new Date();

    return results.every(r => r.success);
  }

  private async executeQualityStandardApply(operation: BatchOperation): Promise<boolean> {
    const { targetProducts, operation: standardData } = operation;

    // 获取质量标准数据
    const qualityStandard = standardData.qualityStandard;
    const applicableProductTypes = standardData.applicableProductTypes;

    const results: OperationResult[] = [];

    // 批量应用质量标准
    for (const productId of targetProducts) {
      try {
        // 验证产品类型是否适用
        const product = await this.getProductById(productId);
        if (applicableProductTypes.includes(product.type)) {
          await this.applyQualityStandard(productId, qualityStandard);
          results.push({
            productId,
            success: true,
            message: '质量标准应用成功'
          });
        } else {
          results.push({
            productId,
            success: false,
            message: '产品类型不适用该质量标准'
          });
        }
      } catch (error) {
        results.push({
          productId,
          success: false,
          message: `质量标准应用失败: ${error.message}`
        });
      }
    }

    operation.results = results;
    operation.status = 'completed';
    operation.completedAt = new Date();

    return results.every(r => r.success);
  }
}

4. 界面交互设计

4.1 产品概览界面

4.1.1 产品卡片布局

<!-- 产品概览界面 -->
<div class="product-overview-container">
  <!-- 全局信息栏 -->
  <div class="global-info-bar">
    <div class="project-info">
      <h3>{{ projectTitle }}</h3>
      <span class="product-count">{{ products.length }}个产品</span>
      <span class="total-budget">总预算: ¥{{ totalBudget.toLocaleString() }}</span>
      <span class="project-type">{{ isMultiProductProject ? '多产品项目' : '单产品项目' }}</span>
    </div>

    <div class="overall-progress">
      <div class="progress-circle">
        <svg width="120" height="120">
          <circle cx="60" cy="60" r="50" fill="none" stroke="#e0e0e0" stroke-width="8"/>
          <circle cx="60" cy="60" r="50" fill="none" stroke="#4CAF50" stroke-width="8"
                  [attr.stroke-dasharray]="circumference"
                  [attr.stroke-dashoffset]="progressOffset"/>
        </svg>
        <div class="progress-text">
          <span class="percentage">{{ overallProgress }}%</span>
          <span class="label">总体进度</span>
        </div>
      </div>
    </div>
  </div>

  <!-- 产品卡片网格 -->
  <div class="products-grid">
    @for (product of products; track product.id) {
      <div class="product-card"
           [class.priority-high]="product.priority >= 8"
           [class.priority-medium]="product.priority >= 5 && product.priority < 8"
           [class.status-completed]="product.status === 'completed'"
           [class.status-in-progress]="product.status === 'in_progress'">

        <!-- 产品头部 -->
        <div class="product-header">
          <div class="product-icon">
            <i class="icon-{{ getProductIcon(product.type) }}"></i>
          </div>
          <div class="product-info">
            <h4>{{ product.name }}</h4>
            <span class="product-type">{{ getProductTypeName(product.type) }}</span>
            @if (product.area) {
              <span class="product-area">{{ product.area }}m²</span>
            }
          </div>
          <div class="product-actions">
            <button class="btn-icon" (click)="editProduct(product.id)" title="编辑">
              <i class="icon-edit"></i>
            </button>
            <button class="btn-icon" (click)="viewProductDetails(product.id)" title="查看详情">
              <i class="icon-view"></i>
            </button>
          </div>
        </div>

        <!-- 产品进度 -->
        <div class="product-progress">
          <div class="progress-bar">
            <div class="progress-fill"
                 [style.width.%]="getProductProgress(product.id)"
                 [class.color-warning]="getProductProgress(product.id) < 50"
                 [class.color-success]="getProductProgress(product.id) >= 80">
            </div>
          </div>
          <span class="progress-text">{{ getProductProgress(product.id) }}%</span>
        </div>

        <!-- 当前阶段 -->
        <div class="current-stage">
          <span class="stage-label">当前阶段:</span>
          <span class="stage-value">{{ getCurrentStage(product.id) }}</span>
        </div>

        <!-- 负责人 -->
        <div class="assignee-info">
          @if (getProductAssignee(product.id)) {
            <div class="assignee-avatar">
              <img [src]="getProductAssignee(product.id).avatar" [alt]="getProductAssignee(product.id).name">
            </div>
            <span class="assignee-name">{{ getProductAssignee(product.id).name }}</span>
          } @else {
            <span class="no-assignee">未分配</span>
          }
        </div>

        <!-- 产品状态标签 -->
        <div class="product-tags">
          @if (product.priority >= 8) {
            <span class="tag tag-high">高优先级</span>
          }
          @if (product.complexity === 'complex') {
            <span class="tag tag-complex">复杂</span>
          }
          @if (hasCrossProductDependencies(product.id)) {
            <span class="tag tag-dependency">依赖</span>
          }
          @if (product.estimatedBudget) {
            <span class="tag tag-budget">¥{{ product.estimatedBudget.toLocaleString() }}</span>
          }
        </div>

        <!-- 快速操作 -->
        <div class="quick-actions">
          <button class="btn-small"
                  [disabled]="!canAdvanceStage(product.id)"
                  (click)="advanceProductStage(product.id)">
            推进阶段
          </button>
          <button class="btn-small btn-secondary"
                  (click)="viewProductFiles(product.id)">
            查看文件
          </button>
          <button class="btn-small btn-secondary"
                  (click)="viewProductRequirements(product.id)">
            查看需求
          </button>
        </div>
      </div>
    }

    <!-- 添加新产品卡片 -->
    <div class="product-card add-product-card" (click)="showAddProductDialog = true">
      <div class="add-product-content">
        <i class="icon-plus"></i>
        <span>添加产品</span>
      </div>
    </div>
  </div>

  <!-- 产品协调面板 -->
  @if (isMultiProductProject) {
    <div class="product-coordination-panel">
      <h3>产品协调管理</h3>

      <!-- 依赖关系管理 -->
      <div class="dependencies-section">
        <h4>依赖关系</h4>
        @for (dependency of productDependencies; track dependency.id) {
          <div class="dependency-item"
               [class.status-pending]="dependency.status === 'pending'"
               [class.status-satisfied]="dependency.status === 'satisfied'"
               [class.status-blocked]="dependency.status === 'blocked'">
            <div class="dependency-info">
              <span class="dependency-from">{{ getProductName(dependency.fromProduct) }}</span>
              <i class="icon-arrow-right"></i>
              <span class="dependency-to">{{ getProductName(dependency.toProduct) }}</span>
              <span class="dependency-type">{{ getDependencyTypeName(dependency.type) }}</span>
            </div>
            <div class="dependency-actions">
              @if (dependency.status === 'pending') {
                <button class="btn-small" (click)="resolveDependency(dependency)">
                  解决依赖
                </button>
              }
            </div>
          </div>
        }
      </div>

      <!-- 批量操作 -->
      <div class="batch-operations-section">
        <h4>批量操作</h4>
        <div class="batch-actions">
          <button class="btn-secondary" (click)="showBatchStyleSync = true">
            <i class="icon-style"></i> 批量风格同步
          </button>
          <button class="btn-secondary" (click)="showBatchColorAdjustment = true">
            <i class="icon-color"></i> 批量色彩调整
          </button>
          <button class="btn-secondary" (click)="showBatchQualityApply = true">
            <i class="icon-quality"></i> 批量质量标准
          </button>
        </div>
      </div>
    </div>
  }
</div>

4.1.2 产品详情弹窗

<!-- 产品详情弹窗 -->
<div class="product-detail-modal" *ngIf="selectedProductId">
  <div class="modal-overlay" (click)="closeProductDetails()"></div>
  <div class="modal-content large">
    <div class="modal-header">
      <h3>{{ getProductName(selectedProductId) }} - 详细信息</h3>
      <div class="header-actions">
        <button class="btn-secondary" (click)="editProduct(selectedProductId)">
          <i class="icon-edit"></i> 编辑产品
        </button>
        <button class="btn-secondary" (click)="exportProductReport(selectedProductId)">
          <i class="icon-export"></i> 导出报告
        </button>
        <button class="btn-icon" (click)="closeProductDetails()">
          <i class="icon-close"></i>
        </button>
      </div>
    </div>

    <div class="modal-body">
      <!-- 标签页导航 -->
      <div class="tab-navigation">
        <button class="tab-btn"
                [class.active]="activeTab === 'overview'"
                (click)="activeTab = 'overview'">
          概览
        </button>
        <button class="tab-btn"
                [class.active]="activeTab === 'requirements'"
                (click)="activeTab = 'requirements'">
          需求
        </button>
        <button class="tab-btn"
                [class.active]="activeTab === 'delivery'"
                (click)="activeTab = 'delivery'">
          交付
        </button>
        <button class="tab-btn"
                [class.active]="activeTab === 'timeline'"
                (click)="activeTab = 'timeline'">
          时间线
        </button>
        <button class="tab-btn"
                [class.active]="activeTab === 'dependencies'"
                (click)="activeTab = 'dependencies'">
          依赖关系
        </button>
        <button class="tab-btn"
                [class.active]="activeTab === 'reviews'"
                (click)="activeTab = 'reviews'">
          评价
        </button>
      </div>

      <!-- 标签页内容 -->
      <div class="tab-content">
        <!-- 概览标签页 -->
        <div *ngIf="activeTab === 'overview'" class="overview-tab">
          <div class="product-metadata">
            <h4>产品信息</h4>
            <div class="metadata-grid">
              <div class="metadata-item">
                <label>产品类型:</label>
                <span>{{ getProductTypeName(getProduct(selectedProductId).type) }}</span>
              </div>
              <div class="metadata-item">
                <label>面积:</label>
                <span>{{ getProduct(selectedProductId).area }}m²</span>
              </div>
              <div class="metadata-item">
                <label>优先级:</label>
                <span class="priority-badge priority-{{ getProduct(selectedProductId).priority }}">
                  {{ getProduct(selectedProductId).priority }}
                </span>
              </div>
              <div class="metadata-item">
                <label>复杂度:</label>
                <span>{{ getProduct(selectedProductId).complexity }}</span>
              </div>
              <div class="metadata-item">
                <label>预估预算:</label>
                <span>¥{{ (getProduct(selectedProductId).estimatedBudget || 0).toLocaleString() }}</span>
              </div>
              <div class="metadata-item">
                <label>预估工期:</label>
                <span>{{ getProduct(selectedProductId).estimatedDuration || 0 }}天</span>
              </div>
            </div>
          </div>

          <div class="product-progress-detail">
            <h4>进度详情</h4>
            <div class="progress-stages">
              @for (stage of getAllStages(); track stage) {
                <div class="stage-progress-item"
                     [class.completed]="isStageCompleted(selectedProductId, stage)"
                     [class.current]="isCurrentStage(selectedProductId, stage)">
                  <div class="stage-icon">
                    <i class="icon-{{ getStageIcon(stage) }}"></i>
                  </div>
                  <div class="stage-info">
                    <span class="stage-name">{{ stage }}</span>
                    <span class="stage-time">{{ getStageTime(selectedProductId, stage) }}</span>
                  </div>
                  <div class="stage-progress">
                    <div class="progress-bar small">
                      <div class="progress-fill"
                           [style.width.%]="getStageProgress(selectedProductId, stage)">
                      </div>
                    </div>
                  </div>
                </div>
              }
            </div>
          </div>
        </div>

        <!-- 需求标签页 -->
        <div *ngIf="activeTab === 'requirements'" class="requirements-tab">
          <app-product-requirements-view
            [productId]="selectedProductId"
            [readonly]="isReadOnly()">
          </app-product-requirements-view>
        </div>

        <!-- 交付标签页 -->
        <div *ngIf="activeTab === 'delivery'" class="delivery-tab">
          <app-product-delivery-view
            [productId]="selectedProductId"
            [readonly]="isReadOnly()">
          </app-product-delivery-view>
        </div>

        <!-- 时间线标签页 -->
        <div *ngIf="activeTab === 'timeline'" class="timeline-tab">
          <app-product-timeline-view
            [productId]="selectedProductId">
          </app-product-timeline-view>
        </div>

        <!-- 依赖关系标签页 -->
        <div *ngIf="activeTab === 'dependencies'" class="dependencies-tab">
          <app-product-dependencies-view
            [productId]="selectedProductId">
          </app-product-dependencies-view>
        </div>

        <!-- 评价标签页 -->
        <div *ngIf="activeTab === 'reviews'" class="reviews-tab">
          <app-product-reviews-view
            [productId]="selectedProductId"
            [readonly]="isReadOnly()">
          </app-product-reviews-view>
        </div>
      </div>
    </div>
  </div>
</div>

4.2 多产品文件管理界面

4.2.1 产品文件浏览器

<!-- 多产品文件浏览器 -->
<div class="multi-product-file-browser">
  <!-- 产品选择器 -->
  <div class="product-selector">
    <div class="product-tabs">
      @for (product of products; track product.id) {
        <button class="product-tab"
                [class.active]="selectedProductId === product.id"
                [class.has-files]="getProductFileCount(product.id) > 0"
                (click)="selectProduct(product.id)">
          <div class="tab-content">
            <i class="icon-{{ getProductIcon(product.type) }}"></i>
            <span class="product-name">{{ product.name }}</span>
            <span class="file-count" *ngIf="getProductFileCount(product.id) > 0">
              {{ getProductFileCount(product.id) }}
            </span>
          </div>
        </button>
      }
    </div>

    <!-- 全选/批量操作 -->
    <div class="batch-actions">
      <label class="checkbox-label">
        <input type="checkbox"
               [(ngModel)]="selectAllProducts"
               (change)="toggleSelectAllProducts()">
        <span>全选产品</span>
      </label>

      @if (selectedProducts.length > 0) {
        <div class="selected-actions">
          <span class="selected-count">已选择 {{ selectedProducts.length }} 个产品</span>
          <button class="btn-small" (click)="batchUploadFiles()">
            批量上传
          </button>
          <button class="btn-small btn-secondary" (click)="batchDownloadFiles()">
            批量下载
          </button>
          <button class="btn-small btn-secondary" (click)="batchSyncFiles()">
            批量同步
          </button>
        </div>
      }
    </div>
  </div>

  <!-- 文件列表 -->
  <div class="file-content-area">
    @if (selectedProductId) {
      <div class="product-file-view">
        <!-- 当前产品信息 -->
        <div class="current-product-header">
          <div class="product-info">
            <i class="icon-{{ getProductIcon(getProduct(selectedProductId).type) }}"></i>
            <h4>{{ getProduct(selectedProductId).name }}</h4>
            <span class="file-total">{{ getProductFileCount(selectedProductId) }} 个文件</span>
            <span class="product-type">{{ getProductTypeName(getProduct(selectedProductId).type) }}</span>
          </div>

          <div class="view-options">
            <div class="file-type-filter">
              <select [(ngModel)]="fileTypeFilter" class="filter-select">
                <option value="all">所有文件</option>
                <option value="concept">概念设计</option>
                <option value="model">三维模型</option>
                <option value="rendering">渲染图</option>
                <option value="document">技术文档</option>
                <option value="reference">参考资料</option>
              </select>
            </div>

            <div class="view-toggle">
              <button class="btn-icon"
                      [class.active]="viewMode === 'grid'"
                      (click)="viewMode = 'grid'"
                      title="网格视图">
                <i class="icon-grid"></i>
              </button>
              <button class="btn-icon"
                      [class.active]="viewMode === 'list'"
                      (click)="viewMode = 'list'"
                      title="列表视图">
                <i class="icon-list"></i>
              </button>
            </div>

            <button class="btn-primary" (click)="triggerFileUpload(selectedProductId)">
              <i class="icon-upload"></i> 上传文件
            </button>
          </div>
        </div>

        <!-- 文件上传区域 -->
        <div class="upload-zone"
             [class.drag-over]="isDragOver"
             (dragover)="isDragOver = true"
             (dragleave)="isDragOver = false"
             (drop)="handleFileDrop($event, selectedProductId)">
          <div class="upload-prompt">
            <i class="icon-upload"></i>
            <p>拖拽文件到此处上传</p>
            <p class="hint">或点击上传按钮选择文件</p>
            <div class="supported-formats">
              <span>支持格式:</span>
              <span>JPG, PNG, PDF, DWG, SKP, 3DM, PSD</span>
            </div>
          </div>
        </div>

        <!-- 文件分类标签 -->
        <div class="file-categories">
          <button class="category-tab"
                  [class.active]="selectedCategory === 'all'"
                  (click)="selectedCategory = 'all'">
            全部 ({{ getProductFileCount(selectedProductId) }})
          </button>
          <button class="category-tab"
                  [class.active]="selectedCategory === 'concept'"
                  (click)="selectedCategory = 'concept'">
            概念设计 ({{ getFileCountByCategory(selectedProductId, 'concept') }})
          </button>
          <button class="category-tab"
                  [class.active]="selectedCategory === 'model'"
                  (click)="selectedCategory = 'model'">
            三维模型 ({{ getFileCountByCategory(selectedProductId, 'model') }})
          </button>
          <button class="category-tab"
                  [class.active]="selectedCategory === 'rendering'"
                  (click)="selectedCategory = 'rendering'">
            渲染图 ({{ getFileCountByCategory(selectedProductId, 'rendering') }})
          </button>
          <button class="category-tab"
                  [class.active]="selectedCategory === 'document'"
                  (click)="selectedCategory = 'document'">
            技术文档 ({{ getFileCountByCategory(selectedProductId, 'document') }})
          </button>
        </div>

        <!-- 文件网格视图 -->
        @if (viewMode === 'grid') {
          <div class="files-grid">
            @for (file of getFilteredProductFiles(selectedProductId); track file.id) {
              <div class="file-card"
                   [class.selected]="selectedFiles.has(file.id)"
                   [class.category-{{ file.category }}]="true"
                   (click)="toggleFileSelection(file.id)">
                <div class="file-preview">
                  @if (isImageFile(file)) {
                    <img [src]="file.url" [alt]="file.name">
                  } @else {
                    <div class="file-icon-placeholder">
                      <i class="icon-{{ getFileIcon(file.type) }}"></i>
                    </div>
                  }
                  @if (file.category) {
                    <div class="file-category-badge">
                      {{ getCategoryName(file.category) }}
                    </div>
                  }
                </div>

                <div class="file-info">
                  <span class="file-name" [title]="file.name">{{ file.name }}</span>
                  <span class="file-size">{{ formatFileSize(file.size) }}</span>
                  <span class="file-date">{{ formatDate(file.uploadTime) }}</span>
                  @if (file.version) {
                    <span class="file-version">v{{ file.version }}</span>
                  }
                </div>

                <div class="file-actions">
                  <button class="btn-icon" (click)="previewFile(file)" title="预览">
                    <i class="icon-eye"></i>
                  </button>
                  <button class="btn-icon" (click)="downloadFile(file)" title="下载">
                    <i class="icon-download"></i>
                  </button>
                  <button class="btn-icon" (click)="shareFile(file)" title="分享">
                    <i class="icon-share"></i>
                  </button>
                  <button class="btn-icon" (click)="deleteFile(file)" title="删除">
                    <i class="icon-delete"></i>
                  </button>
                </div>
              </div>
            }
          </div>
        }

        <!-- 文件列表视图 -->
        @if (viewMode === 'list') {
          <div class="files-list">
            <div class="list-header">
              <div class="header-cell">
                <input type="checkbox"
                       [(ngModel)]="selectAllFiles"
                       (change)="toggleSelectAllFiles()">
              </div>
              <div class="header-cell">文件名</div>
              <div class="header-cell">类别</div>
              <div class="header-cell">大小</div>
              <div class="header-cell">类型</div>
              <div class="header-cell">上传时间</div>
              <div class="header-cell">版本</div>
              <div class="header-cell">操作</div>
            </div>

            @for (file of getFilteredProductFiles(selectedProductId); track file.id) {
              <div class="list-row"
                   [class.selected]="selectedFiles.has(file.id)"
                   [class.category-{{ file.category }}]="true">
                <div class="list-cell">
                  <input type="checkbox"
                         [(ngModel)]="selectedFiles.has(file.id)"
                         (change)="toggleFileSelection(file.id)">
                </div>
                <div class="list-cell file-name-cell">
                  <i class="icon-{{ getFileIcon(file.type) }}"></i>
                  <span>{{ file.name }}</span>
                </div>
                <div class="list-cell">
                  @if (file.category) {
                    <span class="category-badge category-{{ file.category }}">
                      {{ getCategoryName(file.category) }}
                    </span>
                  }
                </div>
                <div class="list-cell">{{ formatFileSize(file.size) }}</div>
                <div class="list-cell">{{ getFileTypeLabel(file.type) }}</div>
                <div class="list-cell">{{ formatDate(file.uploadTime) }}</div>
                <div class="list-cell">
                  @if (file.version) {
                    <span class="version-badge">v{{ file.version }}</span>
                  }
                </div>
                <div class="list-cell actions-cell">
                  <button class="btn-icon small" (click)="previewFile(file)" title="预览">
                    <i class="icon-eye"></i>
                  </button>
                  <button class="btn-icon small" (click)="downloadFile(file)" title="下载">
                    <i class="icon-download"></i>
                  </button>
                  <button class="btn-icon small" (click)="shareFile(file)" title="分享">
                    <i class="icon-share"></i>
                  </button>
                  <button class="btn-icon small" (click)="deleteFile(file)" title="删除">
                    <i class="icon-delete"></i>
                  </button>
                </div>
              </div>
            }
          </div>
        }
      </div>
    } @else {
      <div class="no-product-selected">
        <i class="icon-folder"></i>
        <p>请选择一个产品查看文件</p>
      </div>
    }
  </div>
</div>

5. 数据库迁移方案

5.1 数据结构变更

5.1.1 Project 表迁移

-- 为主项目Project表添加多产品支持字段
ALTER TABLE Project ADD COLUMN projectType VARCHAR(20) DEFAULT 'single';
ALTER TABLE Project ADD COLUMN productIds JSON;
ALTER TABLE Project ADD COLUMN productProgress JSON;
ALTER TABLE Project ADD COLUMN productAssignment JSON;
ALTER TABLE Project ADD COLUMN productDependencies JSON;
ALTER TABLE Project ADD COLUMN coordinationInfo JSON;

-- 创建产品索引
CREATE INDEX idx_project_projectType ON Project(projectType);
CREATE INDEX idx_project_productIds ON Project USING GIN(productIds);

5.1.2 新增产品相关表

-- 创建产品设计表(基于现有Product表)
-- Product表已存在,添加新的字段支持
ALTER TABLE Product ADD COLUMN area DECIMAL(8,2);
ALTER TABLE Product ADD COLUMN priority INTEGER DEFAULT 5;
ALTER TABLE Product ADD COLUMN complexity VARCHAR(20) DEFAULT 'medium';
ALTER TABLE Product ADD COLUMN orderNumber INTEGER DEFAULT 0;
ALTER TABLE Product ADD COLUMN estimatedBudget DECIMAL(12,2);
ALTER TABLE Product ADD COLUMN estimatedDuration INTEGER;

-- 创建产品进度表
CREATE TABLE ProductProgress (
  id VARCHAR(50) PRIMARY KEY,
  productId VARCHAR(50) NOT NULL,
  stage VARCHAR(50) NOT NULL,
  progress INTEGER DEFAULT 0,
  status VARCHAR(20) DEFAULT 'not_started',
  timeline JSON,
  blockers JSON,
  estimatedCompletion DATETIME,
  actualCompletion DATETIME,
  createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
  updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (productId) REFERENCES Product(objectId)
);

-- 创建产品分配表
CREATE TABLE ProductAssignment (
  id VARCHAR(50) PRIMARY KEY,
  productId VARCHAR(50) NOT NULL,
  stage VARCHAR(50) NOT NULL,
  assigneeId VARCHAR(50) NOT NULL,
  assigneeName VARCHAR(100) NOT NULL,
  role VARCHAR(50) NOT NULL,
  assignedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
  assignedBy VARCHAR(50),
  status VARCHAR(20) DEFAULT 'active',
  workload DECIMAL(3,2) DEFAULT 0.0,
  notes TEXT,
  FOREIGN KEY (productId) REFERENCES Product(objectId)
);

-- 创建产品依赖表
CREATE TABLE ProductDependency (
  id VARCHAR(50) PRIMARY KEY,
  fromProductId VARCHAR(50) NOT NULL,
  toProductId VARCHAR(50) NOT NULL,
  type VARCHAR(50) NOT NULL,
  description TEXT,
  status VARCHAR(20) DEFAULT 'pending',
  priority INTEGER DEFAULT 5,
  createdAt DATETIME DEFAULT CURRENT_TIMESTAMP,
  updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (fromProductId) REFERENCES Product(objectId),
  FOREIGN KEY (toProductId) REFERENCES Product(objectId)
);

5.2 数据迁移脚本

5.2.1 现有项目数据迁移

class ProductMigrationService {
  async migrateExistingProjects(): Promise<void> {
    console.log('开始迁移现有项目数据到产品管理模式...');

    // 1. 获取所有现有项目
    const projects = await this.getAllProjects();

    for (const project of projects) {
      await this.migrateProjectToProducts(project);
    }

    console.log('项目数据迁移完成');
  }

  private async migrateProjectToProducts(project: any): Promise<void> {
    // 2. 分析项目是否为多产品
    const isMultiProduct = await this.analyzeProjectProductType(project);

    if (isMultiProduct) {
      // 3. 创建产品记录
      const products = await this.createProductsForProject(project);

      // 4. 更新项目记录
      await this.updateProjectWithProducts(project.objectId, products);

      // 5. 迁移交付数据到产品维度
      await this.migrateDeliveryDataToProducts(project, products);

      // 6. 迁移需求数据到产品维度
      await this.migrateRequirementDataToProducts(project, products);
    } else {
      // 单产品项目,创建默认产品
      const defaultProduct = await this.createDefaultProduct(project);
      await this.updateProjectWithProducts(project.objectId, [defaultProduct]);
    }
  }

  private async analyzeProjectProductType(project: any): Promise<boolean> {
    // 基于项目标题、描述、文件等信息判断是否为多产品
    const indicators = [
      project.title?.includes('全屋') || project.title?.includes('整套'),
      project.data?.description?.includes('多空间'),
      (project.data?.quotation?.items?.length || 0) > 3,
      await this.hasMultipleRoomTypes(project)
    ];

    return indicators.some(indicator => indicator === true);
  }

  private async createProductsForProject(project: any): Promise<Project[]> {
    const products: Project[] = [];

    // 基于报价项创建产品
    if (project.data?.quotation?.items) {
      for (const item of project.data.quotation.items) {
        const productType = this.inferProductTypeFromDescription(item.description);
        if (productType) {
          const product: Project = {
            id: `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
            name: item.room || this.getDefaultProductName(productType),
            type: productType,
            status: 'not_started',
            priority: this.calculateProductPriority(item.amount),
            complexity: 'medium',
            area: this.estimateProductArea(productType, item.amount),
            estimatedBudget: item.amount,
            estimatedDuration: this.estimateProductDuration(productType, item.amount),
            order: products.length + 1,
            projectId: project.objectId,
            createdAt: new Date(),
            updatedAt: new Date()
          };

          products.push(product);
        }
      }
    }

    // 如果没有从报价识别出产品,创建默认产品
    if (products.length === 0) {
      const defaultProduct = await this.createDefaultProduct(project);
      products.push(defaultProduct);
    }

    return products;
  }

  private async createDefaultProduct(project: any): Promise<Project> {
    return {
      id: `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      name: project.title || '主空间设计',
      type: 'living_room',
      status: 'not_started',
      priority: 5,
      complexity: 'medium',
      estimatedBudget: project.data?.quotation?.totalAmount || 0,
      estimatedDuration: 30,
      order: 1,
      projectId: project.objectId,
      createdAt: new Date(),
      updatedAt: new Date()
    };
  }

  private async migrateDeliveryDataToProducts(project: any, products: Project[]): Promise<void> {
    // 迁移交付执行数据到对应产品
    if (project.data?.deliveryProcesses) {
      for (const process of project.data.deliveryProcesses) {
        for (const product of products) {
          // 将交付数据关联到对应产品
          await this.createProductDeliveryData(product.id, process);
        }
      }
    }
  }

  private async migrateRequirementDataToProducts(project: any, products: Project[]): Promise<void> {
    // 迁移需求数据到对应产品
    if (project.data?.requirements) {
      for (const product of products) {
        await this.createProductRequirementData(product.id, project.data.requirements);
      }
    }
  }
}

5.3 向后兼容性保证

5.3.1 数据访问层适配

class ProjectDataAdapter {
  // 适配旧的单产品项目数据格式
  adaptLegacyProject(legacyProject: any): any {
    const adaptedProject: any = {
      ...legacyProject,
      projectType: 'single',
      products: this.createDefaultProductsFromLegacy(legacyProject),
      productProgress: this.createDefaultProgressFromLegacy(legacyProject),
      productAssignment: this.createDefaultAssignmentFromLegacy(legacyProject)
    };

    return adaptedProject;
  }

  private createDefaultProductsFromLegacy(legacyProject: any): Project[] {
    return [{
      id: `default_product_${legacyProject.objectId}`,
      name: legacyProject.title || '主空间设计',
      type: this.inferProductTypeFromLegacy(legacyProject),
      status: this.legacyStatusToProductStatus(legacyProject.status),
      priority: 5,
      complexity: 'medium',
      estimatedBudget: legacyProject.data?.quotation?.totalAmount || 0,
      estimatedDuration: 30,
      order: 1,
      projectId: legacyProject.objectId,
      createdAt: legacyProject.createdAt,
      updatedAt: legacyProject.updatedAt,
      requirements: legacyProject.data?.requirements,
      quotation: legacyProject.data?.quotation,
      reviews: legacyProject.data?.reviews
    }];
  }

  // 适配新的多产品数据格式到旧格式(用于兼容性接口)
  adaptToLegacyFormat(multiProductProject: any): any {
    if (multiProductProject.projectType === 'single') {
      return {
        ...multiProductProject,
        // 将单产品数据扁平化到原有格式
        data: {
          ...multiProductProject.data,
          deliveryProcesses: this.extractDeliveryProcessesFromProducts(multiProductProject),
          requirements: this.extractRequirementsFromProducts(multiProductProject),
          quotation: this.extractQuotationFromProducts(multiProductProject)
        }
      };
    }

    // 多产品项目返回增强格式的数据
    return multiProductProject;
  }
}

6. 实施建议

6.1 分阶段实施计划

6.1.1 第一阶段:基础架构(2周)

  • 数据库结构设计和迁移
  • 核心Product表模型重构
  • 产品管理基础服务(ProductSpaceService)
  • 向后兼容性保证
  • 数据迁移脚本开发

6.1.2 第二阶段:界面适配(3周)

  • 产品概览界面开发
  • 产品详情弹窗实现
  • 多产品文件管理界面
  • 产品协调管理界面
  • 响应式布局适配

6.1.3 第三阶段:业务逻辑(3周)

  • 多产品报价计算系统
  • 产品依赖关系管理
  • 批量操作支持
  • 跨产品协调机制
  • 产品进度跟踪系统

6.1.4 第四阶段:测试优化(2周)

  • 单元测试和集成测试
  • 性能优化
  • 用户体验优化
  • 文档完善
  • 数据迁移验证

6.2 风险控制

6.2.1 数据一致性风险

  • 风险:数据迁移过程中可能出现数据丢失或不一致
  • 应对:完整的数据备份、分步迁移、数据校验机制、回滚方案

6.2.2 性能风险

  • 风险:多产品查询可能影响系统性能
  • 应对:合理的数据库索引、查询优化、缓存策略、分页加载

6.2.3 用户体验风险

  • 风险:界面复杂度增加可能影响用户体验
  • 应对:渐进式界面更新、用户培训、智能默认配置、简化操作流程

6.2.4 业务连续性风险

  • 风险:系统升级可能影响现有业务
  • 应对:平滑迁移、双系统并行、灰度发布、快速回滚机制

6.3 成功指标

6.3.1 功能指标

  • 支持单产品到10+产品的项目管理
  • 产品间依赖关系自动识别率 > 80%
  • 批量操作成功率 > 95%
  • 数据迁移零丢失
  • 向后兼容性100%保证

6.3.2 性能指标

  • 产品切换响应时间 < 200ms
  • 多产品项目加载时间 < 1s
  • 并发用户支持数 > 100
  • 文件上传成功率 > 99%

6.3.3 用户体验指标

  • 用户满意度 > 4.5/5
  • 操作流程简化度提升 > 30%
  • 错误率降低 > 50%
  • 学习成本降低 > 40%

6.3.4 业务指标

  • 项目交付效率提升 > 25%
  • 设计质量评分提升 > 20%
  • 客户满意度提升 > 15%
  • 跨产品协作成功率 > 90%

文档版本:v4.0 (基于Project表统一产品设计管理) 更新日期:2025-10-20 维护者:YSS Development Team