# 项目管理 - 售后归档阶段 PRD (Product表版本) ## 1. 功能概述 ### 1.1 阶段定位 售后归档阶段是项目管理流程的收尾环节,包含尾款结算、全景图合成、客户评价、投诉处理、项目复盘五大核心模块。该阶段负责完成项目交付、收集反馈、总结经验,为后续项目优化提供数据支撑。 ### 1.2 核心目标 - **多产品设计售后管理**:基于Product表实现单产品到多产品项目的差异化管理 - **实现自动化尾款结算流程** - **按产品生成全景图分享链接** - **收集客户多维度评价(按产品维度)** - **处理客户投诉反馈(产品定位)** - **生成多产品项目复盘报告** ### 1.3 涉及角色 - **客服人员**:跟进尾款支付、发送评价链接、处理投诉 - **技术人员**:验收交付物、启动自动结算、合成全景图 - **组长**:审核复盘报告、处理投诉、优化流程 - **财务人员**:确认款项到账、核对支付凭证 ### 1.4 多产品设计售后特性 #### 1.4.1 产品差异化售后策略 - **单产品设计项目**:标准售后流程,统一结算和评价 - **双产品设计项目**:按产品独立生成全景图,支持分别评价 - **多产品设计项目**: - 分产品尾款结算(支持按产品或整体结算) - 产品全景图合集(支持单产品查看和全屋漫游) - 分产品客户评价(支持整体和细分产品评价) - 产品定位的投诉处理 - 多维度项目复盘(产品间对比分析) #### 1.4.2 产品间协同售后 ```mermaid graph TD A[所有产品设计完成] --> B{是否多产品设计项目?} B -->|否| C[单产品设计售后流程] B -->|是| D[多产品协同售后] D --> E[产品设计质量检查] D --> F[跨产品一致性验证] D --> G[整体结算策略选择] D --> H[全景图合成方案确定] E --> I[产品间对比分析] F --> I G --> J[分产品/整体结算] H --> K[产品全景图合集] I --> L[多产品复盘报告] J --> M[客户多维度评价] K --> N[产品漫游体验] L --> O[项目归档完成] M --> O N --> O style C fill:#e8f5e9 style D fill:#fff3e0 style O fill:#e3f2fd ``` ## 2. 多产品尾款结算模块 ### 2.1 基于Product表的多产品结算策略 #### 2.1.1 结算模式选择 ```typescript interface MultiProductSettlementStrategy { mode: 'unified' | 'separated' | 'hybrid'; // 统一结算/分产品结算/混合模式 products: string[]; // 参与结算的产品ID列表 settlementBreakdown: ProductSettlementBreakdown[]; // 产品结算明细 discounts: SettlementDiscount[]; // 多产品优惠 paymentSchedule: PaymentSchedule[]; // 付款计划 } interface ProductSettlementBreakdown { productId: string; productName: string; productType: string; // "bedroom", "living_room" 等 totalAmount: number; paidAmount: number; remainingAmount: number; completionPercentage: number; // 该产品完成度 isFullyDelivered: boolean; // 是否完全交付 specialNotes?: string; // 特殊说明 } class MultiProductSettlementManager { // 智能推荐结算模式 recommendSettlementMode(products: Product[]): SettlementModeRecommendation { const totalProducts = products.length; const completedProducts = products.filter(p => p.status === 'completed').length; const highPriorityProducts = products.filter(p => p.space?.priority >= 8).length; // 策略1:所有产品都完成且优先级相似,推荐统一结算 if (completedProducts === totalProducts && this.hasSimilarPriority(products)) { return { recommendedMode: 'unified', confidence: 0.9, reason: '所有产品已完成且优先级相近,统一结算更便捷' }; } // 策略2:高优先级产品已完成,推荐混合模式 if (highPriorityProducts > 0 && highPriorityProducts === completedProducts) { return { recommendedMode: 'hybrid', confidence: 0.8, reason: '高优先级产品已完成,可以优先结算' }; } // 策略3:产品完成情况差异大,推荐分产品结算 if (this.hasVariableCompletion(products)) { return { recommendedMode: 'separated', confidence: 0.85, reason: '各产品完成情况差异较大,分产品结算更清晰' }; } // 默认推荐统一结算 return { recommendedMode: 'unified', confidence: 0.6, reason: '标准项目,统一结算' }; } // 计算多产品优惠 calculateMultiProductDiscount( products: Product[], baseTotal: number ): SettlementDiscount[] { const discounts: SettlementDiscount[] = []; // 1. 产品数量折扣 if (products.length >= 5) { discounts.push({ type: 'product_count', description: '5产品及以上项目享受10%折扣', value: baseTotal * 0.1, applicable: true }); } else if (products.length >= 3) { discounts.push({ type: 'product_count', description: '3-4产品项目享受5%折扣', value: baseTotal * 0.05, applicable: true }); } // 2. 优先级折扣 const highPriorityCount = products.filter(p => p.space?.priority >= 8).length; if (highPriorityCount === products.length) { discounts.push({ type: 'high_priority', description: '全高优先级产品项目额外5%折扣', value: baseTotal * 0.05, applicable: true }); } // 3. 同时完成折扣 const completedWithinTimeframe = this.getProductsCompletedWithinTimeframe(products, 7); // 7天内 if (completedWithinTimeframe.length === products.length) { discounts.push({ type: 'simultaneous_completion', description: '所有产品同时完成享受3%折扣', value: baseTotal * 0.03, applicable: true }); } return discounts; } // 生成结算报告 generateSettlementReport( strategy: MultiProductSettlementStrategy, products: Product[] ): SettlementReport { const report: SettlementReport = { projectId: this.getProjectId(), settlementDate: new Date(), strategy: strategy.mode, totalProducts: products.length, completedProducts: products.filter(p => p.status === 'completed').length, // 财务明细 financials: { totalBaseAmount: this.calculateBaseAmount(products), discounts: strategy.discounts, finalAmount: this.calculateFinalAmount(products, strategy.discounts), paidAmount: this.calculatePaidAmount(products), remainingAmount: 0 }, // 产品详情 productDetails: strategy.settlementBreakdown.map(breakdown => ({ productId: breakdown.productId, productName: breakdown.productName, productType: breakdown.productType, totalAmount: breakdown.totalAmount, discountApplied: this.getProductDiscount(breakdown.productId, strategy.discounts), finalAmount: breakdown.remainingAmount, completionStatus: breakdown.isFullyDelivered ? 'completed' : 'partial', deliveryQuality: this.assessDeliveryQuality(breakdown.productId) })), // 风险评估 riskAssessment: this.assessSettlementRisks(products, strategy), // 建议 recommendations: this.generateSettlementRecommendations(products, strategy) }; return report; } } interface SettlementModeRecommendation { recommendedMode: 'unified' | 'separated' | 'hybrid'; confidence: number; // 推荐置信度 0-1 reason: string; // 推荐理由 } interface SettlementDiscount { type: 'space_count' | 'high_priority' | 'simultaneous_completion' | 'early_payment'; description: string; value: number; applicable: boolean; } interface SettlementReport { projectId: string; settlementDate: Date; strategy: string; totalSpaces: number; completedSpaces: number; financials: { totalBaseAmount: number; discounts: SettlementDiscount[]; finalAmount: number; paidAmount: number; remainingAmount: number; }; spaceDetails: any[]; riskAssessment: any; recommendations: string[]; } ``` ## 3. 多产品全景图合成模块 ### 3.1 基于Product表的产品全景图管理 #### 3.1.1 全景图合成策略 ```typescript class MultiProductPanoramaManager { // 生成产品全景图合集 async generateProductPanoramaCollection( products: Product[], synthesisOptions: PanoramaSynthesisOptions ): Promise { const collection: PanoramaCollection = { id: `collection_${Date.now()}`, projectId: this.getProjectId(), totalProducts: products.length, synthesisStrategy: synthesisOptions.strategy, // 单产品全景图 productPanoramas: [], // 跨产品连接 productConnections: [], // 全屋漫游 fullHouseTour: null, // 分享链接 shareLinks: { collection: '', individualProducts: {} as Record }, createdAt: new Date(), status: 'processing' }; // 1. 生成各产品独立全景图 for (const product of products) { const productPanorama = await this.generateProductPanorama(product, synthesisOptions); collection.productPanoramas.push(productPanorama); } // 2. 分析产品间连接关系 collection.productConnections = await this.analyzeProductConnections(products); // 3. 生成全屋漫游(如果是多产品项目) if (products.length > 1) { collection.fullHouseTour = await this.generateFullHouseTour( collection.productPanoramas, collection.productConnections ); } // 4. 生成分享链接 collection.shareLinks = await this.generatePanoramaShareLinks(collection); // 5. 保存并返回结果 await this.savePanoramaCollection(collection); return collection; } // 生成单产品全景图 private async generateProductPanorama( product: Product, options: PanoramaSynthesisOptions ): Promise { // 获取产品的最终交付图片 const finalImages = await this.getProductFinalImages(product.id); // KR Panel 集成 const krPanelConfig = { spaceType: product.productType, spaceName: product.productName, images: finalImages, synthesisQuality: options.quality, outputFormat: options.format, includeHotspots: options.includeHotspots, backgroundMusic: options.backgroundMusic }; // 调用 KR Panel 合成 const panoramaData = await this.krPanelService.synthesizePanorama(krPanelConfig); return { id: `panorama_${product.id}_${Date.now()}`, productId: product.id, productName: product.productName, productType: product.productType, // 全景图资源 panoramaUrl: panoramaData.panoramaUrl, thumbnailUrl: panoramaData.thumbnailUrl, previewImages: panoramaData.previewImages, // 热点信息 hotspots: panoramaData.hotspots || [], // 技术参数 resolution: panoramaData.resolution, fileSize: panoramaData.fileSize, renderTime: panoramaData.renderTime, // 元数据 metadata: { createdAt: new Date(), synthesisEngine: 'KR Panel', quality: options.quality, imageCount: finalImages.length } }; } // 分析产品连接关系 private async analyzeProductConnections(products: Product[]): Promise { const connections: ProductConnection[] = []; // 基于产品类型和位置推断连接关系 for (let i = 0; i < products.length; i++) { for (let j = i + 1; j < products.length; j++) { const product1 = products[i]; const product2 = products[j]; const connection = await this.determineProductConnection(product1, product2); if (connection) { connections.push(connection); } } } return connections; } private async determineProductConnection( product1: Product, product2: Product ): Promise { // 定义常见的产品连接关系 const connectionRules = [ { from: 'living_room', to: 'dining_room', type: 'direct', transitionStyle: 'open_passage', likelihood: 0.9 }, { from: 'living_room', to: 'corridor', type: 'direct', transitionStyle: 'doorway', likelihood: 0.8 }, { from: 'bedroom', to: 'corridor', type: 'direct', transitionStyle: 'doorway', likelihood: 0.9 }, { from: 'kitchen', to: 'dining_room', type: 'direct', transitionStyle: 'open_passage', likelihood: 0.7 } ]; // 查找匹配的连接规则 const rule = connectionRules.find(r => (r.from === product1.productType && r.to === product2.productType) || (r.from === product2.productType && r.to === product1.productType) ); if (rule && rule.likelihood > 0.6) { return { fromProductId: product1.id, toProductId: product2.id, connectionType: rule.type, transitionStyle: rule.transitionStyle, confidence: rule.likelihood, navigationLabel: `${product1.productName} → ${product2.productName}`, estimatedDistance: this.estimateProductDistance(product1, product2) }; } return null; } // 生成全屋漫游 private async generateFullHouseTour( panoramas: ProductPanorama[], connections: ProductConnection[] ): Promise { // 构建漫游路径 const tourPath = this.optimizeTourPath(panoramas, connections); // 生成导航数据 const navigationData = { panoramas: panoramas.map(p => ({ id: p.id, name: p.productName, type: p.productType, url: p.panoramaUrl, hotspots: p.hotspots })), connections: connections.map(c => ({ from: c.fromProductId, to: c.toProductId, type: c.connectionType, style: c.transitionStyle, label: c.navigationLabel })), path: tourPath }; // 生成漫游配置 const tourConfig = { autoPlay: true, transitionDuration: 2000, pauseDuration: 5000, showNavigation: true, backgroundMusic: 'soft_ambient', quality: 'high' }; return { id: `tour_${Date.now()}`, navigationData, tourConfig, totalDuration: this.calculateTourDuration(tourPath, tourConfig), estimatedSize: this.estimateTourSize(panoramas), generatedAt: new Date() }; } } interface PanoramaSynthesisOptions { strategy: 'individual' | 'connected' | 'full_house'; quality: 'standard' | 'high' | 'ultra'; format: 'jpg' | 'png' | 'webp'; includeHotspots: boolean; backgroundMusic?: string; maxFileSize?: number; } interface PanoramaCollection { id: string; projectId: string; totalProducts: number; synthesisStrategy: string; productPanoramas: ProductPanorama[]; productConnections: ProductConnection[]; fullHouseTour?: FullHouseTour; shareLinks: { collection: string; individualProducts: Record; }; createdAt: Date; status: 'processing' | 'completed' | 'failed'; } interface ProductPanorama { id: string; productId: string; productName: string; productType: string; panoramaUrl: string; thumbnailUrl: string; previewImages: string[]; hotspots: PanoramaHotspot[]; resolution: { width: number; height: number }; fileSize: number; renderTime: number; metadata: any; } interface ProductConnection { fromProductId: string; toProductId: string; connectionType: 'direct' | 'indirect' | 'external'; transitionStyle: 'doorway' | 'open_passage' | 'stair' | 'corridor'; confidence: number; navigationLabel: string; estimatedDistance: number; } interface FullHouseTour { id: string; navigationData: any; tourConfig: any; totalDuration: number; estimatedSize: number; generatedAt: Date; } ``` ## 4. 多产品客户评价模块 ### 4.1 基于Product表的分产品评价系统 #### 4.1.1 多维度评价结构 ```typescript interface MultiProductCustomerReview { id: string; projectId: string; submittedAt: Date; // 整体评价 overallReview: OverallReview; // 产品评价 productReviews: ProductReview[]; // 跨产品评价 crossProductReview: CrossProductReview; // 推荐意愿 recommendations: RecommendationData; } interface OverallReview { // 整体满意度评分 (1-5星) overallSatisfaction: number; // 多维度评分 dimensionRatings: { designQuality: number; // 设计质量 productPlanning: number; // 产品规划 colorCoordination: number; // 色彩协调 functionality: number; // 功能性 timeliness: number; // 及时性 communication: number; // 沟通效率 professionalism: number; // 专业程度 valueForMoney: number; // 性价比 }; // 文字评价 comments: { strengths: string; // 优点 improvements: string; // 改进建议 overallImpression: string; // 整体印象 additionalComments: string; // 其他意见 }; // 最满意和最不满意的产品 mostSatisfiedProduct?: string; leastSatisfiedProduct?: string; } interface ProductReview { productId: string; productName: string; productType: string; // "bedroom", "living_room" 等 // 产品满意度评分 satisfactionScore: number; // 产品特定评分 productSpecificRatings: { layoutDesign: number; // 布局设计 functionality: number; // 功能实现 aestheticAppeal: number; // 美观度 practicality: number; // 实用性 storageSolutions: number; // 收纳方案 lighting: number; // 灯光效果 }; // 产品使用反馈 usageFeedback: { actualUsage: string; // 实际使用情况 favoriteFeatures: string[]; // 最喜欢的特点 issuesEncountered: string[]; // 遇到的问题 modifications: string[]; // 后续改动 unexpectedBenefits: string[]; // 意外收获 }; // 产品文字评价 comments: { whatWorkedWell: string; // 做得好的地方 whatCouldBeBetter: string; // 可以改进的地方 personalNotes: string; // 个人备注 }; // 照片上传(实际使用后的照片) afterPhotos?: string[]; } interface CrossProductReview { // 产品间一致性 consistencyRatings: { styleConsistency: number; // 风格一致性 colorFlow: number; // 色彩流线 materialHarmony: number; // 材质和谐 scaleProportion: number; // 比例协调 }; // 动线体验 circulationExperience: { flowLogic: number; // 流线逻辑性 transitionSmoothness: number; // 过渡流畅度 accessibility: number; // 便利性 }; // 跨产品评价 crossProductComments: { productRelationships: string; // 产品关系 overallCohesion: string; // 整体协调性 suggestedImprovements: string; // 改进建议 }; } interface RecommendationData { wouldRecommend: boolean; // 是否推荐 likelihoodScore: number; // 推荐意愿 0-10 // 推荐理由 recommendationReasons: string[]; // 不推荐原因(如果不推荐) nonRecommendationReasons?: string[]; // 推荐给的人群 recommendedFor: string[]; // 联系信息(允许联系) contactPermission: boolean; contactInfo?: { wechat?: string; phone?: string; email?: string; }; } class MultiProductReviewManager { // 生成分产品评价链接 async generateMultiProductReviewLinks( projectId: string, products: Product[] ): Promise { const links: MultiProductReviewLinks = { projectId, collectionLink: '', productLinks: {} as Record, expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天后过期 createdAt: new Date() }; // 1. 生成合集评价链接 links.collectionLink = await this.generateCollectionReviewLink(projectId, products); // 2. 生成各产品独立评价链接 for (const product of products) { const productLink = await this.generateProductReviewLink(projectId, product); links.productLinks[product.id] = productLink; } // 3. 保存链接记录 await this.saveReviewLinks(links); return links; } // 处理多产品评价提交 async processMultiProductReview( reviewData: MultiProductCustomerReview ): Promise { const result: ReviewProcessingResult = { success: false, reviewId: '', processingSteps: [] }; try { // 1. 验证评价数据 await this.validateReviewData(reviewData); result.processingSteps.push({ step: 'validation', status: 'completed' }); // 2. 保存评价数据 const savedReview = await this.saveMultiProductReview(reviewData); result.reviewId = savedReview.id; result.processingSteps.push({ step: 'saving', status: 'completed' }); // 3. 计算评价统计 const statistics = await this.calculateReviewStatistics(reviewData); result.processingSteps.push({ step: 'statistics', status: 'completed' }); // 4. 更新项目评分 await this.updateProjectRating(reviewData.projectId, statistics); result.processingSteps.push({ step: 'rating_update', status: 'completed' }); // 5. 发送通知 await this.sendReviewNotifications(reviewData); result.processingSteps.push({ step: 'notifications', status: 'completed' }); result.success = true; } catch (error) { console.error('处理多产品评价失败:', error); result.error = error.message; } return result; } // 分析评价数据 async analyzeMultiProductReviews( projectId: string ): Promise { // 获取项目的所有评价 const reviews = await this.getProjectReviews(projectId); const analysis: MultiProductReviewAnalysis = { projectId, totalReviews: reviews.length, // 整体分析 overallAnalysis: this.analyzeOverallReviews(reviews), // 产品分析 productAnalysis: this.analyzeProductReviews(reviews), // 跨产品分析 crossProductAnalysis: this.analyzeCrossProductReviews(reviews), // 趋势分析 trendAnalysis: this.analyzeReviewTrends(reviews), // 改进建议 improvementSuggestions: this.generateImprovementSuggestions(reviews), // 对比分析 benchmarkComparison: await this.benchmarkAgainstIndustry(reviews) }; return analysis; } private analyzeProductReviews(reviews: MultiProductCustomerReview[]): Record { const productAnalysis: Record = {}; // 按产品分组统计 const reviewsByProduct: Record = {}; for (const review of reviews) { for (const productReview of review.productReviews) { if (!reviewsByProduct[productReview.productId]) { reviewsByProduct[productReview.productId] = []; } reviewsByProduct[productReview.productId].push(productReview); } } // 分析每个产品 for (const [productId, productReviews] of Object.entries(reviewsByProduct)) { const satisfactionScores = productReviews.map(r => r.satisfactionScore); const averageSatisfaction = satisfactionScores.reduce((a, b) => a + b, 0) / satisfactionScores.length; productAnalysis[productId] = { productId, productName: productReviews[0].productName, productType: productReviews[0].productType, totalReviews: productReviews.length, averageSatisfaction, satisfactionDistribution: this.calculateSatisfactionDistribution(satisfactionScores), // 详细评分分析 dimensionAverages: this.calculateDimensionAverages(productReviews), // 常见反馈 commonStrengths: this.extractCommonStrengths(productReviews), commonIssues: this.extractCommonIssues(productReviews), // 改进建议 improvementSuggestions: this.generateProductImprovementSuggestions(productReviews) }; } return productAnalysis; } } interface MultiProductReviewLinks { projectId: string; collectionLink: string; productLinks: Record; expiresAt: Date; createdAt: Date; } interface ReviewProcessingResult { success: boolean; reviewId: string; processingSteps: Array<{ step: string; status: 'completed' | 'failed' | 'skipped'; error?: string; }>; error?: string; } interface MultiProductReviewAnalysis { projectId: string; totalReviews: number; overallAnalysis: any; productAnalysis: Record; crossProductAnalysis: any; trendAnalysis: any; improvementSuggestions: string[]; benchmarkComparison: any; } interface ProductAnalysis { productId: string; productName: string; productType: string; totalReviews: number; averageSatisfaction: number; satisfactionDistribution: Record; dimensionAverages: Record; commonStrengths: string[]; commonIssues: string[]; improvementSuggestions: string[]; } ``` ## 5. 多产品投诉处理模块 ### 5.1 基于Product表的产品定位投诉系统 #### 5.1.1 产品投诉分类 ```typescript class MultiProductComplaintManager { // 创建产品相关投诉 async createProductComplaint( complaintData: ProductComplaintData ): Promise { const complaint: ProductComplaint = { id: `complaint_${Date.now()}`, projectId: complaintData.projectId, // 投诉分类 category: complaintData.category, subcategory: complaintData.subcategory, // 产品信息 productId: complaintData.productId, productName: complaintData.productName, productType: complaintData.productType, affectedProducts: complaintData.affectedProducts || [], // 投诉内容 title: complaintData.title, description: complaintData.description, severity: complaintData.severity, // 客户信息 customerInfo: complaintData.customerInfo, // 处理信息 status: 'pending', priority: this.calculateComplaintPriority(complaintData), assignedTo: null, assignedAt: null, // 时间信息 createdAt: new Date(), expectedResolutionTime: this.calculateExpectedResolutionTime(complaintData), // 附件 attachments: complaintData.attachments || [] }; // 保存投诉 const savedComplaint = await this.saveProductComplaint(complaint); // 自动分析并分配 await this.autoAnalyzeAndAssign(complaint); // 发送通知 await this.sendComplaintNotifications(complaint); return savedComplaint; } // 产品投诉智能分类 private classifyProductComplaint(description: string, productType: string): ComplaintClassification { const classification: ComplaintClassification = { category: 'other', subcategory: 'general', confidence: 0, keywords: [] }; // 产品特定关键词库 const productSpecificKeywords = { 'living_room': { 'sofa': { category: 'furniture', subcategory: 'seating' }, 'tv': { category: 'electronics', subcategory: 'entertainment' }, 'lighting': { category: 'lighting', subcategory: 'ambient' }, 'storage': { category: 'storage', subcategory: 'display' } }, 'bedroom': { 'bed': { category: 'furniture', subcategory: 'sleeping' }, 'wardrobe': { category: 'storage', subcategory: 'clothing' }, 'lighting': { category: 'lighting', subcategory: 'task' }, 'noise': { category: 'environmental', subcategory: 'acoustic' } }, 'kitchen': { 'cabinet': { category: 'furniture', subcategory: 'storage' }, 'countertop': { category: 'materials', subcategory: 'surface' }, 'appliances': { category: 'equipment', subcategory: 'kitchen' }, 'plumbing': { category: 'systems', subcategory: 'water' } } }; // 通用关键词 const generalKeywords = { 'color': { category: 'aesthetics', subcategory: 'color' }, 'size': { category: 'layout', subcategory: 'dimensions' }, 'quality': { category: 'quality', subcategory: 'materials' }, 'function': { category: 'functionality', subcategory: 'usage' }, 'delivery': { category: 'service', subcategory: 'timeline' } }; // 分析描述中的关键词 const allKeywords = { ...generalKeywords, ...(productSpecificKeywords[productType] || {}) }; const foundKeywords: Array<{ keyword: string; classification: any; confidence: number }> = []; for (const [keyword, classification] of Object.entries(allKeywords)) { if (description.toLowerCase().includes(keyword.toLowerCase())) { foundKeywords.push({ keyword, classification, confidence: 0.8 }); } } if (foundKeywords.length > 0) { // 选择置信度最高的分类 const bestMatch = foundKeywords.reduce((best, current) => current.confidence > best.confidence ? current : best ); classification.category = bestMatch.classification.category; classification.subcategory = bestMatch.classification.subcategory; classification.confidence = bestMatch.confidence; classification.keywords = foundKeywords.map(f => f.keyword); } return classification; } // 跨产品投诉处理 async handleCrossProductComplaint( complaintData: CrossProductComplaintData ): Promise { const complaint: CrossProductComplaint = { id: `cross_product_complaint_${Date.now()}`, projectId: complaintData.projectId, // 跨产品特有字段 primaryProductId: complaintData.primaryProductId, affectedProducts: complaintData.affectedProducts, relationshipType: complaintData.relationshipType, // 'style_inconsistency', 'functional_conflict', 'transition_issue' // 投诉内容 title: complaintData.title, description: complaintData.description, category: 'cross_product', severity: complaintData.severity, // 处理信息 status: 'pending', requiresMultiProductCoordination: true, assignedTeam: this.assignCrossProductTeam(complaintData), // 时间信息 createdAt: new Date(), expectedResolutionTime: this.calculateCrossProductResolutionTime(complaintData) }; // 分析产品间关系 complaint.productRelationshipAnalysis = await this.analyzeProductRelationship( complaint.primaryProductId, complaint.affectedProducts ); // 保存并处理 const savedComplaint = await this.saveCrossProductComplaint(complaint); await this.initiateCrossProductResolution(complaint); return savedComplaint; } // 生成投诉处理报告 async generateProductComplaintReport( projectId: string, timeRange?: { start: Date; end: Date } ): Promise { const complaints = await this.getProjectProductComplaints(projectId, timeRange); const report: ProductComplaintReport = { projectId, reportPeriod: timeRange || { start: new Date(0), end: new Date() }, // 统计概览 overview: { totalComplaints: complaints.length, resolvedComplaints: complaints.filter(c => c.status === 'resolved').length, pendingComplaints: complaints.filter(c => c.status === 'pending').length, averageResolutionTime: this.calculateAverageResolutionTime(complaints), complaintRate: this.calculateComplaintRate(complaints) }, // 产品分布 productDistribution: this.analyzeComplaintProductDistribution(complaints), // 分类统计 categoryBreakdown: this.analyzeComplaintCategories(complaints), // 严重程度分析 severityAnalysis: this.analyzeComplaintSeverity(complaints), // 处理效率 resolutionEfficiency: this.analyzeResolutionEfficiency(complaints), // 改进建议 recommendations: this.generateComplaintResolutionRecommendations(complaints), // 趋势分析 trends: this.analyzeComplaintTrends(complaints) }; return report; } } interface ProductComplaintData { projectId: string; productId: string; productName: string; productType: string; affectedProducts?: string[]; category: string; subcategory: string; title: string; description: string; severity: 'low' | 'medium' | 'high' | 'critical'; customerInfo: any; attachments?: any[]; } interface CrossProductComplaintData { projectId: string; primaryProductId: string; affectedProducts: string[]; relationshipType: 'style_inconsistency' | 'functional_conflict' | 'transition_issue'; title: string; description: string; severity: 'low' | 'medium' | 'high' | 'critical'; } interface ProductComplaint { id: string; projectId: string; category: string; subcategory: string; productId: string; productName: string; productType: string; affectedProducts: string[]; title: string; description: string; severity: string; customerInfo: any; status: string; priority: number; assignedTo: string; assignedAt: Date; createdAt: Date; expectedResolutionTime: Date; attachments: any[]; } interface CrossProductComplaint { id: string; projectId: string; primaryProductId: string; affectedProducts: string[]; relationshipType: string; title: string; description: string; category: string; severity: string; status: string; requiresMultiProductCoordination: boolean; assignedTeam: string[]; productRelationshipAnalysis: any; createdAt: Date; expectedResolutionTime: Date; } interface ProductComplaintReport { projectId: string; reportPeriod: { start: Date; end: Date }; overview: any; productDistribution: any; categoryBreakdown: any; severityAnalysis: any; resolutionEfficiency: any; recommendations: string[]; trends: any; } ``` ## 6. 多产品项目复盘模块 ### 6.1 基于Product表的产品对比分析 #### 6.1.1 产品绩效对比 ```typescript class MultiProductReviewManager { // 生成多产品项目复盘报告 async generateMultiProductReviewReport( projectId: string, options?: ReviewReportOptions ): Promise { const report: MultiProductReviewReport = { id: `review_report_${Date.now()}`, projectId, reportType: 'multi_product', generatedAt: new Date(), // 项目概览 projectOverview: await this.generateProjectOverview(projectId), // 产品对比分析 productComparison: await this.generateProductComparison(projectId), // 跨产品分析 crossProductAnalysis: await this.generateCrossProductAnalysis(projectId), // 效率分析 efficiencyAnalysis: await this.generateEfficiencyAnalysis(projectId), // 客户满意度分析 satisfactionAnalysis: await this.generateSatisfactionAnalysis(projectId), // 改进建议 improvementRecommendations: await this.generateImprovementRecommendations(projectId), // 经验总结 lessonsLearned: await this.extractLessonsLearned(projectId) }; return report; } // 生成产品对比分析 private async generateProductComparison(projectId: string): Promise { const products = await this.getProjectProducts(projectId); const comparison: ProductComparisonAnalysis = { products: products.map(product => ({ productId: product.id, productName: product.productName, productType: product.productType, metrics: {} as ProductMetrics })), // 对比维度 comparisonMetrics: [ 'deliveryTime', 'qualityScore', 'customerSatisfaction', 'budgetPerformance', 'revisionCount', 'teamEfficiency' ], // 产品排名 productRankings: {} as Record>, // 最佳实践 bestPractices: {}, // 改进产品 improvementAreas: {} }; // 计算各产品指标 for (const product of products) { comparison.products.find(p => p.productId === product.id)!.metrics = await this.calculateProductMetrics(product.id); } // 生成产品排名 for (const metric of comparison.comparisonMetrics) { const ranked = comparison.products .sort((a, b) => (b.metrics as any)[metric] - (a.metrics as any)[metric]) .map((product, index) => ({ productId: product.productId, rank: index + 1, value: (product.metrics as any)[metric] })); comparison.productRankings[metric] = ranked; } // 识别最佳实践 comparison.bestPractices = this.identifyBestPractices(comparison.products); // 识别改进区域 comparison.improvementAreas = this.identifyImprovementAreas(comparison.products); return comparison; } // 计算产品指标 private async calculateProductMetrics(productId: string): Promise { const metrics: ProductMetrics = { // 时间指标 deliveryTime: await this.calculateProductDeliveryTime(productId), onTimeDelivery: await this.calculateOnTimeDeliveryRate(productId), // 质量指标 qualityScore: await this.calculateProductQualityScore(productId), revisionCount: await this.countProductRevisions(productId), reworkRate: await this.calculateProductReworkRate(productId), // 客户满意度 customerSatisfaction: await this.calculateProductCustomerSatisfaction(productId), customerComplaints: await this.countProductComplaints(productId), // 财务指标 budgetPerformance: await this.calculateProductBudgetPerformance(productId), profitability: await this.calculateProductProfitability(productId), // 团队效率 teamEfficiency: await this.calculateProductTeamEfficiency(productId), resourceUtilization: await this.calculateProductResourceUtilization(productId) }; return metrics; } // 识别最佳实践 private identifyBestPractices(products: any[]): Record { const bestPractices: Record = {}; // 找出各维度表现最好的产品 const topPerformers = { deliveryTime: this.getTopPerformer(products, 'deliveryTime', 'asc'), // 时间越短越好 qualityScore: this.getTopPerformer(products, 'qualityScore', 'desc'), // 质量越高越好 customerSatisfaction: this.getTopPerformer(products, 'customerSatisfaction', 'desc'), budgetPerformance: this.getTopPerformer(products, 'budgetPerformance', 'desc'), teamEfficiency: this.getTopPerformer(products, 'teamEfficiency', 'desc') }; // 提取最佳实践 for (const [metric, performer] of Object.entries(topPerformers)) { const practices = await this.extractBestPractices(performer.productId, metric); bestPractices[metric] = practices; } return bestPractices; } // 生成跨产品分析 private async generateCrossProductAnalysis(projectId: string): Promise { const analysis: CrossProductAnalysis = { // 风格一致性分析 styleConsistency: await this.analyzeStyleConsistency(projectId), // 功能协调性分析 functionalCoordination: await this.analyzeFunctionalCoordination(projectId), // 产品流线分析 circulationFlow: await this.analyzeCirculationFlow(projectId), // 资源配置分析 resourceAllocation: await this.analyzeResourceAllocation(projectId), // 时间协调分析 timeCoordination: await this.analyzeTimeCoordination(projectId) }; return analysis; } // 风格一致性分析 private async analyzeStyleConsistency(projectId: string): Promise { const products = await this.getProjectProducts(projectId); // 提取各产品的设计元素 const designElements = await Promise.all( products.map(product => this.extractProductDesignElements(product.id)) ); // 分析一致性 const consistencyAnalysis: StyleConsistencyAnalysis = { overallConsistencyScore: this.calculateOverallConsistency(designElements), // 具体维度分析 colorConsistency: this.analyzeColorConsistency(designElements), materialConsistency: this.analyzeMaterialConsistency(designElements), styleConsistency: this.analyzeStyleConsistency(designElements), scaleConsistency: this.analyzeScaleConsistency(designElements), // 不一致点识别 inconsistencies: this.identifyStyleInconsistencies(designElements), // 改进建议 recommendations: this.generateStyleConsistencyRecommendations(designElements) }; return consistencyAnalysis; } // 生成效率分析 private async generateEfficiencyAnalysis(projectId: string): Promise { const analysis: EfficiencyAnalysis = { // 时间效率 timeEfficiency: await this.analyzeTimeEfficiency(projectId), // 资源效率 resourceEfficiency: await this.analyzeResourceEfficiency(projectId), // 流程效率 processEfficiency: await this.analyzeProcessEfficiency(projectId), // 协作效率 collaborationEfficiency: await this.analyzeCollaborationEfficiency(projectId), // 效率瓶颈 bottlenecks: await this.identifyEfficiencyBottlenecks(projectId), // 优化建议 optimizationSuggestions: await this.generateEfficiencyOptimizationSuggestions(projectId) }; return analysis; } } interface MultiProductReviewReport { id: string; projectId: string; reportType: string; generatedAt: Date; projectOverview: any; productComparison: ProductComparisonAnalysis; crossProductAnalysis: CrossProductAnalysis; efficiencyAnalysis: EfficiencyAnalysis; satisfactionAnalysis: any; improvementRecommendations: any[]; lessonsLearned: string[]; } interface ProductComparisonAnalysis { products: Array<{ productId: string; productName: string; productType: string; metrics: ProductMetrics; }>; comparisonMetrics: string[]; productRankings: Record>; bestPractices: Record; improvementAreas: Record; } interface ProductMetrics { deliveryTime: number; onTimeDelivery: number; qualityScore: number; revisionCount: number; reworkRate: number; customerSatisfaction: number; customerComplaints: number; budgetPerformance: number; profitability: number; teamEfficiency: number; resourceUtilization: number; } interface BestPractice { title: string; description: string; applicableTo: SpaceType[]; impactLevel: 'high' | 'medium' | 'low'; implementationComplexity: 'simple' | 'moderate' | 'complex'; } interface CrossSpaceAnalysis { styleConsistency: StyleConsistencyAnalysis; functionalCoordination: any; circulationFlow: any; resourceAllocation: any; timeCoordination: any; } interface StyleConsistencyAnalysis { overallConsistencyScore: number; colorConsistency: any; materialConsistency: any; styleConsistency: any; scaleConsistency: any; inconsistencies: any[]; recommendations: string[]; } interface EfficiencyAnalysis { timeEfficiency: any; resourceEfficiency: any; processEfficiency: any; collaborationEfficiency: any; bottlenecks: any[]; optimizationSuggestions: string[]; } ``` --- **文档版本**:v3.0 (Product表统一空间管理) **更新日期**:2025-10-20 **维护者**:YSS Development Team ### 2.1 功能特点 - 技术验收触发自动化结算 - 小程序支付自动监听 - 支付凭证智能识别 - 渲染大图自动解锁 - 客服一键发图 ### 2.2 自动化结算流程 #### 2.2.1 启动自动化结算 ```typescript // project-detail.ts lines 3892-3938 initiateAutoSettlement(): void { console.log('🚀 启动自动化尾款结算流程'); // 1. 权限验证 if (!this.isTechnicalView()) { alert('⚠️ 仅技术人员可以启动自动化结算流程'); return; } // 2. 验收状态检查 if (!this.isAllDeliveryCompleted()) { alert('⚠️ 请先完成所有交付阶段验收'); return; } console.log('✅ 验收状态检查通过'); // 3. 激活小程序支付监听 this.miniprogramPaymentStatus = 'active'; console.log('📱 小程序支付监听已激活'); // 4. 创建尾款结算记录 this.createFinalPaymentRecord(); // 5. 通知客服跟进尾款 this.notifyCustomerServiceForFinalPayment(); // 6. 启动支付自动化 this.setupPaymentAutomation(); alert('✅ 自动化结算流程已启动!\n\n- 小程序支付监听已激活\n- 客服已收到尾款跟进通知\n- 支付到账后将自动解锁大图'); } ``` **权限验证**: - 仅技术人员可以启动 - 确保所有交付阶段已完成 - 验证交付物质量合格 #### 2.2.2 创建结算记录 ```typescript // project-detail.ts lines 3940-3962 private createFinalPaymentRecord(): void { const totalAmount = this.orderAmount || 150000; const downPayment = totalAmount * 0.5; // 假设定金50% const remainingAmount = totalAmount - downPayment; this.settlementRecord = { id: `settlement-${Date.now()}`, projectId: this.projectId, totalAmount: totalAmount, downPayment: downPayment, remainingAmount: remainingAmount, paidAmount: 0, status: 'pending', createdAt: new Date(), dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7天后到期 paymentMethod: undefined, paidAt: undefined, notes: '技术验收完成,等待客户支付尾款' }; console.log('📝 尾款结算记录已创建:', this.settlementRecord); } ``` **结算记录结构**: ```typescript interface SettlementRecord { id: string; projectId: string; totalAmount: number; // 订单总金额 downPayment: number; // 定金金额 remainingAmount: number; // 尾款金额 paidAmount: number; // 已支付金额 status: 'pending' | 'partial' | 'completed' | 'overdue'; createdAt: Date; dueDate: Date; // 到期日期 paymentMethod?: 'wechat' | 'alipay' | 'bank'; paidAt?: Date; voucherUrl?: string; // 支付凭证URL notes?: string; } ``` #### 2.2.3 通知客服跟进 ```typescript // project-detail.ts lines 3964-3978 private notifyCustomerServiceForFinalPayment(): void { const notification = { type: 'final-payment-reminder', projectId: this.projectId, projectName: this.project?.name || '未命名项目', amount: this.settlementRecord?.remainingAmount || 0, dueDate: this.settlementRecord?.dueDate, message: `项目"${this.project?.name}"已完成技术验收,请跟进客户支付尾款 ¥${this.settlementRecord?.remainingAmount.toLocaleString()}` }; // 实际应用中调用通知服务 console.log('📧 已发送客服通知:', notification); // 模拟通知发送 alert(`✉️ 已通知客服跟进尾款\n\n项目: ${notification.projectName}\n尾款金额: ¥${notification.amount.toLocaleString()}`); } ``` ### 2.3 支付监听系统 #### 2.3.1 小程序支付监听 ```typescript // project-detail.ts lines 3980-4012 private setupPaymentAutomation(): void { console.log('🔧 设置支付自动化监听'); // 监听小程序支付状态变化 // 实际应用中应使用WebSocket或轮询API this.miniprogramPaymentStatus = 'active'; // 模拟支付监听(实际项目中应替换为真实的WebSocket连接) this.simulatePaymentMonitoring(); } private simulatePaymentMonitoring(): void { console.log('🔄 开始模拟支付监听...'); // 实际项目中应该是: // 1. 建立WebSocket连接到支付服务器 // 2. 监听支付成功事件 // 3. 接收支付信息(金额、方式、时间等) // 4. 自动触发解锁流程 // 这里仅作演示,实际不会自动触发 console.log('💡 提示: 客户通过小程序支付后,系统将自动接收通知'); console.log('💡 提示: 支付凭证识别功能可手动上传截图触发'); } ``` **监听流程**: ```mermaid sequenceDiagram participant Customer as 客户 participant MiniApp as 小程序 participant PaymentGateway as 支付网关 participant System as 系统 participant Designer as 设计师 Customer->>MiniApp: 发起尾款支付 MiniApp->>PaymentGateway: 调用支付接口 PaymentGateway-->>MiniApp: 支付成功回调 MiniApp->>System: 推送支付通知 System->>System: 更新结算状态 System->>System: 解锁渲染大图 System->>Designer: 通知客服发图 ``` #### 2.3.2 支付到账处理 ```typescript // project-detail.ts lines 4014-4048 onPaymentReceived(paymentInfo?: any): void { console.log('💰 收到支付通知:', paymentInfo); if (!this.settlementRecord) { console.error('❌ 结算记录不存在'); return; } // 更新结算状态 this.settlementRecord.status = 'completed'; this.settlementRecord.paidAmount = paymentInfo?.amount || this.settlementRecord.remainingAmount; this.settlementRecord.paymentMethod = paymentInfo?.method || 'wechat'; this.settlementRecord.paidAt = new Date(); console.log('✅ 结算状态已更新:', this.settlementRecord); // 自动解锁渲染大图 this.autoUnlockAndSendImages(); // 发送支付确认通知 this.sendPaymentConfirmationNotifications(); // 停止支付监听 this.miniprogramPaymentStatus = 'completed'; console.log('🎉 尾款结算流程完成'); } ``` #### 2.3.3 自动解锁大图 ```typescript // project-detail.ts lines 4050-4068 private autoUnlockAndSendImages(): void { console.log('🔓 开始自动解锁渲染大图'); // 解锁所有渲染大图 let unlockedCount = 0; this.renderLargeImages.forEach(img => { if (img.locked) { img.locked = false; unlockedCount++; } }); console.log(`✅ 已解锁${unlockedCount}张渲染大图`); // 通知客服可以发送大图 alert(`✅ 尾款已到账,${unlockedCount}张渲染大图已解锁!\n\n客服可一键发送给客户。`); // 触发界面更新 this.cdr.detectChanges(); } ``` ### 2.4 支付凭证识别 #### 2.4.1 凭证上传 ```typescript // 上传支付凭证 uploadPaymentVoucher(event: Event): void { const input = event.target as HTMLInputElement; if (!input.files || input.files.length === 0) return; const file = input.files[0]; // 验证文件类型 if (!file.type.startsWith('image/')) { alert('请上传图片格式的支付凭证'); return; } this.isUploadingVoucher = true; // 上传文件到服务器 this.uploadFile(file).then(url => { // 触发智能识别 this.recognizePaymentVoucher(url); }).catch(error => { console.error('支付凭证上传失败:', error); alert('上传失败,请重试'); this.isUploadingVoucher = false; }); } ``` #### 2.4.2 智能识别 ```typescript // 调用支付凭证识别服务 private recognizePaymentVoucher(imageUrl: string): void { this.paymentVoucherService.recognize(imageUrl).subscribe({ next: (result) => { console.log('识别结果:', result); // 显示识别结果 this.voucherRecognitionResult = { amount: result.amount, paymentMethod: result.method, transactionId: result.transactionId, transactionTime: result.time, confidence: result.confidence }; // 如果识别置信度高,自动填充 if (result.confidence > 0.8) { this.autoFillPaymentInfo(result); } this.isUploadingVoucher = false; }, error: (error) => { console.error('支付凭证识别失败:', error); alert('识别失败,请手动填写支付信息'); this.isUploadingVoucher = false; } }); } ``` **识别结果结构**: ```typescript interface VoucherRecognitionResult { amount: number; // 支付金额 paymentMethod: 'wechat' | 'alipay'; // 支付方式 transactionId: string; // 交易单号 transactionTime: Date; // 交易时间 confidence: number; // 识别置信度 0-1 merchantName?: string; // 商户名称 remarks?: string; // 备注信息 } ``` ### 2.5 一键发图功能 ```typescript // 客服一键发送渲染大图 sendImagesToCustomer(): void { const unlockedImages = this.renderLargeImages.filter(img => !img.locked); if (unlockedImages.length === 0) { alert('没有可发送的图片(渲染大图未解锁)'); return; } // 生成图片下载链接 const imageLinks = unlockedImages.map(img => ({ name: img.name, url: img.url, size: img.size })); // 调用发送服务 this.projectService.sendImagesToCustomer( this.projectId, imageLinks ).subscribe({ next: (result) => { if (result.success) { alert(`✅ 已成功发送${unlockedImages.length}张图片给客户!`); // 标记为已发送 unlockedImages.forEach(img => { img.synced = true; }); } }, error: (error) => { console.error('发送图片失败:', error); alert('发送失败,请重试'); } }); } ``` ## 3. 全景图合成模块 ### 3.1 功能特点 - KR Panel集成 - 智能空间标注 - 自动生成分享链接 - 漫游式预览体验 ### 3.2 全景图合成流程 #### 3.2.1 启动合成 ```typescript // 开始全景图合成 startPanoramicSynthesis(): void { console.log('🖼️ 启动全景图合成'); // 打开文件选择对话框 const input = document.createElement('input'); input.type = 'file'; input.multiple = true; input.accept = 'image/*'; input.onchange = (event: any) => { const files = Array.from(event.target.files) as File[]; if (files.length === 0) return; this.uploadAndSynthesizePanoramic(files); }; input.click(); } ``` #### 3.2.2 文件上传与合成 ```typescript // project-detail.ts lines 4217-4288 private uploadAndSynthesizePanoramic(files: File[]): void { console.log(`📤 开始上传${files.length}个文件...`); this.isUploadingPanoramicFiles = true; this.panoramicUploadProgress = 0; // 模拟文件上传进度 const uploadInterval = setInterval(() => { this.panoramicUploadProgress += 10; if (this.panoramicUploadProgress >= 100) { this.panoramicUploadProgress = 100; clearInterval(uploadInterval); // 上传完成,开始合成 this.synthesizePanoramicView(files); } }, 300); } private synthesizePanoramicView(files: File[]): void { console.log('🔧 开始合成全景图...'); this.isUploadingPanoramicFiles = false; this.isSynthesizingPanoramic = true; this.panoramicSynthesisProgress = 0; // 模拟合成进度 const synthesisInterval = setInterval(() => { this.panoramicSynthesisProgress += 5; if (this.panoramicSynthesisProgress >= 100) { this.panoramicSynthesisProgress = 100; clearInterval(synthesisInterval); // 合成完成 this.completePanoramicSynthesis(files); } }, 500); } ``` **KR Panel集成**: - 支持多角度图片合成 - 自动识别空间名称 - 生成3D漫游场景 - 支持VR模式预览 #### 3.2.3 完成合成 ```typescript // project-detail.ts lines 4290-4328 private completePanoramicSynthesis(files: File[]): void { this.isSynthesizingPanoramic = false; // 创建全景图合成记录 const synthesis: PanoramicSynthesis = { id: `panoramic-${Date.now()}`, name: `全景图_${new Date().toLocaleDateString()}`, createdAt: new Date(), spaces: files.map((file, index) => ({ id: `space-${index}`, name: this.extractSpaceName(file.name), imageUrl: URL.createObjectURL(file), angle: index * 60 // 假设每60度一个角度 })), previewUrl: 'https://example.com/panoramic/preview', downloadUrl: 'https://example.com/panoramic/download', shareLink: '', fileSize: files.reduce((sum, f) => sum + f.size, 0), status: 'completed' }; // 添加到历史记录 this.panoramicSynthesisHistory.push(synthesis); // 生成分享链接 this.generatePanoramicShareLink(synthesis); console.log('✅ 全景图合成完成:', synthesis); alert(`✅ 全景图合成完成!\n\n已生成${synthesis.spaces.length}个空间的全景图\n文件大小: ${this.formatFileSize(synthesis.fileSize)}`); } ``` **全景图数据结构**: ```typescript interface PanoramicSynthesis { id: string; name: string; createdAt: Date; spaces: Array<{ id: string; name: string; // 空间名称:客厅-角度1 imageUrl: string; angle: number; // 拍摄角度 }>; previewUrl: string; // 预览链接 downloadUrl: string; // 下载链接 shareLink: string; // 分享链接 fileSize: number; status: 'processing' | 'completed' | 'failed'; } ``` ### 3.3 自动生成分享链接 ```typescript // project-detail.ts lines 4330-4360 private generatePanoramicShareLink(synthesis: PanoramicSynthesis): void { // 生成唯一分享链接 const linkId = btoa(`panoramic-${synthesis.id}-${Date.now()}`); const shareLink = `https://vr.example.com/view/${linkId}`; synthesis.shareLink = shareLink; console.log('🔗 已生成分享链接:', shareLink); // 自动复制到剪贴板 this.copyToClipboard(shareLink); // 通知客服发送给客户 this.notifyCustomerServiceForPanoramicShare(synthesis); alert(`✅ 分享链接已生成并复制到剪贴板!\n\n${shareLink}\n\n客服已收到通知,可发送给客户。`); } private notifyCustomerServiceForPanoramicShare(synthesis: PanoramicSynthesis): void { const notification = { type: 'panoramic-ready', projectId: this.projectId, projectName: this.project?.name || '未命名项目', shareLink: synthesis.shareLink, spaceCount: synthesis.spaces.length, message: `项目"${this.project?.name}"的全景图已合成完成,请发送给客户查看` }; console.log('📧 已通知客服发送全景图:', notification); } ``` **分享链接特点**: - 唯一性标识 - 有效期控制(可选) - 访问统计 - VR模式支持 ## 4. 客户评价模块 ### 4.1 功能特点 - 多维度评分系统 - 评价链接自动生成 - 30天有效期 - 数据统计分析 ### 4.2 评价链接生成 #### 4.2.1 生成评价令牌 ```typescript // 生成客户评价链接 generateReviewLink(): void { console.log('📋 生成客户评价链接'); // 生成唯一评价令牌 const token = this.generateUniqueToken(); // 创建评价链接记录 const reviewLink: CustomerReviewLink = { id: `review-link-${Date.now()}`, projectId: this.projectId, token: token, link: `https://review.example.com/${token}`, createdAt: new Date(), expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天后过期 status: 'active', submittedAt: undefined }; this.customerReviewLink = reviewLink; // 复制到剪贴板 this.copyToClipboard(reviewLink.link); // 通知客服 this.notifyCustomerServiceForReview(reviewLink); console.log('✅ 评价链接已生成:', reviewLink); alert(`✅ 评价链接已生成并复制到剪贴板!\n\n${reviewLink.link}\n\n有效期: 30天\n客服已收到通知,可发送给客户。`); } ``` **评价链接结构**: ```typescript interface CustomerReviewLink { id: string; projectId: string; token: string; // 唯一令牌 link: string; // 完整链接 createdAt: Date; expiresAt: Date; // 过期时间 status: 'active' | 'submitted' | 'expired'; submittedAt?: Date; } ``` #### 4.2.2 生成唯一令牌 ```typescript private generateUniqueToken(): string { const timestamp = Date.now().toString(36); const randomStr = Math.random().toString(36).substring(2, 15); const projectIdHash = btoa(this.projectId).substring(0, 8); return `${timestamp}-${randomStr}-${projectIdHash}`; } ``` ### 4.3 评价数据结构 ```typescript interface CustomerReview { id: string; projectId: string; submittedAt: Date; // 多维度评分 (1-5星) ratings: { overall: number; // 整体满意度 timeliness: number; // 及时性 quality: number; // 设计质量 communication: number; // 沟通效率 professionalism: number; // 专业程度 }; // 文字评价 comments: { strengths: string; // 优点 improvements: string; // 改进建议 additional: string; // 其他意见 }; // 推荐意愿 wouldRecommend: boolean; // 附加信息 contact?: string; permitPublish: boolean; // 是否允许公开 } ``` ### 4.4 评价提交处理 ```typescript // 处理客户提交的评价 onReviewSubmitted(reviewData: CustomerReview): void { console.log('📝 收到客户评价:', reviewData); // 保存评价数据 this.customerReviews.push(reviewData); // 更新评价链接状态 if (this.customerReviewLink) { this.customerReviewLink.status = 'submitted'; this.customerReviewLink.submittedAt = new Date(); } // 计算平均分 this.calculateAverageRatings(); // 通知相关人员 this.notifyReviewReceived(reviewData); console.log('✅ 客户评价已保存'); alert('✅ 感谢客户的宝贵评价!\n\n评价数据已保存并通知相关人员。'); } ``` ### 4.5 评价数据分析 ```typescript // 计算平均评分 private calculateAverageRatings(): void { if (this.customerReviews.length === 0) { this.averageRatings = { overall: 0, timeliness: 0, quality: 0, communication: 0, professionalism: 0 }; return; } const sum = this.customerReviews.reduce((acc, review) => ({ overall: acc.overall + review.ratings.overall, timeliness: acc.timeliness + review.ratings.timeliness, quality: acc.quality + review.ratings.quality, communication: acc.communication + review.ratings.communication, professionalism: acc.professionalism + review.ratings.professionalism }), { overall: 0, timeliness: 0, quality: 0, communication: 0, professionalism: 0 }); const count = this.customerReviews.length; this.averageRatings = { overall: Math.round((sum.overall / count) * 10) / 10, timeliness: Math.round((sum.timeliness / count) * 10) / 10, quality: Math.round((sum.quality / count) * 10) / 10, communication: Math.round((sum.communication / count) * 10) / 10, professionalism: Math.round((sum.professionalism / count) * 10) / 10 }; console.log('📊 平均评分已更新:', this.averageRatings); } ``` ## 5. 投诉处理模块 ### 5.1 功能特点 - 人工创建投诉 - 关键词自动抓取 - 智能标注问题类型 - 处理进度跟踪 ### 5.2 人工创建投诉 #### 5.2.1 创建投诉记录 ```typescript // 人工创建投诉记录 createComplaintManually(): void { console.log('📝 人工创建投诉记录'); // 验证权限 if (!this.isTeamLeaderView() && !this.isCustomerServiceView()) { alert('⚠️ 仅组长和客服可以创建投诉记录'); return; } // 打开投诉创建表单 this.showComplaintForm = true; this.complaintFormData = { source: 'manual', stage: '', reason: '', description: '', severity: 'medium', tags: [] }; } ``` #### 5.2.2 提交投诉 ```typescript // 提交投诉记录 submitComplaint(): void { if (!this.complaintFormData.reason || !this.complaintFormData.description) { alert('请填写投诉原因和详细描述'); return; } const complaint: ComplaintRecord = { id: `complaint-${Date.now()}`, projectId: this.projectId, source: this.complaintFormData.source, stage: this.complaintFormData.stage || '未指定', reason: this.complaintFormData.reason, description: this.complaintFormData.description, severity: this.complaintFormData.severity, tags: this.complaintFormData.tags, status: '待处理', createdAt: new Date(), createdBy: this.getCurrentUserName(), assignedTo: undefined, resolvedAt: undefined, resolution: undefined }; // 添加到投诉列表 this.complaints.push(complaint); // 通知相关处理人员 this.notifyComplaintHandler(complaint); // 关闭表单 this.showComplaintForm = false; console.log('✅ 投诉记录已创建:', complaint); alert('✅ 投诉记录已创建!\n\n相关人员已收到通知。'); } ``` **投诉数据结构**: ```typescript interface ComplaintRecord { id: string; projectId: string; source: 'manual' | 'keyword-detection'; // 来源 stage: string; // 投诉环节 reason: string; // 投诉原因 description: string; // 详细描述 severity: 'low' | 'medium' | 'high'; // 严重程度 tags: string[]; // 问题标签 status: '待处理' | '处理中' | '已解决' | '已关闭'; createdAt: Date; createdBy: string; assignedTo?: string; // 分配给 resolvedAt?: Date; resolution?: string; // 解决方案 attachments?: Array<{ id: string; name: string; url: string; }>; } ``` ### 5.3 关键词自动监控 #### 5.3.1 设置关键词监测 ```typescript // 启动关键词监测 setupKeywordMonitoring(): void { console.log('🔍 设置关键词监测'); // 打开监控设置面板 this.showKeywordMonitoringSettings = true; // 初始化默认关键词 if (this.monitoringKeywords.length === 0) { this.monitoringKeywords = [ '不满意', '投诉', '退款', '差评', '质量问题', '延期', '态度差' ]; } console.log('📋 当前监控关键词:', this.monitoringKeywords); } ``` #### 5.3.2 关键词检测 ```typescript // 检测消息中的关键词 private detectKeywords(message: string): string[] { const detectedKeywords: string[] = []; this.monitoringKeywords.forEach(keyword => { if (message.includes(keyword)) { detectedKeywords.push(keyword); } }); return detectedKeywords; } ``` #### 5.3.3 自动创建投诉 ```typescript // 检测到关键词后自动创建投诉 onKeywordDetected(message: string, keyword: string): void { console.log(`🚨 检测到关键词: ${keyword}`); // 智能分析投诉严重程度 const severity = this.assessComplaintSeverity(keyword); // 智能识别投诉环节 const stage = this.identifyComplaintStage(message); // 智能标注问题类型 const tags = this.generateComplaintTags(message, keyword); // 自动创建投诉记录 const complaint: ComplaintRecord = { id: `complaint-auto-${Date.now()}`, projectId: this.projectId, source: 'keyword-detection', stage: stage, reason: `检测到关键词: ${keyword}`, description: message, severity: severity, tags: tags, status: '待处理', createdAt: new Date(), createdBy: '系统自动', assignedTo: undefined, resolvedAt: undefined, resolution: undefined }; this.complaints.push(complaint); // 立即通知处理人员 this.notifyUrgentComplaint(complaint); console.log('✅ 已自动创建投诉记录:', complaint); alert(`🚨 检测到客户投诉关键词: ${keyword}\n\n已自动创建投诉记录并通知相关人员。`); } ``` **智能分析方法**: ```typescript // 评估投诉严重程度 private assessComplaintSeverity(keyword: string): 'low' | 'medium' | 'high' { const highSeverityKeywords = ['退款', '投诉', '差评']; const mediumSeverityKeywords = ['不满意', '质量问题', '延期']; if (highSeverityKeywords.includes(keyword)) return 'high'; if (mediumSeverityKeywords.includes(keyword)) return 'medium'; return 'low'; } // 识别投诉环节 private identifyComplaintStage(message: string): string { if (message.includes('需求') || message.includes('沟通')) return '需求沟通'; if (message.includes('方案') || message.includes('设计')) return '方案确认'; if (message.includes('建模') || message.includes('模型')) return '建模'; if (message.includes('软装') || message.includes('家具')) return '软装'; if (message.includes('渲染') || message.includes('效果图')) return '渲染'; if (message.includes('交付') || message.includes('延期')) return '交付'; return '未识别'; } // 生成问题标签 private generateComplaintTags(message: string, keyword: string): string[] { const tags: string[] = []; // 根据消息内容添加标签 if (message.includes('需求') || message.includes('理解')) tags.push('需求理解'); if (message.includes('质量') || message.includes('效果')) tags.push('设计质量'); if (message.includes('延期') || message.includes('时间')) tags.push('交付延期'); if (message.includes('态度') || message.includes('服务')) tags.push('服务态度'); if (message.includes('价格') || message.includes('费用')) tags.push('价格问题'); // 添加关键词作为标签 tags.push(keyword); return [...new Set(tags)]; // 去重 } ``` ### 5.4 投诉处理流程 ```typescript // 处理投诉 handleComplaint(complaintId: string, resolution: string): void { const complaint = this.complaints.find(c => c.id === complaintId); if (!complaint) return; complaint.status = '已解决'; complaint.resolvedAt = new Date(); complaint.resolution = resolution; // 通知客户和相关人员 this.notifyComplaintResolved(complaint); console.log('✅ 投诉已处理:', complaint); alert('✅ 投诉处理完成!\n\n已通知客户和相关人员。'); } ``` ## 6. 项目复盘模块 ### 6.1 功能特点 - 三大核心板块(SOP执行数据、经验复盘、优化建议) - 数据可视化展示 - 自动生成复盘报告 - 导出为PDF/Excel ### 6.2 SOP执行数据 #### 6.2.1 数据收集 ```typescript // 收集SOP执行数据 collectSOPExecutionData(): any { return { requirementCommunications: this.countRequirementCommunications(), revisionCount: this.countRevisions(), deliveryCycleCompliance: this.checkDeliveryCycleCompliance(), customerSatisfaction: this.getCustomerSatisfactionScore(), stageDetails: this.getStageExecutionDetails() }; } ``` #### 6.2.2 阶段执行详情 ```typescript // 获取各阶段执行详情 private getStageExecutionDetails(): Array<{ stage: string; plannedDuration: number; actualDuration: number; status: 'on-time' | 'delayed' | 'ahead'; score: number; }> { return [ { stage: '需求沟通', plannedDuration: 2, actualDuration: 2, status: 'on-time', score: 95 }, { stage: '方案确认', plannedDuration: 3, actualDuration: 4, status: 'delayed', score: 85 }, { stage: '建模', plannedDuration: 5, actualDuration: 4, status: 'ahead', score: 92 }, { stage: '软装', plannedDuration: 3, actualDuration: 3, status: 'on-time', score: 90 }, { stage: '渲染', plannedDuration: 4, actualDuration: 5, status: 'delayed', score: 88 } ]; } ``` ### 6.3 经验复盘 #### 6.3.1 自动提取信息 ```typescript // 提取经验复盘数据 extractExperienceSummary(): any { return { customerNeeds: this.extractCustomerNeeds(), customerConcerns: this.extractCustomerConcerns(), complaintPoints: this.extractComplaintPoints(), projectHighlights: this.extractProjectHighlights(), keyConversations: this.extractKeyConversations() }; } ``` #### 6.3.2 提取客户需求 ```typescript private extractCustomerNeeds(): string[] { // 从需求沟通记录中提取 return [ '客户希望整体风格偏现代简约', '客户重视收纳空间的设计', '客户要求使用环保材料', '客户希望采光效果良好' ]; } ``` ### 6.4 优化建议 #### 6.4.1 生成优化建议 ```typescript // 生成优化建议 generateOptimizationSuggestions(): any[] { const suggestions = []; // 基于数据分析生成建议 const sopData = this.collectSOPExecutionData(); // 建议1:需求沟通优化 if (sopData.requirementCommunications > 5) { suggestions.push({ priority: 'high', priorityText: '高', category: '需求沟通', problem: '需求沟通次数过多(6次),影响项目效率', dataSupport: `需求沟通次数: ${sopData.requirementCommunications}次,标准为3-4次`, solution: '建议在首次沟通时使用标准化需求采集表,确保需求收集的完整性', actionPlan: [ '制定标准需求采集表模板', '培训设计师使用标准表单', '要求首次沟通必须完成80%需求确认' ], expectedImprovement: '减少30%的需求沟通次数', referenceCase: '参考项目#1234在使用标准表后沟通次数从6次降至3次', accepted: false }); } // 建议2:渲染阶段优化 const renderingStage = sopData.stageDetails.find((s: any) => s.stage === '渲染'); if (renderingStage && renderingStage.status === 'delayed') { suggestions.push({ priority: 'medium', priorityText: '中', category: '渲染效率', problem: '渲染阶段超期1天,影响整体交付时间', dataSupport: `计划4天,实际5天,超期率25%`, solution: '建议提前进行渲染设备性能检查,并预留缓冲时间', actionPlan: [ '每月检查渲染设备性能', '建模完成后立即启动预渲染', '渲染阶段预留20%缓冲时间' ], expectedImprovement: '降低渲染超期率至10%以下', referenceCase: '团队B采用预渲染机制后超期率从30%降至8%', accepted: false }); } return suggestions; } ``` **优化建议结构**: ```typescript interface OptimizationSuggestion { priority: 'high' | 'medium' | 'low'; priorityText: string; category: string; // 类别 problem: string; // 问题描述 dataSupport: string; // 数据支撑 solution: string; // 解决方案 actionPlan: string[]; // 行动计划 expectedImprovement: string; // 预期提升 referenceCase?: string; // 参考案例 accepted: boolean; // 是否已采纳 acceptedAt?: Date; } ``` ### 6.5 复盘报告生成 #### 6.5.1 生成报告 ```typescript // 生成完整复盘报告 generateReviewReport(): void { console.log('📊 生成项目复盘报告'); this.isGeneratingReview = true; // 模拟生成进度 let progress = 0; const interval = setInterval(() => { progress += 20; if (progress >= 100) { clearInterval(interval); this.completeReviewReportGeneration(); } }, 500); } private completeReviewReportGeneration(): void { // 收集所有数据 const reportData = { projectInfo: { name: this.project?.name || '未命名项目', id: this.projectId, startDate: this.project?.createdAt, endDate: new Date() }, sopData: this.collectSOPExecutionData(), experience: this.extractExperienceSummary(), suggestions: this.generateOptimizationSuggestions(), statistics: { overallScore: this.calculateOverallScore(), strengths: this.getProjectStrengths(), weaknesses: this.getProjectWeaknesses() } }; // 保存报告 this.reviewReport = reportData; this.isGeneratingReview = false; console.log('✅ 复盘报告生成完成:', reportData); alert('✅ 项目复盘报告已生成!\n\n您可以查看详情或导出报告。'); } ``` #### 6.5.2 导出报告 ```typescript // 导出复盘报告 exportReviewReport(format: 'pdf' | 'excel'): void { if (!this.reviewReport) { alert('请先生成复盘报告'); return; } console.log(`📤 导出复盘报告 (${format})`); if (format === 'excel') { this.exportAsExcel(this.reviewReport); } else { this.exportAsPDF(this.reviewReport); } } private exportAsExcel(data: any): void { // 转换为CSV格式 let csvContent = '\uFEFF'; // UTF-8 BOM // 项目概况 csvContent += '=== 项目概况 ===\n'; csvContent += `项目名称,${data.projectInfo.name}\n`; csvContent += `项目ID,${data.projectInfo.id}\n`; csvContent += `总耗时,${this.calculateProjectDuration()}天\n\n`; // SOP执行数据 csvContent += '=== SOP执行数据 ===\n'; csvContent += '阶段,计划时长,实际时长,状态,评分\n'; data.sopData.stageDetails.forEach((stage: any) => { csvContent += `${stage.stage},${stage.plannedDuration},${stage.actualDuration},${stage.status},${stage.score}\n`; }); // 优化建议 csvContent += '\n=== 优化建议 ===\n'; csvContent += '优先级,类别,问题,建议,预期提升\n'; data.suggestions.forEach((s: any) => { csvContent += `${s.priorityText},${s.category},"${s.problem}","${s.solution}",${s.expectedImprovement}\n`; }); // 创建下载 const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.href = url; link.download = `项目复盘报告_${data.projectInfo.name}_${this.formatDate(new Date())}.csv`; link.click(); URL.revokeObjectURL(url); console.log('✅ 报告已导出为Excel'); alert('✅ 报告已导出!\n\n文件已下载到您的下载文件夹。'); } ``` ## 7. 权限控制 ### 7.1 角色权限矩阵 | 操作 | 客服 | 设计师 | 组长 | 技术 | 财务 | |-----|------|--------|------|------|------| | 查看售后板块 | ✅ | ✅ | ✅ | ✅ | ✅ | | 启动自动结算 | ❌ | ❌ | ❌ | ✅ | ❌ | | 上传支付凭证 | ✅ | ❌ | ✅ | ❌ | ✅ | | 发送图片给客户 | ✅ | ❌ | ✅ | ❌ | ❌ | | 合成全景图 | ❌ | ❌ | ❌ | ✅ | ❌ | | 生成评价链接 | ✅ | ❌ | ✅ | ❌ | ❌ | | 创建投诉记录 | ✅ | ❌ | ✅ | ❌ | ❌ | | 处理投诉 | ✅ | ❌ | ✅ | ❌ | ❌ | | 生成复盘报告 | ❌ | ❌ | ✅ | ✅ | ❌ | | 导出复盘报告 | ❌ | ❌ | ✅ | ✅ | ✅ | ### 7.2 权限检查实现 ```typescript // 检查尾款结算权限 canInitiateSettlement(): boolean { return this.isTechnicalView(); } // 检查全景图合成权限 canSynthesizePanoramic(): boolean { return this.isTechnicalView(); } // 检查投诉处理权限 canHandleComplaints(): boolean { return this.isTeamLeaderView() || this.isCustomerServiceView(); } // 检查复盘报告权限 canGenerateReviewReport(): boolean { return this.isTeamLeaderView() || this.isTechnicalView(); } ``` ## 8. 数据流转 ### 8.1 售后流程总览 ```mermaid sequenceDiagram participant Tech as 技术 participant System as 系统 participant Payment as 支付网关 participant CS as 客服 participant Customer as 客户 Tech->>System: 启动自动结算 System->>Payment: 激活支付监听 System->>CS: 通知跟进尾款 CS->>Customer: 发送支付请求 Customer->>Payment: 完成支付 Payment->>System: 支付通知 System->>System: 解锁渲染大图 System->>CS: 通知发送大图 CS->>Customer: 发送渲染大图 System->>CS: 生成评价链接 CS->>Customer: 发送评价链接 Customer->>System: 提交评价 System->>Tech: 生成复盘报告 ``` ### 8.2 数据同步机制 ```typescript // 售后数据同步到项目 private syncAfterCareDataToProject(): void { if (!this.project) return; this.project.afterCare = { settlement: this.settlementRecord, panoramic: this.panoramicSynthesisHistory, reviews: this.customerReviews, complaints: this.complaints, reviewReport: this.reviewReport }; // 同步到服务器 this.projectService.updateProject(this.project).subscribe({ next: (result) => { console.log('✅ 售后数据已同步到项目'); }, error: (error) => { console.error('❌ 售后数据同步失败:', error); } }); } ``` ## 9. 异常处理 ### 9.1 支付监听失败 ```typescript // 支付监听连接失败处理 private handlePaymentMonitoringError(error: any): void { console.error('支付监听连接失败:', error); // 降级为手动模式 this.miniprogramPaymentStatus = 'error'; alert(`⚠️ 支付自动监听失败\n\n请使用"上传支付凭证"功能手动确认支付。`); // 显示手动上传入口 this.showManualPaymentVoucherUpload = true; } ``` ### 9.2 全景图合成失败 ```typescript // 全景图合成失败处理 private handlePanoramicSynthesisError(error: any): void { console.error('全景图合成失败:', error); this.isSynthesizingPanoramic = false; let errorMessage = '全景图合成失败'; if (error.code === 'INSUFFICIENT_IMAGES') { errorMessage = '图片数量不足,至少需要6张图片'; } else if (error.code === 'INVALID_FORMAT') { errorMessage = '图片格式不支持,请使用JPG或PNG格式'; } alert(`❌ ${errorMessage}\n\n请检查后重试。`); } ``` ### 9.3 评价链接过期 ```typescript // 检查评价链接是否过期 checkReviewLinkExpiry(linkId: string): boolean { const link = this.customerReviewLinks.find(l => l.id === linkId); if (!link) return true; if (link.status === 'expired') return true; // 检查是否超过有效期 if (new Date() > link.expiresAt) { link.status = 'expired'; return true; } return false; } // 重新生成过期的评价链接 regenerateReviewLink(oldLinkId: string): void { const oldLink = this.customerReviewLinks.find(l => l.id === oldLinkId); if (!oldLink) return; // 将旧链接标记为过期 oldLink.status = 'expired'; // 生成新链接 this.generateReviewLink(); alert('✅ 已重新生成评价链接!\n\n旧链接已失效,请使用新链接。'); } ``` ## 10. 性能优化 ### 10.1 报告生成优化 ```typescript // 使用Worker生成大型报告 private generateReportWithWorker(data: any): void { if (typeof Worker !== 'undefined') { const worker = new Worker(new URL('./report-generator.worker', import.meta.url)); worker.onmessage = ({ data }) => { console.log('报告生成完成:', data); this.reviewReport = data.report; this.isGeneratingReview = false; }; worker.postMessage({ type: 'generate', data }); } else { // 降级为同步生成 this.generateReportSync(data); } } ``` ### 10.2 图片压缩 ```typescript // 压缩全景图用于预览 private compressImageForPreview(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 压缩到最大1920px const maxDimension = 1920; const scale = Math.min(maxDimension / img.width, maxDimension / img.height, 1); canvas.width = img.width * scale; canvas.height = img.height * scale; ctx?.drawImage(img, 0, 0, canvas.width, canvas.height); canvas.toBlob((blob) => { if (blob) { resolve(blob); } else { reject(new Error('压缩失败')); } }, 'image/jpeg', 0.8); }; img.src = e.target?.result as string; }; reader.readAsDataURL(file); }); } ``` ### 10.3 数据缓存 ```typescript // 缓存复盘报告数据 private cacheReviewReport(report: any): void { try { localStorage.setItem( `review-report-${this.projectId}`, JSON.stringify(report) ); console.log('✅ 复盘报告已缓存'); } catch (error) { console.warn('缓存失败:', error); } } // 加载缓存的报告 private loadCachedReviewReport(): any | null { try { const cached = localStorage.getItem(`review-report-${this.projectId}`); if (cached) { return JSON.parse(cached); } } catch (error) { console.warn('加载缓存失败:', error); } return null; } ``` ## 11. 测试用例 ### 11.1 尾款结算测试 ```typescript describe('Final Payment Settlement', () => { it('should initiate auto settlement by technical user', () => { component.roleContext = 'technical'; spyOn(component, 'isAllDeliveryCompleted').and.returnValue(true); component.initiateAutoSettlement(); expect(component.miniprogramPaymentStatus).toBe('active'); expect(component.settlementRecord).toBeDefined(); }); it('should reject non-technical users', () => { component.roleContext = 'designer'; spyOn(window, 'alert'); component.initiateAutoSettlement(); expect(window.alert).toHaveBeenCalledWith(jasmine.stringContaining('仅技术人员')); }); it('should unlock images after payment received', () => { component.renderLargeImages = [ { id: '1', name: 'img1.jpg', url: 'blob:1', locked: true }, { id: '2', name: 'img2.jpg', url: 'blob:2', locked: true } ]; component.onPaymentReceived({ amount: 75000, method: 'wechat' }); expect(component.renderLargeImages.every(img => !img.locked)).toBe(true); }); }); ``` ### 11.2 投诉处理测试 ```typescript describe('Complaint Handling', () => { it('should create complaint manually', () => { component.roleContext = 'team-leader'; component.complaintFormData = { source: 'manual', stage: '渲染', reason: '质量问题', description: '渲染效果不符合预期', severity: 'medium', tags: ['设计质量'] }; component.submitComplaint(); expect(component.complaints.length).toBeGreaterThan(0); }); it('should detect keywords and create complaint', () => { const message = '我对渲染效果很不满意,要求退款'; component.monitoringKeywords = ['不满意', '退款']; component.onKeywordDetected(message, '不满意'); expect(component.complaints.length).toBeGreaterThan(0); expect(component.complaints[0].severity).toBe('high'); }); }); ``` ### 11.3 复盘报告测试 ```typescript describe('Review Report Generation', () => { it('should generate complete review report', () => { component.generateReviewReport(); // Wait for generation setTimeout(() => { expect(component.reviewReport).toBeDefined(); expect(component.reviewReport.sopData).toBeDefined(); expect(component.reviewReport.experience).toBeDefined(); expect(component.reviewReport.suggestions.length).toBeGreaterThan(0); }, 3000); }); it('should export report as Excel', () => { component.reviewReport = mockReviewReport; spyOn(document, 'createElement').and.callThrough(); component.exportReviewReport('excel'); expect(document.createElement).toHaveBeenCalledWith('a'); }); }); ``` --- **文档版本**:v1.0.0 **创建日期**:2025-10-16 **最后更新**:2025-10-16 **维护人**:产品团队 **相关文档**: - [AFTERCARE-FEATURES-README.md](/home/ryan/workspace/nova/yss-project/src/app/pages/designer/project-detail/AFTERCARE-FEATURES-README.md) - 售后模块功能实现说明