|
@@ -1,3110 +1,1330 @@
|
|
|
-# 项目管理 - 售后归档阶段 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<PanoramaCollection> {
|
|
|
-
|
|
|
- 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<string, string>
|
|
|
- },
|
|
|
-
|
|
|
- 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<ProductPanorama> {
|
|
|
-
|
|
|
- // 获取产品的最终交付图片
|
|
|
- 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<ProductConnection[]> {
|
|
|
- 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<ProductConnection | null> {
|
|
|
- // 定义常见的产品连接关系
|
|
|
- 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<FullHouseTour> {
|
|
|
-
|
|
|
- // 构建漫游路径
|
|
|
- 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<string, string>;
|
|
|
- };
|
|
|
- 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<MultiProductReviewLinks> {
|
|
|
-
|
|
|
- const links: MultiProductReviewLinks = {
|
|
|
- projectId,
|
|
|
- collectionLink: '',
|
|
|
- productLinks: {} as Record<string, string>,
|
|
|
- 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<ReviewProcessingResult> {
|
|
|
-
|
|
|
- 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<MultiProductReviewAnalysis> {
|
|
|
-
|
|
|
- // 获取项目的所有评价
|
|
|
- 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<string, ProductAnalysis> {
|
|
|
- const productAnalysis: Record<string, ProductAnalysis> = {};
|
|
|
-
|
|
|
- // 按产品分组统计
|
|
|
- const reviewsByProduct: Record<string, ProductReview[]> = {};
|
|
|
-
|
|
|
- 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<string, string>;
|
|
|
- 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<string, ProductAnalysis>;
|
|
|
- crossProductAnalysis: any;
|
|
|
- trendAnalysis: any;
|
|
|
- improvementSuggestions: string[];
|
|
|
- benchmarkComparison: any;
|
|
|
-}
|
|
|
-
|
|
|
-interface ProductAnalysis {
|
|
|
- productId: string;
|
|
|
- productName: string;
|
|
|
- productType: string;
|
|
|
- totalReviews: number;
|
|
|
- averageSatisfaction: number;
|
|
|
- satisfactionDistribution: Record<string, number>;
|
|
|
- dimensionAverages: Record<string, number>;
|
|
|
- commonStrengths: string[];
|
|
|
- commonIssues: string[];
|
|
|
- improvementSuggestions: string[];
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-## 5. 多产品投诉处理模块
|
|
|
-
|
|
|
-### 5.1 基于Product表的产品定位投诉系统
|
|
|
-
|
|
|
-#### 5.1.1 产品投诉分类
|
|
|
-```typescript
|
|
|
-class MultiProductComplaintManager {
|
|
|
- // 创建产品相关投诉
|
|
|
- async createProductComplaint(
|
|
|
- complaintData: ProductComplaintData
|
|
|
- ): Promise<ProductComplaint> {
|
|
|
-
|
|
|
- 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<CrossProductComplaint> {
|
|
|
-
|
|
|
- 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<ProductComplaintReport> {
|
|
|
-
|
|
|
- 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<MultiProductReviewReport> {
|
|
|
-
|
|
|
- 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<ProductComparisonAnalysis> {
|
|
|
- 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<string, Record<string, number>>,
|
|
|
-
|
|
|
- // 最佳实践
|
|
|
- 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<ProductMetrics> {
|
|
|
- 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<string, BestPractice[]> {
|
|
|
- const bestPractices: Record<string, BestPractice[]> = {};
|
|
|
-
|
|
|
- // 找出各维度表现最好的产品
|
|
|
- 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<CrossProductAnalysis> {
|
|
|
- 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<StyleConsistencyAnalysis> {
|
|
|
- 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<EfficiencyAnalysis> {
|
|
|
- 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<string, Array<{
|
|
|
- productId: string;
|
|
|
- rank: number;
|
|
|
- value: number;
|
|
|
- }>>;
|
|
|
- bestPractices: Record<string, BestPractice[]>;
|
|
|
- improvementAreas: Record<string, ImprovementArea[]>;
|
|
|
-}
|
|
|
-
|
|
|
-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';
|
|
|
-}
|
|
|
-```
|
|
|
+基于ProjectPayment、ProjectFeedback、Product等表结构和现有的stage-aftercare组件,设计完整的项目售后归档模块,包含**验收尾款**、**客户评价**、**项目复盘**三大核心功能,通过数据分析实现项目执行效率和团队绩效的科学评估。
|
|
|
|
|
|
-### 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);
|
|
|
+### 1. ProjectPayment(项目付款表)
|
|
|
|
|
|
- // 通知客服发送给客户
|
|
|
- this.notifyCustomerServiceForPanoramicShare(synthesis);
|
|
|
+现有结构已支持尾款管理:
|
|
|
+- **type**: "final" - 尾款类型
|
|
|
+- **stage**: "aftercare" - 售后归档阶段
|
|
|
+- **status**: "pending" | "partial" | "completed" - 支付状态
|
|
|
+- **voucherFile**: 指向ProjectFile的付款凭证
|
|
|
+- **product**: 支持按Product分摊尾款
|
|
|
|
|
|
- alert(`✅ 分享链接已生成并复制到剪贴板!\n\n${shareLink}\n\n客服已收到通知,可发送给客户。`);
|
|
|
-}
|
|
|
+### 2. ProjectFeedback(客户反馈表)
|
|
|
|
|
|
-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}"的全景图已合成完成,请发送给客户查看`
|
|
|
- };
|
|
|
+现有结构支持多维度评价:
|
|
|
+- **project**: 关联项目
|
|
|
+- **product**: 关联具体空间产品
|
|
|
+- **rating**: 1-5分评分
|
|
|
+- **feedbackType**: "suggestion" | "complaint" | "praise"
|
|
|
+- **content**: 评价内容
|
|
|
+- **stage**: 评价阶段
|
|
|
|
|
|
- console.log('📧 已通知客服发送全景图:', notification);
|
|
|
-}
|
|
|
-```
|
|
|
+### 3. Product(空间设计产品表)
|
|
|
|
|
|
-**分享链接特点**:
|
|
|
-- 唯一性标识
|
|
|
-- 有效期控制(可选)
|
|
|
-- 访问统计
|
|
|
-- VR模式支持
|
|
|
+现有结构支持复盘分析:
|
|
|
+- **profile**: 负责设计师
|
|
|
+- **stage**: 当前阶段状态
|
|
|
+- **status**: 产品状态
|
|
|
+- **estimatedDuration**: 预估工期
|
|
|
+- **order**: 排序顺序
|
|
|
+- **reviews**: 产品评价数组
|
|
|
|
|
|
-## 4. 客户评价模块
|
|
|
+## 三大功能模块设计
|
|
|
|
|
|
-### 4.1 功能特点
|
|
|
-- 多维度评分系统
|
|
|
-- 评价链接自动生成
|
|
|
-- 30天有效期
|
|
|
-- 数据统计分析
|
|
|
+### 1. 验收尾款模块
|
|
|
|
|
|
-### 4.2 评价链接生成
|
|
|
+#### 1.1 功能结构
|
|
|
|
|
|
-#### 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
|
|
|
+interface FinalPaymentModule {
|
|
|
+ // 尾款总览
|
|
|
+ paymentOverview: {
|
|
|
+ totalAmount: number; // 尾款总额
|
|
|
+ paidAmount: number; // 已支付金额
|
|
|
+ remainingAmount: number; // 待支付金额
|
|
|
+ dueDate: Date; // 应付款日期
|
|
|
+ overdueDays: number; // 逾期天数
|
|
|
+ status: PaymentStatus; // 支付状态
|
|
|
};
|
|
|
|
|
|
- this.customerReviewLink = reviewLink;
|
|
|
-
|
|
|
- // 复制到剪贴板
|
|
|
- this.copyToClipboard(reviewLink.link);
|
|
|
-
|
|
|
- // 通知客服
|
|
|
- this.notifyCustomerServiceForReview(reviewLink);
|
|
|
+ // 按产品分摊
|
|
|
+ productBreakdown: Array<{
|
|
|
+ productId: string;
|
|
|
+ productName: string;
|
|
|
+ productType: string;
|
|
|
+ amount: number;
|
|
|
+ percentage: number;
|
|
|
+ paidAmount: number;
|
|
|
+ vouchers: PaymentVoucher[];
|
|
|
+ }>;
|
|
|
|
|
|
- console.log('✅ 评价链接已生成:', reviewLink);
|
|
|
+ // 支付凭证管理
|
|
|
+ paymentVouchers: Array<{
|
|
|
+ id: string;
|
|
|
+ amount: number;
|
|
|
+ paymentMethod: string;
|
|
|
+ paymentTime: Date;
|
|
|
+ voucherFile: Parse.Object;
|
|
|
+ verifiedStatus: 'pending' | 'verified' | 'rejected';
|
|
|
+ ocrResult?: OCRResult;
|
|
|
+ notes?: string;
|
|
|
+ }>;
|
|
|
+}
|
|
|
|
|
|
- alert(`✅ 评价链接已生成并复制到剪贴板!\n\n${reviewLink.link}\n\n有效期: 30天\n客服已收到通知,可发送给客户。`);
|
|
|
+enum PaymentStatus {
|
|
|
+ PENDING = 'pending', // 待支付
|
|
|
+ OVERDUE = 'overdue', // 逾期
|
|
|
+ PARTIAL = 'partial', // 部分支付
|
|
|
+ PAID = 'paid', // 已支付
|
|
|
+ DISPUTED = 'disputed' // 争议中
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-**评价链接结构**:
|
|
|
+#### 1.2 OCR智能识别
|
|
|
+
|
|
|
+**支付凭证OCR识别结构**:
|
|
|
```typescript
|
|
|
-interface CustomerReviewLink {
|
|
|
- id: string;
|
|
|
- projectId: string;
|
|
|
- token: string; // 唯一令牌
|
|
|
- link: string; // 完整链接
|
|
|
- createdAt: Date;
|
|
|
- expiresAt: Date; // 过期时间
|
|
|
- status: 'active' | 'submitted' | 'expired';
|
|
|
- submittedAt?: Date;
|
|
|
+interface OCRResult {
|
|
|
+ confidence: number; // 识别置信度
|
|
|
+ amount: number; // 识别金额
|
|
|
+ paymentMethod: string; // 支付方式
|
|
|
+ transactionId?: string; // 交易号
|
|
|
+ payerName?: string; // 付款人姓名
|
|
|
+ paymentTime?: Date; // 付款时间
|
|
|
+ merchantName?: string; // 收款商户
|
|
|
+ rawText: string; // 原始识别文本
|
|
|
+ warnings?: string[]; // 识别警告
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-#### 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);
|
|
|
+#### 1.3 业务流程
|
|
|
|
|
|
- return `${timestamp}-${randomStr}-${projectIdHash}`;
|
|
|
-}
|
|
|
+```mermaid
|
|
|
+graph TD
|
|
|
+ A[进入售后归档] --> B[加载尾款信息]
|
|
|
+ B --> C{是否已有尾款记录?}
|
|
|
+ C -->|否| D[从报价自动生成尾款]
|
|
|
+ C -->|是| E[显示现有尾款状态]
|
|
|
+ D --> F[按产品分摊尾款]
|
|
|
+ E --> G[尾款管理界面]
|
|
|
+ F --> G
|
|
|
+ G --> H{支付状态}
|
|
|
+ H -->|待支付| I[上传支付凭证]
|
|
|
+ H -->|已支付| J[显示凭证列表]
|
|
|
+ I --> K[OCR自动识别]
|
|
|
+ K --> L[人工确认信息]
|
|
|
+ L --> M[更新支付状态]
|
|
|
+ M --> N[尾款结清]
|
|
|
+ J --> N
|
|
|
+ N --> O[进入评价阶段]
|
|
|
```
|
|
|
|
|
|
-### 4.3 评价数据结构
|
|
|
+### 2. 客户评价模块
|
|
|
+
|
|
|
+#### 2.1 多维度评价体系
|
|
|
|
|
|
```typescript
|
|
|
-interface CustomerReview {
|
|
|
- id: string;
|
|
|
+interface CustomerEvaluation {
|
|
|
+ // 基础信息
|
|
|
+ evaluationId: string;
|
|
|
projectId: string;
|
|
|
- submittedAt: Date;
|
|
|
-
|
|
|
- // 多维度评分 (1-5星)
|
|
|
- ratings: {
|
|
|
- overall: number; // 整体满意度
|
|
|
- timeliness: number; // 及时性
|
|
|
- quality: number; // 设计质量
|
|
|
- communication: number; // 沟通效率
|
|
|
- professionalism: number; // 专业程度
|
|
|
- };
|
|
|
+ customerId: string;
|
|
|
+ evaluationTime: Date;
|
|
|
|
|
|
- // 文字评价
|
|
|
- comments: {
|
|
|
- strengths: string; // 优点
|
|
|
- improvements: string; // 改进建议
|
|
|
- additional: string; // 其他意见
|
|
|
+ // 整体评价
|
|
|
+ overallRating: {
|
|
|
+ score: number; // 综合评分 1-5
|
|
|
+ comments: string; // 整体评价
|
|
|
+ wouldRecommend: boolean; // 是否愿意推荐
|
|
|
+ improvementSuggestions: string[];
|
|
|
};
|
|
|
|
|
|
- // 推荐意愿
|
|
|
- wouldRecommend: boolean;
|
|
|
+ // 分维度评价
|
|
|
+ dimensionRatings: Array<{
|
|
|
+ dimension: 'design_quality' | 'service_attitude' | 'delivery_timeliness' | 'value_for_money';
|
|
|
+ score: number; // 维度评分
|
|
|
+ weight: number; // 权重
|
|
|
+ comments?: string; // 维度评价
|
|
|
+ }>;
|
|
|
+
|
|
|
+ // 产品级评价
|
|
|
+ productRatings: Array<{
|
|
|
+ productId: string;
|
|
|
+ productName: string;
|
|
|
+ spaceSatisfaction: number; // 空间满意度
|
|
|
+ designRating: number; // 设计评分
|
|
|
+ functionalityRating: number; // 功能性评分
|
|
|
+ issues: string[]; // 存在问题
|
|
|
+ improvements: string[]; // 改进建议
|
|
|
+ photos?: string[]; // 实际效果照片
|
|
|
+ }>;
|
|
|
|
|
|
- // 附加信息
|
|
|
- contact?: string;
|
|
|
- permitPublish: boolean; // 是否允许公开
|
|
|
+ // 评价验证
|
|
|
+ verification: {
|
|
|
+ isVerified: boolean;
|
|
|
+ verificationMethod: 'customer_confirmation' | 'project_completion' | 'payment_confirmation';
|
|
|
+ verificationTime: Date;
|
|
|
+ verifiedBy: string;
|
|
|
+ };
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 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();
|
|
|
+#### 2.2 智能评价引导
|
|
|
|
|
|
- // 通知相关人员
|
|
|
- this.notifyReviewReceived(reviewData);
|
|
|
+**评价引导策略**:
|
|
|
+- **实时反馈**: 客户可在项目过程中随时记录反馈
|
|
|
+- **分阶段提醒**: 在关键节点(设计确认、交付完成)提醒评价
|
|
|
+- **个性化问题**: 基于项目类型生成针对性评价问题
|
|
|
+- **可视化对比**: 对比预期效果与实际效果的差异
|
|
|
|
|
|
- console.log('✅ 客户评价已保存');
|
|
|
+#### 2.3 评价数据结构
|
|
|
|
|
|
- alert('✅ 感谢客户的宝贵评价!\n\n评价数据已保存并通知相关人员。');
|
|
|
-}
|
|
|
-```
|
|
|
+```typescript
|
|
|
+// 扩展ProjectFeedback表结构
|
|
|
+interface EnhancedProjectFeedback extends ProjectFeedback {
|
|
|
+ // 产品关联
|
|
|
+ product?: Parse.Object;
|
|
|
|
|
|
-### 4.5 评价数据分析
|
|
|
+ // 多维度评分
|
|
|
+ dimensionScores?: {
|
|
|
+ designQuality: number; // 设计质量
|
|
|
+ serviceAttitude: number; // 服务态度
|
|
|
+ deliveryTimeliness: number; // 交付及时性
|
|
|
+ valueForMoney: number; // 性价比
|
|
|
+ };
|
|
|
|
|
|
-```typescript
|
|
|
-// 计算平均评分
|
|
|
-private calculateAverageRatings(): void {
|
|
|
- if (this.customerReviews.length === 0) {
|
|
|
- this.averageRatings = {
|
|
|
- overall: 0,
|
|
|
- timeliness: 0,
|
|
|
- quality: 0,
|
|
|
- communication: 0,
|
|
|
- professionalism: 0
|
|
|
- };
|
|
|
- return;
|
|
|
- }
|
|
|
+ // 评价上下文
|
|
|
+ context?: {
|
|
|
+ evaluationStage: string; // 评价阶段
|
|
|
+ touchpoints: string[]; // 接触点
|
|
|
+ emotions: string[]; // 情感标签
|
|
|
+ expectations: string[]; // 期望值
|
|
|
+ };
|
|
|
|
|
|
- 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
|
|
|
+ // 改进建议分类
|
|
|
+ improvementCategories?: {
|
|
|
+ design: string[]; // 设计改进
|
|
|
+ service: string[]; // 服务改进
|
|
|
+ process: string[]; // 流程改进
|
|
|
+ communication: string[]; // 沟通改进
|
|
|
};
|
|
|
|
|
|
- console.log('📊 平均评分已更新:', this.averageRatings);
|
|
|
+ // 推荐意愿
|
|
|
+ recommendationWillingness?: {
|
|
|
+ score: number; // 推荐评分 1-10
|
|
|
+ reasons: string[]; // 推荐原因
|
|
|
+ barriers?: string[]; // 推荐障碍
|
|
|
+ networkScope: string[]; // 愿意推荐的圈层
|
|
|
+ };
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-## 5. 投诉处理模块
|
|
|
-
|
|
|
-### 5.1 功能特点
|
|
|
-- 人工创建投诉
|
|
|
-- 关键词自动抓取
|
|
|
-- 智能标注问题类型
|
|
|
-- 处理进度跟踪
|
|
|
+### 3. 项目复盘模块
|
|
|
|
|
|
-### 5.2 人工创建投诉
|
|
|
+#### 3.1 复盘数据收集
|
|
|
|
|
|
-#### 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: []
|
|
|
+interface ProjectRetrospectiveData {
|
|
|
+ // 项目基础信息
|
|
|
+ projectBasic: {
|
|
|
+ title: string;
|
|
|
+ type: string;
|
|
|
+ duration: number; // 实际工期(天)
|
|
|
+ budget: number; // 项目预算
|
|
|
+ teamSize: number; // 团队规模
|
|
|
+ productCount: number; // 空间数量
|
|
|
};
|
|
|
-}
|
|
|
-```
|
|
|
|
|
|
-#### 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
|
|
|
+ // 时间节点分析
|
|
|
+ timelineAnalysis: {
|
|
|
+ nodes: Array<{
|
|
|
+ stage: string; // 阶段名称
|
|
|
+ plannedStart: Date; // 计划开始时间
|
|
|
+ actualStart: Date; // 实际开始时间
|
|
|
+ plannedEnd: Date; // 计划结束时间
|
|
|
+ actualEnd: Date; // 实际结束时间
|
|
|
+ duration: number; // 阶段耗时
|
|
|
+ delayDays: number; // 延期天数
|
|
|
+ efficiency: number; // 效率评分
|
|
|
+ }>;
|
|
|
+ bottlenecks: string[]; // 瓶颈环节
|
|
|
+ criticalPath: string[]; // 关键路径
|
|
|
};
|
|
|
|
|
|
- // 添加到投诉列表
|
|
|
- this.complaints.push(complaint);
|
|
|
-
|
|
|
- // 通知相关处理人员
|
|
|
- this.notifyComplaintHandler(complaint);
|
|
|
-
|
|
|
- // 关闭表单
|
|
|
- this.showComplaintForm = false;
|
|
|
+ // 团队效能分析
|
|
|
+ teamPerformance: Array<{
|
|
|
+ memberId: string;
|
|
|
+ memberName: string;
|
|
|
+ role: string; // 角色类型
|
|
|
+ workload: number; // 工作负载
|
|
|
+ efficiency: number; // 执行效率
|
|
|
+ quality: number; // 工作质量
|
|
|
+ collaboration: number; // 协作表现
|
|
|
+ timeDistribution: {
|
|
|
+ design: number; // 设计时间占比
|
|
|
+ communication: number; // 沟通时间占比
|
|
|
+ revision: number; // 修改时间占比
|
|
|
+ admin: number; // 行政时间占比
|
|
|
+ };
|
|
|
+ contributions: Array<{
|
|
|
+ type: 'design' | 'review' | 'coordination' | 'problem_solving';
|
|
|
+ description: string;
|
|
|
+ impact: 'high' | 'medium' | 'low';
|
|
|
+ }>;
|
|
|
+ }>;
|
|
|
|
|
|
- console.log('✅ 投诉记录已创建:', complaint);
|
|
|
+ // 质量分析
|
|
|
+ qualityAnalysis: {
|
|
|
+ revisionRate: number; // 修改率
|
|
|
+ firstPassYield: number; // 一次通过率
|
|
|
+ customerSatisfaction: number; // 客户满意度
|
|
|
+ issueResolutionTime: number; // 问题解决时间
|
|
|
+ qualityEscalations: number; // 质量升级次数
|
|
|
+ productScores: Array<{
|
|
|
+ productId: string;
|
|
|
+ productName: string;
|
|
|
+ finalScore: number;
|
|
|
+ issues: string[];
|
|
|
+ highlights: string[];
|
|
|
+ }>;
|
|
|
+ };
|
|
|
|
|
|
- alert('✅ 投诉记录已创建!\n\n相关人员已收到通知。');
|
|
|
+ // 财务分析
|
|
|
+ financialAnalysis: {
|
|
|
+ budgetVariance: number; // 预算偏差
|
|
|
+ profitMargin: number; // 利润率
|
|
|
+ costBreakdown: {
|
|
|
+ labor: number; // 人力成本
|
|
|
+ materials: number; // 材料成本
|
|
|
+ overhead: number; // 管理费用
|
|
|
+ revisions: number; // 修改成本
|
|
|
+ };
|
|
|
+ cashFlow: Array<{
|
|
|
+ period: string;
|
|
|
+ inflow: number;
|
|
|
+ outflow: number;
|
|
|
+ netFlow: number;
|
|
|
+ }>;
|
|
|
+ };
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-**投诉数据结构**:
|
|
|
+#### 3.2 AI智能复盘生成
|
|
|
+
|
|
|
+**复盘生成算法**:
|
|
|
```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;
|
|
|
- }>;
|
|
|
-}
|
|
|
-```
|
|
|
+class RetrospectiveGenerator {
|
|
|
+ /**
|
|
|
+ * 生成项目复盘报告
|
|
|
+ */
|
|
|
+ async generateRetrospective(
|
|
|
+ projectData: ProjectData,
|
|
|
+ changeLogs: ProjectChange[],
|
|
|
+ teamMetrics: TeamMetrics
|
|
|
+ ): Promise<RetrospectiveReport> {
|
|
|
+
|
|
|
+ // 1. 数据预处理
|
|
|
+ const processedData = await this.preprocessData(projectData, changeLogs);
|
|
|
+
|
|
|
+ // 2. 效率分析
|
|
|
+ const efficiencyAnalysis = this.analyzeEfficiency(processedData);
|
|
|
+
|
|
|
+ // 3. 质量分析
|
|
|
+ const qualityAnalysis = this.analyzeQuality(processedData);
|
|
|
+
|
|
|
+ // 4. 团队协作分析
|
|
|
+ const collaborationAnalysis = this.analyzeCollaboration(changeLogs, teamMetrics);
|
|
|
+
|
|
|
+ // 5. 客户满意度分析
|
|
|
+ const satisfactionAnalysis = this.analyzeCustomerSatisfaction(processedData);
|
|
|
+
|
|
|
+ // 6. 生成洞察和建议
|
|
|
+ const insights = await this.generateInsights({
|
|
|
+ efficiency: efficiencyAnalysis,
|
|
|
+ quality: qualityAnalysis,
|
|
|
+ collaboration: collaborationAnalysis,
|
|
|
+ satisfaction: satisfactionAnalysis
|
|
|
+ });
|
|
|
|
|
|
-### 5.3 关键词自动监控
|
|
|
+ return {
|
|
|
+ summary: this.generateSummary(insights),
|
|
|
+ highlights: insights.highlights,
|
|
|
+ challenges: insights.challenges,
|
|
|
+ lessons: insights.lessons,
|
|
|
+ recommendations: insights.recommendations,
|
|
|
+ teamMetrics: this.formatTeamMetrics(teamMetrics),
|
|
|
+ detailedAnalysis: {
|
|
|
+ efficiency: efficiencyAnalysis,
|
|
|
+ quality: qualityAnalysis,
|
|
|
+ collaboration: collaborationAnalysis,
|
|
|
+ timeline: this.analyzeTimeline(processedData)
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
-#### 5.3.1 设置关键词监测
|
|
|
-```typescript
|
|
|
-// 启动关键词监测
|
|
|
-setupKeywordMonitoring(): void {
|
|
|
- console.log('🔍 设置关键词监测');
|
|
|
-
|
|
|
- // 打开监控设置面板
|
|
|
- this.showKeywordMonitoringSettings = true;
|
|
|
-
|
|
|
- // 初始化默认关键词
|
|
|
- if (this.monitoringKeywords.length === 0) {
|
|
|
- this.monitoringKeywords = [
|
|
|
- '不满意',
|
|
|
- '投诉',
|
|
|
- '退款',
|
|
|
- '差评',
|
|
|
- '质量问题',
|
|
|
- '延期',
|
|
|
- '态度差'
|
|
|
- ];
|
|
|
+ /**
|
|
|
+ * 分析项目效率
|
|
|
+ */
|
|
|
+ private analyzeEfficiency(data: ProcessedData): EfficiencyAnalysis {
|
|
|
+ return {
|
|
|
+ overallEfficiency: this.calculateOverallEfficiency(data),
|
|
|
+ stageEfficiency: this.calculateStageEfficiency(data),
|
|
|
+ resourceUtilization: this.calculateResourceUtilization(data),
|
|
|
+ bottlenecks: this.identifyBottlenecks(data),
|
|
|
+ timeDistribution: this.analyzeTimeDistribution(data)
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
- console.log('📋 当前监控关键词:', this.monitoringKeywords);
|
|
|
+ /**
|
|
|
+ * 识别项目瓶颈
|
|
|
+ */
|
|
|
+ private identifyBottlenecks(data: ProcessedData): Array<{
|
|
|
+ stage: string;
|
|
|
+ issue: string;
|
|
|
+ impact: string;
|
|
|
+ suggestion: string;
|
|
|
+ severity: 'high' | 'medium' | 'low';
|
|
|
+ }> {
|
|
|
+ const bottlenecks = [];
|
|
|
+
|
|
|
+ // 分析时间瓶颈
|
|
|
+ const timeBottlenecks = this.analyzeTimeBottlenecks(data.timeline);
|
|
|
+ bottlenecks.push(...timeBottlenecks);
|
|
|
+
|
|
|
+ // 分析协作瓶颈
|
|
|
+ const collaborationBottlenecks = this.analyzeCollaborationBottlenecks(data.teamWork);
|
|
|
+ bottlenecks.push(...collaborationBottlenecks);
|
|
|
+
|
|
|
+ // 分析质量瓶颈
|
|
|
+ const qualityBottlenecks = this.analyzeQualityBottlenecks(data.quality);
|
|
|
+ bottlenecks.push(...qualityBottlenecks);
|
|
|
+
|
|
|
+ return bottlenecks.sort((a, b) => {
|
|
|
+ const severityOrder = { 'high': 3, 'medium': 2, 'low': 1 };
|
|
|
+ return severityOrder[b.severity] - severityOrder[a.severity];
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-#### 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;
|
|
|
-}
|
|
|
-```
|
|
|
+#### 3.3 团队成员效率分析
|
|
|
|
|
|
-#### 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
|
|
|
+interface TeamMemberEfficiency {
|
|
|
+ memberId: string;
|
|
|
+ memberName: string;
|
|
|
+
|
|
|
+ // 时间效率
|
|
|
+ timeEfficiency: {
|
|
|
+ plannedVsActual: number; // 计划vs实际时间比
|
|
|
+ taskCompletionRate: number; // 任务完成率
|
|
|
+ onTimeDeliveryRate: number; // 按时交付率
|
|
|
+ avgTaskDuration: number; // 平均任务耗时
|
|
|
};
|
|
|
|
|
|
- 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('价格问题');
|
|
|
+ // 质量效率
|
|
|
+ qualityEfficiency: {
|
|
|
+ firstPassYield: number; // 一次通过率
|
|
|
+ revisionRate: number; // 修改率
|
|
|
+ errorRate: number; // 错误率
|
|
|
+ customerRating: number; // 客户评分
|
|
|
+ };
|
|
|
|
|
|
- // 添加关键词作为标签
|
|
|
- tags.push(keyword);
|
|
|
+ // 协作效率
|
|
|
+ collaborationEfficiency: {
|
|
|
+ communicationFrequency: number; // 沟通频次
|
|
|
+ responseTime: number; // 响应时间
|
|
|
+ coordinationScore: number; // 协调评分
|
|
|
+ knowledgeSharing: number; // 知识分享
|
|
|
+ };
|
|
|
|
|
|
- return [...new Set(tags)]; // 去重
|
|
|
+ // 综合效率评分
|
|
|
+ overallEfficiency: {
|
|
|
+ score: number; // 综合评分 0-100
|
|
|
+ ranking: number; // 团队排名
|
|
|
+ strengths: string[]; // 优势领域
|
|
|
+ improvements: string[]; // 改进建议
|
|
|
+ };
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 5.4 投诉处理流程
|
|
|
+#### 3.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;
|
|
|
+interface ProjectScenarioAnalysis {
|
|
|
+ // 项目复杂度评估
|
|
|
+ complexityAssessment: {
|
|
|
+ scopeComplexity: number; // 范围复杂度
|
|
|
+ technicalComplexity: number; // 技术复杂度
|
|
|
+ coordinationComplexity: number; // 协调复杂度
|
|
|
+ clientComplexity: number; // 客户复杂度
|
|
|
+ overallComplexity: 'simple' | 'moderate' | 'complex' | 'highly_complex';
|
|
|
+ };
|
|
|
|
|
|
- // 通知客户和相关人员
|
|
|
- this.notifyComplaintResolved(complaint);
|
|
|
+ // 风险识别
|
|
|
+ riskIdentification: {
|
|
|
+ identifiedRisks: Array<{
|
|
|
+ riskType: 'timeline' | 'budget' | 'quality' | 'resource' | 'scope';
|
|
|
+ probability: number; // 发生概率 0-1
|
|
|
+ impact: number; // 影响程度 0-1
|
|
|
+ riskLevel: 'low' | 'medium' | 'high' | 'critical';
|
|
|
+ mitigation: string; // 缓解措施
|
|
|
+ owner: string; // 负责人
|
|
|
+ }>;
|
|
|
+ riskMitigationEffectiveness: number; // 风险缓解有效性
|
|
|
+ };
|
|
|
|
|
|
- console.log('✅ 投诉已处理:', complaint);
|
|
|
+ // 成功因素分析
|
|
|
+ successFactors: {
|
|
|
+ keySuccessFactors: string[]; // 关键成功因素
|
|
|
+ clientAlignment: number; // 客户对齐度
|
|
|
+ teamCapability: number; // 团队能力
|
|
|
+ processMaturity: number; // 流程成熟度
|
|
|
+ technologyFit: number; // 技术适配性
|
|
|
+ };
|
|
|
|
|
|
- alert('✅ 投诉处理完成!\n\n已通知客户和相关人员。');
|
|
|
+ // 改进机会
|
|
|
+ improvementOpportunities: Array<{
|
|
|
+ area: string; // 改进领域
|
|
|
+ currentPerformance: number; // 当前绩效
|
|
|
+ targetPerformance: number; // 目标绩效
|
|
|
+ improvementPotential: number; // 改进潜力
|
|
|
+ requiredActions: string[]; // 必要行动
|
|
|
+ estimatedEffort: string; // 预估工作量
|
|
|
+ expectedBenefit: string; // 预期收益
|
|
|
+ }>;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-## 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()
|
|
|
- };
|
|
|
-}
|
|
|
-```
|
|
|
+### 1. 整体架构设计
|
|
|
|
|
|
-#### 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
|
|
|
- }
|
|
|
- ];
|
|
|
-}
|
|
|
-```
|
|
|
+interface AftercarePageArchitecture {
|
|
|
+ // 视图管理
|
|
|
+ currentView: 'overview' | 'payment' | 'evaluation' | 'retrospective' | 'archive';
|
|
|
|
|
|
-### 6.3 经验复盘
|
|
|
+ // 数据状态
|
|
|
+ dataStatus: {
|
|
|
+ loading: boolean;
|
|
|
+ error?: string;
|
|
|
+ lastSyncTime?: Date;
|
|
|
+ };
|
|
|
|
|
|
-#### 6.3.1 自动提取信息
|
|
|
-```typescript
|
|
|
-// 提取经验复盘数据
|
|
|
-extractExperienceSummary(): any {
|
|
|
- return {
|
|
|
- customerNeeds: this.extractCustomerNeeds(),
|
|
|
- customerConcerns: this.extractCustomerConcerns(),
|
|
|
- complaintPoints: this.extractComplaintPoints(),
|
|
|
- projectHighlights: this.extractProjectHighlights(),
|
|
|
- keyConversations: this.extractKeyConversations()
|
|
|
+ // 权限控制
|
|
|
+ permissions: {
|
|
|
+ canEditPayment: boolean;
|
|
|
+ canViewEvaluation: boolean;
|
|
|
+ canSubmitEvaluation: boolean;
|
|
|
+ canGenerateRetrospective: boolean;
|
|
|
+ canArchive: boolean;
|
|
|
};
|
|
|
-}
|
|
|
-```
|
|
|
|
|
|
-#### 6.3.2 提取客户需求
|
|
|
-```typescript
|
|
|
-private extractCustomerNeeds(): string[] {
|
|
|
- // 从需求沟通记录中提取
|
|
|
- return [
|
|
|
- '客户希望整体风格偏现代简约',
|
|
|
- '客户重视收纳空间的设计',
|
|
|
- '客户要求使用环保材料',
|
|
|
- '客户希望采光效果良好'
|
|
|
- ];
|
|
|
+ // 导航配置
|
|
|
+ navigation: {
|
|
|
+ steps: Array<{
|
|
|
+ id: string;
|
|
|
+ title: string;
|
|
|
+ icon: string;
|
|
|
+ status: 'pending' | 'in_progress' | 'completed';
|
|
|
+ enabled: boolean;
|
|
|
+ }>;
|
|
|
+ currentStep: number;
|
|
|
+ };
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 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;
|
|
|
-}
|
|
|
+### 2. 用户界面设计
|
|
|
+
|
|
|
+#### 2.1 概览视图
|
|
|
+
|
|
|
+**概览仪表板**:
|
|
|
+- **进度卡片**: 显示三大模块完成状态
|
|
|
+- **关键指标**: 尾款状态、客户满意度、项目效率
|
|
|
+- **快速操作**: 跳转到各功能模块
|
|
|
+- **时间线**: 项目关键节点时间轴
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 概览视图模板 -->
|
|
|
+<div class="aftercare-overview">
|
|
|
+ <!-- 进度卡片 -->
|
|
|
+ <div class="progress-cards">
|
|
|
+ <div class="progress-card" [class.completed]="paymentCompleted">
|
|
|
+ <div class="card-icon">💰</div>
|
|
|
+ <h3>验收尾款</h3>
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-fill" [style.width.%]="paymentProgress"></div>
|
|
|
+ </div>
|
|
|
+ <p class="status">{{ paymentStatusText }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="progress-card" [class.completed]="evaluationCompleted">
|
|
|
+ <div class="card-icon">⭐</div>
|
|
|
+ <h3>客户评价</h3>
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-fill" [style.width.%]="evaluationProgress"></div>
|
|
|
+ </div>
|
|
|
+ <p class="status">{{ evaluationStatusText }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="progress-card" [class.completed]="retrospectiveCompleted">
|
|
|
+ <div class="card-icon">📊</div>
|
|
|
+ <h3>项目复盘</h3>
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-fill" [style.width.%]="retrospectiveProgress"></div>
|
|
|
+ </div>
|
|
|
+ <p class="status">{{ retrospectiveStatusText }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 关键指标 -->
|
|
|
+ <div class="key-metrics">
|
|
|
+ <div class="metric-card">
|
|
|
+ <h4>尾款状态</h4>
|
|
|
+ <div class="metric-value">{{ finalPaymentAmount }}</div>
|
|
|
+ <div class="metric-change" [class.positive]="paymentTrend > 0">
|
|
|
+ {{ paymentTrend > 0 ? '↑' : '↓' }} {{ Math.abs(paymentTrend) }}%
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="metric-card">
|
|
|
+ <h4>客户满意度</h4>
|
|
|
+ <div class="metric-value">{{ customerSatisfaction }}/5.0</div>
|
|
|
+ <div class="metric-stars">
|
|
|
+ @for (star of [1,2,3,4,5]; track star) {
|
|
|
+ <span class="star" [class.filled]="star <= customerSatisfaction">★</span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="metric-card">
|
|
|
+ <h4>项目效率</h4>
|
|
|
+ <div class="metric-value">{{ projectEfficiency }}%</div>
|
|
|
+ <div class="metric-label">vs 行业平均</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 快速操作 -->
|
|
|
+ <div class="quick-actions">
|
|
|
+ <button class="action-btn" (click)="navigateToPayment()" [disabled]="!canEditPayment">
|
|
|
+ <span class="btn-icon">💰</span>
|
|
|
+ 管理尾款
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="action-btn" (click)="navigateToEvaluation()" [disabled]="!canViewEvaluation">
|
|
|
+ <span class="btn-icon">⭐</span>
|
|
|
+ 查看评价
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="action-btn" (click)="generateRetrospective()" [disabled]="!canGenerateRetrospective">
|
|
|
+ <span class="btn-icon">📊</span>
|
|
|
+ 生成复盘
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button class="action-btn primary" (click)="archiveProject()" [disabled]="!canArchive">
|
|
|
+ <span class="btn-icon">📁</span>
|
|
|
+ 归档项目
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
```
|
|
|
|
|
|
-**优化建议结构**:
|
|
|
-```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;
|
|
|
-}
|
|
|
-```
|
|
|
+#### 2.2 尾款管理视图
|
|
|
|
|
|
-### 6.5 复盘报告生成
|
|
|
+**智能尾款管理**:
|
|
|
+- **分摊显示**: 按Product分摊显示尾款明细
|
|
|
+- **OCR识别**: 自动识别支付凭证信息
|
|
|
+- **状态追踪**: 实时显示支付进度
|
|
|
+- **提醒功能**: 逾期提醒和付款通知
|
|
|
|
|
|
-#### 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);
|
|
|
-}
|
|
|
+interface PaymentManagementComponent {
|
|
|
+ // 尾款数据
|
|
|
+ finalPayment: FinalPaymentModule;
|
|
|
+
|
|
|
+ // 产品分摊
|
|
|
+ productAllocation: Array<{
|
|
|
+ product: Parse.Object;
|
|
|
+ allocation: PaymentAllocation;
|
|
|
+ status: 'pending' | 'partial' | 'completed';
|
|
|
+ }>;
|
|
|
|
|
|
-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()
|
|
|
- }
|
|
|
+ // OCR处理
|
|
|
+ ocrProcessing: {
|
|
|
+ isProcessing: boolean;
|
|
|
+ progress: number;
|
|
|
+ result?: OCRResult;
|
|
|
};
|
|
|
|
|
|
- // 保存报告
|
|
|
- this.reviewReport = reportData;
|
|
|
- this.isGeneratingReview = false;
|
|
|
-
|
|
|
- console.log('✅ 复盘报告生成完成:', reportData);
|
|
|
-
|
|
|
- alert('✅ 项目复盘报告已生成!\n\n您可以查看详情或导出报告。');
|
|
|
+ // 上传管理
|
|
|
+ uploadManagement: {
|
|
|
+ multipleUpload: boolean;
|
|
|
+ dragDropEnabled: boolean;
|
|
|
+ supportedFormats: string[];
|
|
|
+ maxFileSize: number;
|
|
|
+ };
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-#### 6.5.2 导出报告
|
|
|
-```typescript
|
|
|
-// 导出复盘报告
|
|
|
-exportReviewReport(format: 'pdf' | 'excel'): void {
|
|
|
- if (!this.reviewReport) {
|
|
|
- alert('请先生成复盘报告');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- console.log(`📤 导出复盘报告 (${format})`);
|
|
|
+#### 2.3 客户评价视图
|
|
|
+
|
|
|
+**交互式评价界面**:
|
|
|
+- **引导式评价**: 分步骤引导客户完成评价
|
|
|
+- **可视化反馈**: 支持图片、语音等多媒体评价
|
|
|
+- **实时预览**: 评价内容实时预览和确认
|
|
|
+- **奖励机制**: 评价完成后的客户激励
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 客户评价模板 -->
|
|
|
+<div class="customer-evaluation">
|
|
|
+ <!-- 评价引导 -->
|
|
|
+ <div class="evaluation-wizard">
|
|
|
+ <div class="wizard-steps">
|
|
|
+ <div class="step" [class.active]="currentStep === 1" [class.completed]="stepCompleted(1)">
|
|
|
+ <span class="step-number">1</span>
|
|
|
+ <span class="step-label">整体评价</span>
|
|
|
+ </div>
|
|
|
+ <div class="step" [class.active]="currentStep === 2" [class.completed]="stepCompleted(2)">
|
|
|
+ <span class="step-number">2</span>
|
|
|
+ <span class="step-label">分项评分</span>
|
|
|
+ </div>
|
|
|
+ <div class="step" [class.active]="currentStep === 3" [class.completed]="stepCompleted(3)">
|
|
|
+ <span class="step-number">3</span>
|
|
|
+ <span class="step-label">详细反馈</span>
|
|
|
+ </div>
|
|
|
+ <div class="step" [class.active]="currentStep === 4" [class.completed]="stepCompleted(4)">
|
|
|
+ <span class="step-number">4</span>
|
|
|
+ <span class="step-label">确认提交</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 评价内容 -->
|
|
|
+ <div class="evaluation-content">
|
|
|
+ <!-- 整体评价 -->
|
|
|
+ @if (currentStep === 1) {
|
|
|
+ <div class="overall-evaluation">
|
|
|
+ <h3>您对整体服务的评价如何?</h3>
|
|
|
+ <div class="rating-scale">
|
|
|
+ @for (rating of [1,2,3,4,5]; track rating) {
|
|
|
+ <button class="rating-btn" (click)="setOverallRating(rating)">
|
|
|
+ <span class="rating-number">{{ rating }}</span>
|
|
|
+ <span class="rating-label">{{ getRatingLabel(rating) }}</span>
|
|
|
+ </button>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ <textarea
|
|
|
+ class="evaluation-textarea"
|
|
|
+ placeholder="请分享您的整体体验..."
|
|
|
+ [(ngModel)]="evaluation.overallComments">
|
|
|
+ </textarea>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
|
|
|
- if (format === 'excel') {
|
|
|
- this.exportAsExcel(this.reviewReport);
|
|
|
- } else {
|
|
|
- this.exportAsPDF(this.reviewReport);
|
|
|
- }
|
|
|
-}
|
|
|
+ <!-- 分项评分 -->
|
|
|
+ @if (currentStep === 2) {
|
|
|
+ <div class="dimension-evaluation">
|
|
|
+ @for (dimension of evaluationDimensions; track dimension) {
|
|
|
+ <div class="dimension-item">
|
|
|
+ <h4>{{ dimension.label }}</h4>
|
|
|
+ <p class="dimension-desc">{{ dimension.description }}</p>
|
|
|
+ <div class="star-rating">
|
|
|
+ @for (star of [1,2,3,4,5]; track star) {
|
|
|
+ <span
|
|
|
+ class="star"
|
|
|
+ [class.filled]="star <= dimension.rating"
|
|
|
+ (click)="setDimensionRating(dimension.key, star)">
|
|
|
+ ★
|
|
|
+ </span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ }
|
|
|
|
|
|
-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文件已下载到您的下载文件夹。');
|
|
|
-}
|
|
|
+ <!-- 详细反馈 -->
|
|
|
+ @if (currentStep === 3) {
|
|
|
+ <div class="detailed-feedback">
|
|
|
+ <div class="feedback-section">
|
|
|
+ <h4>最喜欢的方面</h4>
|
|
|
+ <div class="feedback-tags">
|
|
|
+ @for (tag of positiveTags; track tag) {
|
|
|
+ <span class="feedback-tag" (click)="toggleTag(tag, 'positive')">
|
|
|
+ {{ tag }}
|
|
|
+ </span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="feedback-section">
|
|
|
+ <h4>希望改进的方面</h4>
|
|
|
+ <div class="feedback-tags">
|
|
|
+ @for (tag of improvementTags; track tag) {
|
|
|
+ <span class="feedback-tag" (click)="toggleTag(tag, 'improvement')">
|
|
|
+ {{ tag }}
|
|
|
+ </span>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="feedback-section">
|
|
|
+ <h4>照片反馈</h4>
|
|
|
+ <div class="photo-upload">
|
|
|
+ <div class="upload-area" (drop)="onPhotoDrop($event)" (dragover)="onDragOver($event)">
|
|
|
+ <span class="upload-icon">📷</span>
|
|
|
+ <p>上传实际效果照片</p>
|
|
|
+ <input type="file" accept="image/*" multiple (change)="onPhotoSelect($event)">
|
|
|
+ </div>
|
|
|
+ <div class="uploaded-photos">
|
|
|
+ @for (photo of uploadedPhotos; track photo) {
|
|
|
+ <div class="photo-item">
|
|
|
+ <img [src]="photo.url" [alt]="photo.description">
|
|
|
+ <button class="remove-btn" (click)="removePhoto(photo.id)">×</button>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
```
|
|
|
|
|
|
-## 7. 权限控制
|
|
|
-
|
|
|
-### 7.1 角色权限矩阵
|
|
|
+#### 2.4 项目复盘视图
|
|
|
|
|
|
-| 操作 | 客服 | 设计师 | 组长 | 技术 | 财务 |
|
|
|
-|-----|------|--------|------|------|------|
|
|
|
-| 查看售后板块 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
|
-| 启动自动结算 | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
|
-| 上传支付凭证 | ✅ | ❌ | ✅ | ❌ | ✅ |
|
|
|
-| 发送图片给客户 | ✅ | ❌ | ✅ | ❌ | ❌ |
|
|
|
-| 合成全景图 | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
|
-| 生成评价链接 | ✅ | ❌ | ✅ | ❌ | ❌ |
|
|
|
-| 创建投诉记录 | ✅ | ❌ | ✅ | ❌ | ❌ |
|
|
|
-| 处理投诉 | ✅ | ❌ | ✅ | ❌ | ❌ |
|
|
|
-| 生成复盘报告 | ❌ | ❌ | ✅ | ✅ | ❌ |
|
|
|
-| 导出复盘报告 | ❌ | ❌ | ✅ | ✅ | ✅ |
|
|
|
-
|
|
|
-### 7.2 权限检查实现
|
|
|
+**数据驱动复盘**:
|
|
|
+- **可视化图表**: 时间线、效率分析、质量统计
|
|
|
+- **团队绩效**: 个人和团队的多维度分析
|
|
|
+- **智能洞察**: AI生成的关键发现和建议
|
|
|
+- **对比分析**: 与历史项目和行业基准对比
|
|
|
|
|
|
```typescript
|
|
|
-// 检查尾款结算权限
|
|
|
-canInitiateSettlement(): boolean {
|
|
|
- return this.isTechnicalView();
|
|
|
-}
|
|
|
+interface RetrospectiveVisualization {
|
|
|
+ // 时间线可视化
|
|
|
+ timelineChart: {
|
|
|
+ type: 'gantt' | 'timeline';
|
|
|
+ data: Array<{
|
|
|
+ phase: string;
|
|
|
+ planned: DateRange;
|
|
|
+ actual: DateRange;
|
|
|
+ participants: string[];
|
|
|
+ efficiency: number;
|
|
|
+ }>;
|
|
|
+ };
|
|
|
|
|
|
-// 检查全景图合成权限
|
|
|
-canSynthesizePanoramic(): boolean {
|
|
|
- return this.isTechnicalView();
|
|
|
-}
|
|
|
+ // 效率分析图表
|
|
|
+ efficiencyCharts: {
|
|
|
+ radarChart: {
|
|
|
+ dimensions: string[];
|
|
|
+ teamData: number[][];
|
|
|
+ benchmarkData: number[];
|
|
|
+ };
|
|
|
+ barChart: {
|
|
|
+ categories: string[];
|
|
|
+ planned: number[];
|
|
|
+ actual: number[];
|
|
|
+ };
|
|
|
+ };
|
|
|
|
|
|
-// 检查投诉处理权限
|
|
|
-canHandleComplaints(): boolean {
|
|
|
- return this.isTeamLeaderView() || this.isCustomerServiceView();
|
|
|
-}
|
|
|
+ // 团队绩效矩阵
|
|
|
+ teamMatrix: {
|
|
|
+ members: string[];
|
|
|
+ metrics: string[];
|
|
|
+ data: number[][];
|
|
|
+ heatmap: boolean;
|
|
|
+ };
|
|
|
|
|
|
-// 检查复盘报告权限
|
|
|
-canGenerateReviewReport(): boolean {
|
|
|
- return this.isTeamLeaderView() || this.isTechnicalView();
|
|
|
+ // 趋势分析
|
|
|
+ trendAnalysis: {
|
|
|
+ timeSeries: Array<{
|
|
|
+ date: Date;
|
|
|
+ efficiency: number;
|
|
|
+ quality: number;
|
|
|
+ satisfaction: number;
|
|
|
+ }>;
|
|
|
+ projections: Array<{
|
|
|
+ futureDate: Date;
|
|
|
+ predictedEfficiency: number;
|
|
|
+ confidence: number;
|
|
|
+ }>;
|
|
|
+ };
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-## 8. 数据流转
|
|
|
+### 3. 交互体验优化
|
|
|
|
|
|
-### 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: 生成复盘报告
|
|
|
-```
|
|
|
+#### 3.1 响应式设计
|
|
|
|
|
|
-### 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
|
|
|
- };
|
|
|
+#### 3.2 实时协作
|
|
|
|
|
|
- // 同步到服务器
|
|
|
- this.projectService.updateProject(this.project).subscribe({
|
|
|
- next: (result) => {
|
|
|
- console.log('✅ 售后数据已同步到项目');
|
|
|
- },
|
|
|
- error: (error) => {
|
|
|
- console.error('❌ 售后数据同步失败:', error);
|
|
|
- }
|
|
|
- });
|
|
|
-}
|
|
|
-```
|
|
|
+**多用户协同**:
|
|
|
+- **实时状态**: 显示其他用户当前操作
|
|
|
+- **冲突解决**: 多人编辑时的冲突处理
|
|
|
+- **消息通知**: 重要操作的实时通知
|
|
|
+- **版本控制**: 数据变更的版本管理
|
|
|
|
|
|
-## 9. 异常处理
|
|
|
+#### 3.3 智能化辅助
|
|
|
|
|
|
-### 9.1 支付监听失败
|
|
|
+**AI辅助功能**:
|
|
|
+- **智能提示**: 基于历史数据的操作建议
|
|
|
+- **自动填充**: 常用信息的自动填充
|
|
|
+- **异常检测**: 数据异常的自动识别和提醒
|
|
|
+- **预测分析**: 基于当前数据的结果预测
|
|
|
|
|
|
-```typescript
|
|
|
-// 支付监听连接失败处理
|
|
|
-private handlePaymentMonitoringError(error: any): void {
|
|
|
- console.error('支付监听连接失败:', error);
|
|
|
+## 数据分析具体方法
|
|
|
|
|
|
- // 降级为手动模式
|
|
|
- this.miniprogramPaymentStatus = 'error';
|
|
|
+### 1. 项目效率分析算法
|
|
|
|
|
|
- alert(`⚠️ 支付自动监听失败\n\n请使用"上传支付凭证"功能手动确认支付。`);
|
|
|
+#### 1.1 时间效率计算
|
|
|
|
|
|
- // 显示手动上传入口
|
|
|
- this.showManualPaymentVoucherUpload = true;
|
|
|
-}
|
|
|
-```
|
|
|
+```typescript
|
|
|
+class ProjectEfficiencyAnalyzer {
|
|
|
+ /**
|
|
|
+ * 计算整体项目效率
|
|
|
+ */
|
|
|
+ calculateOverallEfficiency(projectData: ProjectData): EfficiencyScore {
|
|
|
+ const { plannedDuration, actualDuration, milestones, teamWorkload } = projectData;
|
|
|
+
|
|
|
+ // 时间效率 = 计划时间 / 实际时间
|
|
|
+ const timeEfficiency = Math.min(1.0, plannedDuration / actualDuration) * 100;
|
|
|
+
|
|
|
+ // 里程碑完成率
|
|
|
+ const milestoneCompletionRate = this.calculateMilestoneCompletionRate(milestones);
|
|
|
+
|
|
|
+ // 资源利用效率
|
|
|
+ const resourceUtilization = this.calculateResourceUtilization(teamWorkload);
|
|
|
+
|
|
|
+ // 质量效率(基于修改率)
|
|
|
+ const qualityEfficiency = this.calculateQualityEfficiency(projectData.quality);
|
|
|
+
|
|
|
+ // 综合效率评分(加权平均)
|
|
|
+ const weights = {
|
|
|
+ time: 0.3,
|
|
|
+ milestones: 0.25,
|
|
|
+ resources: 0.25,
|
|
|
+ quality: 0.2
|
|
|
+ };
|
|
|
|
|
|
-### 9.2 全景图合成失败
|
|
|
+ const overallEfficiency =
|
|
|
+ timeEfficiency * weights.time +
|
|
|
+ milestoneCompletionRate * weights.milestones +
|
|
|
+ resourceUtilization * weights.resources +
|
|
|
+ qualityEfficiency * weights.quality;
|
|
|
|
|
|
-```typescript
|
|
|
-// 全景图合成失败处理
|
|
|
-private handlePanoramicSynthesisError(error: any): void {
|
|
|
- console.error('全景图合成失败:', error);
|
|
|
+ return {
|
|
|
+ overallScore: Math.round(overallEfficiency),
|
|
|
+ timeEfficiency: Math.round(timeEfficiency),
|
|
|
+ milestoneCompletionRate: Math.round(milestoneCompletionRate),
|
|
|
+ resourceUtilization: Math.round(resourceUtilization),
|
|
|
+ qualityEfficiency: Math.round(qualityEfficiency),
|
|
|
+ grade: this.getEfficiencyGrade(overallEfficiency)
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- this.isSynthesizingPanoramic = false;
|
|
|
+ /**
|
|
|
+ * 识别效率瓶颈
|
|
|
+ */
|
|
|
+ identifyEfficiencyBottlenecks(projectData: ProjectData): Array<{
|
|
|
+ stage: string;
|
|
|
+ issue: string;
|
|
|
+ impact: number;
|
|
|
+ suggestion: string;
|
|
|
+ priority: 'high' | 'medium' | 'low';
|
|
|
+ }> {
|
|
|
+ const bottlenecks = [];
|
|
|
+
|
|
|
+ // 分析各阶段效率
|
|
|
+ for (const stage of projectData.stages) {
|
|
|
+ const stageEfficiency = this.calculateStageEfficiency(stage);
|
|
|
+
|
|
|
+ if (stageEfficiency < 0.7) { // 效率低于70%
|
|
|
+ bottlenecks.push({
|
|
|
+ stage: stage.name,
|
|
|
+ issue: `${stage.name}阶段效率偏低`,
|
|
|
+ impact: (0.7 - stageEfficiency) * 100,
|
|
|
+ suggestion: this.generateEfficiencySuggestion(stage),
|
|
|
+ priority: stageEfficiency < 0.5 ? 'high' : 'medium'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- let errorMessage = '全景图合成失败';
|
|
|
+ // 分析团队协作问题
|
|
|
+ const collaborationIssues = this.analyzeCollaborationIssues(projectData);
|
|
|
+ bottlenecks.push(...collaborationIssues);
|
|
|
|
|
|
- if (error.code === 'INSUFFICIENT_IMAGES') {
|
|
|
- errorMessage = '图片数量不足,至少需要6张图片';
|
|
|
- } else if (error.code === 'INVALID_FORMAT') {
|
|
|
- errorMessage = '图片格式不支持,请使用JPG或PNG格式';
|
|
|
+ return bottlenecks.sort((a, b) => b.impact - a.impact);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- alert(`❌ ${errorMessage}\n\n请检查后重试。`);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 9.3 评价链接过期
|
|
|
+### 2. 团队绩效分析模型
|
|
|
|
|
|
-```typescript
|
|
|
-// 检查评价链接是否过期
|
|
|
-checkReviewLinkExpiry(linkId: string): boolean {
|
|
|
- const link = this.customerReviewLinks.find(l => l.id === linkId);
|
|
|
-
|
|
|
- if (!link) return true;
|
|
|
+#### 2.1 个人绩效评估
|
|
|
|
|
|
- if (link.status === 'expired') return true;
|
|
|
+```typescript
|
|
|
+class TeamPerformanceAnalyzer {
|
|
|
+ /**
|
|
|
+ * 评估团队成员绩效
|
|
|
+ */
|
|
|
+ evaluateMemberPerformance(
|
|
|
+ memberId: string,
|
|
|
+ projectData: ProjectData,
|
|
|
+ changeLogs: ProjectChange[]
|
|
|
+ ): TeamMemberPerformance {
|
|
|
+
|
|
|
+ // 获取成员相关的数据
|
|
|
+ const memberData = this.extractMemberData(memberId, projectData, changeLogs);
|
|
|
+
|
|
|
+ // 工作量评估
|
|
|
+ const workloadScore = this.evaluateWorkload(memberData);
|
|
|
+
|
|
|
+ // 效率评估
|
|
|
+ const efficiencyScore = this.evaluateEfficiency(memberData);
|
|
|
+
|
|
|
+ // 质量评估
|
|
|
+ const qualityScore = this.evaluateQuality(memberData);
|
|
|
+
|
|
|
+ // 协作评估
|
|
|
+ const collaborationScore = this.evaluateCollaboration(memberData, changeLogs);
|
|
|
+
|
|
|
+ // 创新评估
|
|
|
+ const innovationScore = this.evaluateInnovation(memberData);
|
|
|
+
|
|
|
+ // 综合绩效评分
|
|
|
+ const overallScore = this.calculateOverallScore({
|
|
|
+ workload: workloadScore,
|
|
|
+ efficiency: efficiencyScore,
|
|
|
+ quality: qualityScore,
|
|
|
+ collaboration: collaborationScore,
|
|
|
+ innovation: innovationScore
|
|
|
+ });
|
|
|
|
|
|
- // 检查是否超过有效期
|
|
|
- if (new Date() > link.expiresAt) {
|
|
|
- link.status = 'expired';
|
|
|
- return true;
|
|
|
+ return {
|
|
|
+ memberId,
|
|
|
+ overallScore,
|
|
|
+ workloadScore,
|
|
|
+ efficiencyScore,
|
|
|
+ qualityScore,
|
|
|
+ collaborationScore,
|
|
|
+ innovationScore,
|
|
|
+ strengths: this.identifyStrengths({
|
|
|
+ workload: workloadScore,
|
|
|
+ efficiency: efficiencyScore,
|
|
|
+ quality: qualityScore,
|
|
|
+ collaboration: collaborationScore,
|
|
|
+ innovation: innovationScore
|
|
|
+ }),
|
|
|
+ improvements: this.identifyImprovements({
|
|
|
+ workload: workloadScore,
|
|
|
+ efficiency: efficiencyScore,
|
|
|
+ quality: qualityScore,
|
|
|
+ collaboration: collaborationScore,
|
|
|
+ innovation: innovationScore
|
|
|
+ }),
|
|
|
+ recommendations: this.generateRecommendations(overallScore),
|
|
|
+ ranking: this.calculateRanking(memberId, overallScore, projectData)
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-// 重新生成过期的评价链接
|
|
|
-regenerateReviewLink(oldLinkId: string): void {
|
|
|
- const oldLink = this.customerReviewLinks.find(l => l.id === oldLinkId);
|
|
|
-
|
|
|
- if (!oldLink) return;
|
|
|
-
|
|
|
- // 将旧链接标记为过期
|
|
|
- oldLink.status = 'expired';
|
|
|
+ /**
|
|
|
+ * 分析协作表现
|
|
|
+ */
|
|
|
+ private evaluateCollaboration(memberData: any, changeLogs: ProjectChange[]): number {
|
|
|
+ const collaborationMetrics = {
|
|
|
+ communicationFrequency: 0,
|
|
|
+ responseTime: 0,
|
|
|
+ coordinationScore: 0,
|
|
|
+ knowledgeSharing: 0,
|
|
|
+ conflictResolution: 0
|
|
|
+ };
|
|
|
|
|
|
- // 生成新链接
|
|
|
- this.generateReviewLink();
|
|
|
+ // 分析ProjectChange中的协作行为
|
|
|
+ const memberChanges = changeLogs.filter(log =>
|
|
|
+ log.profile.objectId === memberData.memberId
|
|
|
+ );
|
|
|
|
|
|
- alert('✅ 已重新生成评价链接!\n\n旧链接已失效,请使用新链接。');
|
|
|
-}
|
|
|
-```
|
|
|
+ for (const change of memberChanges) {
|
|
|
+ switch (change.changeType) {
|
|
|
+ case 'communication':
|
|
|
+ collaborationMetrics.communicationFrequency += 1;
|
|
|
+ break;
|
|
|
+ case 'coordination':
|
|
|
+ collaborationMetrics.coordinationScore += 1;
|
|
|
+ break;
|
|
|
+ case 'knowledge_sharing':
|
|
|
+ collaborationMetrics.knowledgeSharing += 1;
|
|
|
+ break;
|
|
|
+ case 'conflict_resolution':
|
|
|
+ collaborationMetrics.conflictResolution += 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
-## 10. 性能优化
|
|
|
+ // 计算响应时间
|
|
|
+ if (change.duration && change.duration < 3600000) { // 1小时内响应
|
|
|
+ collaborationMetrics.responseTime += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-### 10.1 报告生成优化
|
|
|
+ // 归一化评分
|
|
|
+ const maxChanges = Math.max(...Object.values(collaborationMetrics));
|
|
|
+ const normalizedMetrics = Object.fromEntries(
|
|
|
+ Object.entries(collaborationMetrics).map(([key, value]) => [
|
|
|
+ key,
|
|
|
+ maxChanges > 0 ? value / maxChanges : 0
|
|
|
+ ])
|
|
|
+ );
|
|
|
|
|
|
-```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;
|
|
|
+ // 加权平均
|
|
|
+ const weights = {
|
|
|
+ communicationFrequency: 0.25,
|
|
|
+ responseTime: 0.3,
|
|
|
+ coordinationScore: 0.2,
|
|
|
+ knowledgeSharing: 0.15,
|
|
|
+ conflictResolution: 0.1
|
|
|
};
|
|
|
|
|
|
- worker.postMessage({ type: 'generate', data });
|
|
|
- } else {
|
|
|
- // 降级为同步生成
|
|
|
- this.generateReportSync(data);
|
|
|
+ return Object.entries(weights).reduce((score, [metric, weight]) => {
|
|
|
+ return score + normalizedMetrics[metric] * weight;
|
|
|
+ }, 0) * 100;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-### 10.2 图片压缩
|
|
|
+### 3. 客户满意度分析
|
|
|
+
|
|
|
+#### 3.1 满意度趋势分析
|
|
|
|
|
|
```typescript
|
|
|
-// 压缩全景图用于预览
|
|
|
-private compressImageForPreview(file: File): Promise<Blob> {
|
|
|
- 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);
|
|
|
- };
|
|
|
+class SatisfactionAnalyzer {
|
|
|
+ /**
|
|
|
+ * 分析客户满意度趋势
|
|
|
+ */
|
|
|
+ analyzeSatisfactionTrends(
|
|
|
+ projectData: ProjectData,
|
|
|
+ feedbackHistory: ProjectFeedback[]
|
|
|
+ ): SatisfactionAnalysis {
|
|
|
|
|
|
- img.src = e.target?.result as string;
|
|
|
- };
|
|
|
+ // 计算当前满意度
|
|
|
+ const currentSatisfaction = this.calculateCurrentSatisfaction(feedbackHistory);
|
|
|
|
|
|
- reader.readAsDataURL(file);
|
|
|
- });
|
|
|
-}
|
|
|
-```
|
|
|
+ // 分析历史趋势
|
|
|
+ const trendAnalysis = this.analyzeHistoricalTrends(feedbackHistory);
|
|
|
|
|
|
-### 10.3 数据缓存
|
|
|
+ // 识别满意度驱动因素
|
|
|
+ const drivers = this.identifySatisfactionDrivers(feedbackHistory);
|
|
|
|
|
|
-```typescript
|
|
|
-// 缓存复盘报告数据
|
|
|
-private cacheReviewReport(report: any): void {
|
|
|
- try {
|
|
|
- localStorage.setItem(
|
|
|
- `review-report-${this.projectId}`,
|
|
|
- JSON.stringify(report)
|
|
|
- );
|
|
|
- console.log('✅ 复盘报告已缓存');
|
|
|
- } catch (error) {
|
|
|
- console.warn('缓存失败:', error);
|
|
|
+ // 预测满意度
|
|
|
+ const prediction = this.predictSatisfaction(projectData, drivers);
|
|
|
+
|
|
|
+ return {
|
|
|
+ currentScore: currentSatisfaction.overallScore,
|
|
|
+ dimensionScores: currentSatisfaction.dimensionScores,
|
|
|
+ trend: trendAnalysis,
|
|
|
+ keyDrivers: drivers,
|
|
|
+ prediction: prediction,
|
|
|
+ improvementAreas: this.identifyImprovementAreas(currentSatisfaction, drivers),
|
|
|
+ recommendations: this.generateSatisfactionRecommendations(currentSatisfaction, drivers)
|
|
|
+ };
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-// 加载缓存的报告
|
|
|
-private loadCachedReviewReport(): any | null {
|
|
|
- try {
|
|
|
- const cached = localStorage.getItem(`review-report-${this.projectId}`);
|
|
|
- if (cached) {
|
|
|
- return JSON.parse(cached);
|
|
|
+ /**
|
|
|
+ * 识别满意度关键驱动因素
|
|
|
+ */
|
|
|
+ private identifySatisfactionDrivers(feedback: ProjectFeedback[]): Array<{
|
|
|
+ factor: string;
|
|
|
+ impact: number;
|
|
|
+ correlation: number;
|
|
|
+ description: string;
|
|
|
+ }> {
|
|
|
+ const drivers = [];
|
|
|
+
|
|
|
+ // 分析各维度与整体满意度的相关性
|
|
|
+ const dimensions = ['designQuality', 'serviceAttitude', 'deliveryTimeliness', 'valueForMoney'];
|
|
|
+
|
|
|
+ for (const dimension of dimensions) {
|
|
|
+ const correlation = this.calculateCorrelation(
|
|
|
+ feedback.map(f => this.getDimensionScore(f, dimension)),
|
|
|
+ feedback.map(f => f.rating || 0)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (Math.abs(correlation) > 0.5) { // 相关性阈值
|
|
|
+ drivers.push({
|
|
|
+ factor: dimension,
|
|
|
+ impact: Math.abs(correlation) * 100,
|
|
|
+ correlation: correlation,
|
|
|
+ description: this.getDriverDescription(dimension, correlation)
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
- } catch (error) {
|
|
|
- console.warn('加载缓存失败:', error);
|
|
|
+
|
|
|
+ return drivers.sort((a, b) => b.impact - a.impact);
|
|
|
+ }
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-## 11. 测试用例
|
|
|
+### 4. 项目复盘洞察生成
|
|
|
|
|
|
-### 11.1 尾款结算测试
|
|
|
+#### 4.1 AI驱动的洞察生成
|
|
|
|
|
|
```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');
|
|
|
+class RetrospectiveInsightGenerator {
|
|
|
+ /**
|
|
|
+ * 生成项目复盘洞察
|
|
|
+ */
|
|
|
+ async generateInsights(projectData: ProjectData): Promise<RetrospectiveInsights> {
|
|
|
+ // 1. 数据预处理和特征提取
|
|
|
+ const features = await this.extractFeatures(projectData);
|
|
|
|
|
|
- component.initiateAutoSettlement();
|
|
|
+ // 2. 模式识别
|
|
|
+ const patterns = this.identifyPatterns(features);
|
|
|
|
|
|
- expect(window.alert).toHaveBeenCalledWith(jasmine.stringContaining('仅技术人员'));
|
|
|
- });
|
|
|
+ // 3. 异常检测
|
|
|
+ const anomalies = this.detectAnomalies(features);
|
|
|
|
|
|
- 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 }
|
|
|
- ];
|
|
|
+ // 4. 根因分析
|
|
|
+ const rootCauses = this.analyzeRootCauses(patterns, anomalies);
|
|
|
|
|
|
- component.onPaymentReceived({ amount: 75000, method: 'wechat' });
|
|
|
+ // 5. 生成洞察
|
|
|
+ const insights = await this.generateInsightsFromData(patterns, anomalies, rootCauses);
|
|
|
|
|
|
- expect(component.renderLargeImages.every(img => !img.locked)).toBe(true);
|
|
|
- });
|
|
|
-});
|
|
|
-```
|
|
|
+ return {
|
|
|
+ summary: this.generateExecutiveSummary(insights),
|
|
|
+ highlights: insights.highlights,
|
|
|
+ challenges: insights.challenges,
|
|
|
+ rootCauses: insights.rootCauses,
|
|
|
+ lessons: insights.lessons,
|
|
|
+ recommendations: insights.recommendations,
|
|
|
+ actionItems: this.generateActionItems(insights),
|
|
|
+ riskFactors: this.identifyFutureRisks(insights),
|
|
|
+ successFactors: this.identifySuccessFactors(insights)
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
-### 11.2 投诉处理测试
|
|
|
+ /**
|
|
|
+ * 识别项目模式
|
|
|
+ */
|
|
|
+ private identifyPatterns(features: ProjectFeatures): Array<{
|
|
|
+ type: 'positive' | 'negative' | 'neutral';
|
|
|
+ pattern: string;
|
|
|
+ confidence: number;
|
|
|
+ evidence: string[];
|
|
|
+ impact: string;
|
|
|
+ }> {
|
|
|
+ const patterns = [];
|
|
|
+
|
|
|
+ // 识别高效模式
|
|
|
+ if (features.efficiencyPattern === 'consistent_high') {
|
|
|
+ patterns.push({
|
|
|
+ type: 'positive',
|
|
|
+ pattern: '持续高效执行',
|
|
|
+ confidence: 0.85,
|
|
|
+ evidence: [
|
|
|
+ '各阶段效率均高于80%',
|
|
|
+ '里程碑按时完成率100%',
|
|
|
+ '资源利用率稳定在75%-85%'
|
|
|
+ ],
|
|
|
+ impact: '项目执行效率优秀,团队协作顺畅'
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
-```typescript
|
|
|
-describe('Complaint Handling', () => {
|
|
|
- it('should create complaint manually', () => {
|
|
|
- component.roleContext = 'team-leader';
|
|
|
- component.complaintFormData = {
|
|
|
- source: 'manual',
|
|
|
- stage: '渲染',
|
|
|
- reason: '质量问题',
|
|
|
- description: '渲染效果不符合预期',
|
|
|
- severity: 'medium',
|
|
|
- tags: ['设计质量']
|
|
|
- };
|
|
|
+ // 识别协作问题模式
|
|
|
+ if (features.collaborationIssues.length > 2) {
|
|
|
+ patterns.push({
|
|
|
+ type: 'negative',
|
|
|
+ pattern: '跨团队协作障碍',
|
|
|
+ confidence: 0.75,
|
|
|
+ evidence: features.collaborationIssues,
|
|
|
+ impact: '影响项目进度,增加沟通成本'
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- component.submitComplaint();
|
|
|
+ // 识别客户需求变化模式
|
|
|
+ if (features.requirementChanges > 3) {
|
|
|
+ patterns.push({
|
|
|
+ type: 'neutral',
|
|
|
+ pattern: '需求频繁变更',
|
|
|
+ confidence: 0.8,
|
|
|
+ evidence: [
|
|
|
+ `需求变更次数: ${features.requirementChanges}`,
|
|
|
+ '平均变更间隔: 7天',
|
|
|
+ '变更影响范围: 中等'
|
|
|
+ ],
|
|
|
+ impact: '需要加强需求管理和变更控制'
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- expect(component.complaints.length).toBeGreaterThan(0);
|
|
|
- });
|
|
|
+ return patterns;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
|
|
|
- it('should detect keywords and create complaint', () => {
|
|
|
- const message = '我对渲染效果很不满意,要求退款';
|
|
|
+## 实施建议
|
|
|
|
|
|
- component.monitoringKeywords = ['不满意', '退款'];
|
|
|
+### 1. 技术实施方案
|
|
|
|
|
|
- component.onKeywordDetected(message, '不满意');
|
|
|
+#### 1.1 数据迁移策略
|
|
|
+- **渐进式迁移**: 保留现有功能,逐步添加新特性
|
|
|
+- **数据兼容**: 确保现有ProjectPayment和ProjectFeedback数据兼容
|
|
|
+- **向后兼容**: API设计保持向后兼容性
|
|
|
|
|
|
- expect(component.complaints.length).toBeGreaterThan(0);
|
|
|
- expect(component.complaints[0].severity).toBe('high');
|
|
|
- });
|
|
|
-});
|
|
|
-```
|
|
|
+#### 1.2 性能优化
|
|
|
+- **数据缓存**: 复杂分析结果的缓存机制
|
|
|
+- **异步处理**: OCR识别和AI分析的异步处理
|
|
|
+- **分页加载**: 大数据集的分页加载
|
|
|
|
|
|
-### 11.3 复盘报告测试
|
|
|
+### 2. 用户体验优化
|
|
|
|
|
|
-```typescript
|
|
|
-describe('Review Report Generation', () => {
|
|
|
- it('should generate complete review report', () => {
|
|
|
- component.generateReviewReport();
|
|
|
+#### 2.1 引导和帮助
|
|
|
+- **新手引导**: 首次使用时的操作引导
|
|
|
+- **上下文帮助**: 各功能模块的即时帮助
|
|
|
+- **错误恢复**: 操作失败时的友好提示和恢复建议
|
|
|
|
|
|
- // 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);
|
|
|
- });
|
|
|
+#### 2.2 移动端适配
|
|
|
+- **响应式设计**: 完整的移动端适配
|
|
|
+- **触摸优化**: 触摸操作的优化
|
|
|
+- **离线支持**: 关键功能的离线缓存
|
|
|
|
|
|
- it('should export report as Excel', () => {
|
|
|
- component.reviewReport = mockReviewReport;
|
|
|
+### 3. 数据分析应用
|
|
|
|
|
|
- spyOn(document, 'createElement').and.callThrough();
|
|
|
+#### 3.1 实时监控
|
|
|
+- **效率仪表板**: 项目效率的实时监控
|
|
|
+- **质量追踪**: 项目质量的持续追踪
|
|
|
+- **团队状态**: 团队成员状态的实时更新
|
|
|
|
|
|
- component.exportReviewReport('excel');
|
|
|
+#### 3.2 预测分析
|
|
|
+- **风险预警**: 项目风险的早期预警
|
|
|
+- **效率预测**: 基于历史数据的效率预测
|
|
|
+- **资源规划**: 基于分析结果的资源规划建议
|
|
|
|
|
|
- expect(document.createElement).toHaveBeenCalledWith('a');
|
|
|
- });
|
|
|
-});
|
|
|
-```
|
|
|
+## 总结
|
|
|
|
|
|
----
|
|
|
+通过重新设计项目售后归档模块,实现了:
|
|
|
|
|
|
-**文档版本**:v1.0.0
|
|
|
-**创建日期**:2025-10-16
|
|
|
-**最后更新**:2025-10-16
|
|
|
-**维护人**:产品团队
|
|
|
+✅ **完整功能覆盖** - 验收尾款、客户评价、项目复盘三大核心功能
|
|
|
+✅ **智能化升级** - OCR识别、AI复盘、智能评价引导
|
|
|
+✅ **数据驱动决策** - 多维度数据分析和洞察生成
|
|
|
+✅ **用户体验优化** - 响应式设计、实时协作、智能辅助
|
|
|
+✅ **科学绩效评估** - 团队成员效率的量化分析和评估
|
|
|
|
|
|
-**相关文档**:
|
|
|
-- [AFTERCARE-FEATURES-README.md](/home/ryan/workspace/nova/yss-project/src/app/pages/designer/project-detail/AFTERCARE-FEATURES-README.md) - 售后模块功能实现说明
|
|
|
+该方案将为项目管理提供强大的数据支持,帮助团队持续改进和优化项目执行效率。
|