项目-售后归档.md 44 KB

项目管理 - 售后归档阶段 PRD

1. 功能概述

1.1 阶段定位

售后归档阶段是项目管理流程的收尾环节,包含尾款结算、全景图合成、客户评价、投诉处理、项目复盘五大核心模块。该阶段负责完成项目交付、收集反馈、总结经验,为后续项目优化提供数据支撑。

1.2 核心目标

  • 实现自动化尾款结算流程
  • 生成全景图分享链接
  • 收集客户多维度评价
  • 处理客户投诉反馈
  • 生成项目复盘报告

1.3 涉及角色

  • 客服人员:跟进尾款支付、发送评价链接、处理投诉
  • 技术人员:验收交付物、启动自动结算、合成全景图
  • 组长:审核复盘报告、处理投诉、优化流程
  • 财务人员:确认款项到账、核对支付凭证

1.4 五大核心模块

graph TD
    A[后期完成] --> B[尾款结算]
    B --> C[全景图合成]
    C --> D[客户评价]
    D --> E[投诉处理]
    E --> F[项目复盘]

    style B fill:#e8f5e9
    style C fill:#fff3e0
    style D fill:#e3f2fd
    style E fill:#fce4ec
    style F fill:#f3e5f5

2. 尾款结算模块

2.1 功能特点

  • 技术验收触发自动化结算
  • 小程序支付自动监听
  • 支付凭证智能识别
  • 渲染大图自动解锁
  • 客服一键发图

2.2 自动化结算流程

2.2.1 启动自动化结算

// 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 创建结算记录

// 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);
}

结算记录结构

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 通知客服跟进

// 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 小程序支付监听

// 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('💡 提示: 支付凭证识别功能可手动上传截图触发');
}

监听流程

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 支付到账处理

// 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 自动解锁大图

// 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 凭证上传

// 上传支付凭证
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 智能识别

// 调用支付凭证识别服务
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;
    }
  });
}

识别结果结构

interface VoucherRecognitionResult {
  amount: number;                           // 支付金额
  paymentMethod: 'wechat' | 'alipay';      // 支付方式
  transactionId: string;                    // 交易单号
  transactionTime: Date;                    // 交易时间
  confidence: number;                       // 识别置信度 0-1
  merchantName?: string;                    // 商户名称
  remarks?: string;                         // 备注信息
}

2.5 一键发图功能

// 客服一键发送渲染大图
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 启动合成

// 开始全景图合成
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 文件上传与合成

// 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 完成合成

// 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)}`);
}

全景图数据结构

interface PanoramicSynthesis {
  id: string;
  name: string;
  createdAt: Date;
  spaces: Array<{
    id: string;
    name: string;              // 空间名称:客厅-角度1
    imageUrl: string;
    angle: number;             // 拍摄角度
  }>;
  previewUrl: string;          // 预览链接
  downloadUrl: string;         // 下载链接
  shareLink: string;           // 分享链接
  fileSize: number;
  status: 'processing' | 'completed' | 'failed';
}

3.3 自动生成分享链接

// project-detail.ts lines 4330-4360
private generatePanoramicShareLink(synthesis: PanoramicSynthesis): void {
  // 生成唯一分享链接
  const linkId = btoa(`panoramic-${synthesis.id}-${Date.now()}`);
  const shareLink = `https://vr.example.com/view/${linkId}`;

  synthesis.shareLink = shareLink;

  console.log('🔗 已生成分享链接:', shareLink);

  // 自动复制到剪贴板
  this.copyToClipboard(shareLink);

  // 通知客服发送给客户
  this.notifyCustomerServiceForPanoramicShare(synthesis);

  alert(`✅ 分享链接已生成并复制到剪贴板!\n\n${shareLink}\n\n客服已收到通知,可发送给客户。`);
}

private notifyCustomerServiceForPanoramicShare(synthesis: PanoramicSynthesis): void {
  const notification = {
    type: 'panoramic-ready',
    projectId: this.projectId,
    projectName: this.project?.name || '未命名项目',
    shareLink: synthesis.shareLink,
    spaceCount: synthesis.spaces.length,
    message: `项目"${this.project?.name}"的全景图已合成完成,请发送给客户查看`
  };

  console.log('📧 已通知客服发送全景图:', notification);
}

分享链接特点

  • 唯一性标识
  • 有效期控制(可选)
  • 访问统计
  • VR模式支持

4. 客户评价模块

4.1 功能特点

  • 多维度评分系统
  • 评价链接自动生成
  • 30天有效期
  • 数据统计分析

4.2 评价链接生成

4.2.1 生成评价令牌

// 生成客户评价链接
generateReviewLink(): void {
  console.log('📋 生成客户评价链接');

  // 生成唯一评价令牌
  const token = this.generateUniqueToken();

  // 创建评价链接记录
  const reviewLink: CustomerReviewLink = {
    id: `review-link-${Date.now()}`,
    projectId: this.projectId,
    token: token,
    link: `https://review.example.com/${token}`,
    createdAt: new Date(),
    expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30天后过期
    status: 'active',
    submittedAt: undefined
  };

  this.customerReviewLink = reviewLink;

  // 复制到剪贴板
  this.copyToClipboard(reviewLink.link);

  // 通知客服
  this.notifyCustomerServiceForReview(reviewLink);

  console.log('✅ 评价链接已生成:', reviewLink);

  alert(`✅ 评价链接已生成并复制到剪贴板!\n\n${reviewLink.link}\n\n有效期: 30天\n客服已收到通知,可发送给客户。`);
}

评价链接结构

interface CustomerReviewLink {
  id: string;
  projectId: string;
  token: string;                // 唯一令牌
  link: string;                 // 完整链接
  createdAt: Date;
  expiresAt: Date;              // 过期时间
  status: 'active' | 'submitted' | 'expired';
  submittedAt?: Date;
}

4.2.2 生成唯一令牌

private generateUniqueToken(): string {
  const timestamp = Date.now().toString(36);
  const randomStr = Math.random().toString(36).substring(2, 15);
  const projectIdHash = btoa(this.projectId).substring(0, 8);

  return `${timestamp}-${randomStr}-${projectIdHash}`;
}

4.3 评价数据结构

interface CustomerReview {
  id: string;
  projectId: string;
  submittedAt: Date;

  // 多维度评分 (1-5星)
  ratings: {
    overall: number;            // 整体满意度
    timeliness: number;         // 及时性
    quality: number;            // 设计质量
    communication: number;      // 沟通效率
    professionalism: number;    // 专业程度
  };

  // 文字评价
  comments: {
    strengths: string;          // 优点
    improvements: string;       // 改进建议
    additional: string;         // 其他意见
  };

  // 推荐意愿
  wouldRecommend: boolean;

  // 附加信息
  contact?: string;
  permitPublish: boolean;       // 是否允许公开
}

4.4 评价提交处理

// 处理客户提交的评价
onReviewSubmitted(reviewData: CustomerReview): void {
  console.log('📝 收到客户评价:', reviewData);

  // 保存评价数据
  this.customerReviews.push(reviewData);

  // 更新评价链接状态
  if (this.customerReviewLink) {
    this.customerReviewLink.status = 'submitted';
    this.customerReviewLink.submittedAt = new Date();
  }

  // 计算平均分
  this.calculateAverageRatings();

  // 通知相关人员
  this.notifyReviewReceived(reviewData);

  console.log('✅ 客户评价已保存');

  alert('✅ 感谢客户的宝贵评价!\n\n评价数据已保存并通知相关人员。');
}

4.5 评价数据分析

// 计算平均评分
private calculateAverageRatings(): void {
  if (this.customerReviews.length === 0) {
    this.averageRatings = {
      overall: 0,
      timeliness: 0,
      quality: 0,
      communication: 0,
      professionalism: 0
    };
    return;
  }

  const sum = this.customerReviews.reduce((acc, review) => ({
    overall: acc.overall + review.ratings.overall,
    timeliness: acc.timeliness + review.ratings.timeliness,
    quality: acc.quality + review.ratings.quality,
    communication: acc.communication + review.ratings.communication,
    professionalism: acc.professionalism + review.ratings.professionalism
  }), {
    overall: 0,
    timeliness: 0,
    quality: 0,
    communication: 0,
    professionalism: 0
  });

  const count = this.customerReviews.length;

  this.averageRatings = {
    overall: Math.round((sum.overall / count) * 10) / 10,
    timeliness: Math.round((sum.timeliness / count) * 10) / 10,
    quality: Math.round((sum.quality / count) * 10) / 10,
    communication: Math.round((sum.communication / count) * 10) / 10,
    professionalism: Math.round((sum.professionalism / count) * 10) / 10
  };

  console.log('📊 平均评分已更新:', this.averageRatings);
}

5. 投诉处理模块

5.1 功能特点

  • 人工创建投诉
  • 关键词自动抓取
  • 智能标注问题类型
  • 处理进度跟踪

5.2 人工创建投诉

5.2.1 创建投诉记录

// 人工创建投诉记录
createComplaintManually(): void {
  console.log('📝 人工创建投诉记录');

  // 验证权限
  if (!this.isTeamLeaderView() && !this.isCustomerServiceView()) {
    alert('⚠️ 仅组长和客服可以创建投诉记录');
    return;
  }

  // 打开投诉创建表单
  this.showComplaintForm = true;
  this.complaintFormData = {
    source: 'manual',
    stage: '',
    reason: '',
    description: '',
    severity: 'medium',
    tags: []
  };
}

5.2.2 提交投诉

// 提交投诉记录
submitComplaint(): void {
  if (!this.complaintFormData.reason || !this.complaintFormData.description) {
    alert('请填写投诉原因和详细描述');
    return;
  }

  const complaint: ComplaintRecord = {
    id: `complaint-${Date.now()}`,
    projectId: this.projectId,
    source: this.complaintFormData.source,
    stage: this.complaintFormData.stage || '未指定',
    reason: this.complaintFormData.reason,
    description: this.complaintFormData.description,
    severity: this.complaintFormData.severity,
    tags: this.complaintFormData.tags,
    status: '待处理',
    createdAt: new Date(),
    createdBy: this.getCurrentUserName(),
    assignedTo: undefined,
    resolvedAt: undefined,
    resolution: undefined
  };

  // 添加到投诉列表
  this.complaints.push(complaint);

  // 通知相关处理人员
  this.notifyComplaintHandler(complaint);

  // 关闭表单
  this.showComplaintForm = false;

  console.log('✅ 投诉记录已创建:', complaint);

  alert('✅ 投诉记录已创建!\n\n相关人员已收到通知。');
}

投诉数据结构

interface ComplaintRecord {
  id: string;
  projectId: string;
  source: 'manual' | 'keyword-detection';    // 来源
  stage: string;                              // 投诉环节
  reason: string;                             // 投诉原因
  description: string;                        // 详细描述
  severity: 'low' | 'medium' | 'high';       // 严重程度
  tags: string[];                             // 问题标签
  status: '待处理' | '处理中' | '已解决' | '已关闭';
  createdAt: Date;
  createdBy: string;
  assignedTo?: string;                        // 分配给
  resolvedAt?: Date;
  resolution?: string;                        // 解决方案
  attachments?: Array<{
    id: string;
    name: string;
    url: string;
  }>;
}

5.3 关键词自动监控

5.3.1 设置关键词监测

// 启动关键词监测
setupKeywordMonitoring(): void {
  console.log('🔍 设置关键词监测');

  // 打开监控设置面板
  this.showKeywordMonitoringSettings = true;

  // 初始化默认关键词
  if (this.monitoringKeywords.length === 0) {
    this.monitoringKeywords = [
      '不满意',
      '投诉',
      '退款',
      '差评',
      '质量问题',
      '延期',
      '态度差'
    ];
  }

  console.log('📋 当前监控关键词:', this.monitoringKeywords);
}

5.3.2 关键词检测

// 检测消息中的关键词
private detectKeywords(message: string): string[] {
  const detectedKeywords: string[] = [];

  this.monitoringKeywords.forEach(keyword => {
    if (message.includes(keyword)) {
      detectedKeywords.push(keyword);
    }
  });

  return detectedKeywords;
}

5.3.3 自动创建投诉

// 检测到关键词后自动创建投诉
onKeywordDetected(message: string, keyword: string): void {
  console.log(`🚨 检测到关键词: ${keyword}`);

  // 智能分析投诉严重程度
  const severity = this.assessComplaintSeverity(keyword);

  // 智能识别投诉环节
  const stage = this.identifyComplaintStage(message);

  // 智能标注问题类型
  const tags = this.generateComplaintTags(message, keyword);

  // 自动创建投诉记录
  const complaint: ComplaintRecord = {
    id: `complaint-auto-${Date.now()}`,
    projectId: this.projectId,
    source: 'keyword-detection',
    stage: stage,
    reason: `检测到关键词: ${keyword}`,
    description: message,
    severity: severity,
    tags: tags,
    status: '待处理',
    createdAt: new Date(),
    createdBy: '系统自动',
    assignedTo: undefined,
    resolvedAt: undefined,
    resolution: undefined
  };

  this.complaints.push(complaint);

  // 立即通知处理人员
  this.notifyUrgentComplaint(complaint);

  console.log('✅ 已自动创建投诉记录:', complaint);

  alert(`🚨 检测到客户投诉关键词: ${keyword}\n\n已自动创建投诉记录并通知相关人员。`);
}

智能分析方法

// 评估投诉严重程度
private assessComplaintSeverity(keyword: string): 'low' | 'medium' | 'high' {
  const highSeverityKeywords = ['退款', '投诉', '差评'];
  const mediumSeverityKeywords = ['不满意', '质量问题', '延期'];

  if (highSeverityKeywords.includes(keyword)) return 'high';
  if (mediumSeverityKeywords.includes(keyword)) return 'medium';
  return 'low';
}

// 识别投诉环节
private identifyComplaintStage(message: string): string {
  if (message.includes('需求') || message.includes('沟通')) return '需求沟通';
  if (message.includes('方案') || message.includes('设计')) return '方案确认';
  if (message.includes('建模') || message.includes('模型')) return '建模';
  if (message.includes('软装') || message.includes('家具')) return '软装';
  if (message.includes('渲染') || message.includes('效果图')) return '渲染';
  if (message.includes('交付') || message.includes('延期')) return '交付';
  return '未识别';
}

// 生成问题标签
private generateComplaintTags(message: string, keyword: string): string[] {
  const tags: string[] = [];

  // 根据消息内容添加标签
  if (message.includes('需求') || message.includes('理解')) tags.push('需求理解');
  if (message.includes('质量') || message.includes('效果')) tags.push('设计质量');
  if (message.includes('延期') || message.includes('时间')) tags.push('交付延期');
  if (message.includes('态度') || message.includes('服务')) tags.push('服务态度');
  if (message.includes('价格') || message.includes('费用')) tags.push('价格问题');

  // 添加关键词作为标签
  tags.push(keyword);

  return [...new Set(tags)]; // 去重
}

5.4 投诉处理流程

// 处理投诉
handleComplaint(complaintId: string, resolution: string): void {
  const complaint = this.complaints.find(c => c.id === complaintId);
  if (!complaint) return;

  complaint.status = '已解决';
  complaint.resolvedAt = new Date();
  complaint.resolution = resolution;

  // 通知客户和相关人员
  this.notifyComplaintResolved(complaint);

  console.log('✅ 投诉已处理:', complaint);

  alert('✅ 投诉处理完成!\n\n已通知客户和相关人员。');
}

6. 项目复盘模块

6.1 功能特点

  • 三大核心板块(SOP执行数据、经验复盘、优化建议)
  • 数据可视化展示
  • 自动生成复盘报告
  • 导出为PDF/Excel

6.2 SOP执行数据

6.2.1 数据收集

// 收集SOP执行数据
collectSOPExecutionData(): any {
  return {
    requirementCommunications: this.countRequirementCommunications(),
    revisionCount: this.countRevisions(),
    deliveryCycleCompliance: this.checkDeliveryCycleCompliance(),
    customerSatisfaction: this.getCustomerSatisfactionScore(),
    stageDetails: this.getStageExecutionDetails()
  };
}

6.2.2 阶段执行详情

// 获取各阶段执行详情
private getStageExecutionDetails(): Array<{
  stage: string;
  plannedDuration: number;
  actualDuration: number;
  status: 'on-time' | 'delayed' | 'ahead';
  score: number;
}> {
  return [
    {
      stage: '需求沟通',
      plannedDuration: 2,
      actualDuration: 2,
      status: 'on-time',
      score: 95
    },
    {
      stage: '方案确认',
      plannedDuration: 3,
      actualDuration: 4,
      status: 'delayed',
      score: 85
    },
    {
      stage: '建模',
      plannedDuration: 5,
      actualDuration: 4,
      status: 'ahead',
      score: 92
    },
    {
      stage: '软装',
      plannedDuration: 3,
      actualDuration: 3,
      status: 'on-time',
      score: 90
    },
    {
      stage: '渲染',
      plannedDuration: 4,
      actualDuration: 5,
      status: 'delayed',
      score: 88
    }
  ];
}

6.3 经验复盘

6.3.1 自动提取信息

// 提取经验复盘数据
extractExperienceSummary(): any {
  return {
    customerNeeds: this.extractCustomerNeeds(),
    customerConcerns: this.extractCustomerConcerns(),
    complaintPoints: this.extractComplaintPoints(),
    projectHighlights: this.extractProjectHighlights(),
    keyConversations: this.extractKeyConversations()
  };
}

6.3.2 提取客户需求

private extractCustomerNeeds(): string[] {
  // 从需求沟通记录中提取
  return [
    '客户希望整体风格偏现代简约',
    '客户重视收纳空间的设计',
    '客户要求使用环保材料',
    '客户希望采光效果良好'
  ];
}

6.4 优化建议

6.4.1 生成优化建议

// 生成优化建议
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;
}

优化建议结构

interface OptimizationSuggestion {
  priority: 'high' | 'medium' | 'low';
  priorityText: string;
  category: string;                     // 类别
  problem: string;                      // 问题描述
  dataSupport: string;                  // 数据支撑
  solution: string;                     // 解决方案
  actionPlan: string[];                 // 行动计划
  expectedImprovement: string;          // 预期提升
  referenceCase?: string;               // 参考案例
  accepted: boolean;                    // 是否已采纳
  acceptedAt?: Date;
}

6.5 复盘报告生成

6.5.1 生成报告

// 生成完整复盘报告
generateReviewReport(): void {
  console.log('📊 生成项目复盘报告');

  this.isGeneratingReview = true;

  // 模拟生成进度
  let progress = 0;
  const interval = setInterval(() => {
    progress += 20;
    if (progress >= 100) {
      clearInterval(interval);
      this.completeReviewReportGeneration();
    }
  }, 500);
}

private completeReviewReportGeneration(): void {
  // 收集所有数据
  const reportData = {
    projectInfo: {
      name: this.project?.name || '未命名项目',
      id: this.projectId,
      startDate: this.project?.createdAt,
      endDate: new Date()
    },
    sopData: this.collectSOPExecutionData(),
    experience: this.extractExperienceSummary(),
    suggestions: this.generateOptimizationSuggestions(),
    statistics: {
      overallScore: this.calculateOverallScore(),
      strengths: this.getProjectStrengths(),
      weaknesses: this.getProjectWeaknesses()
    }
  };

  // 保存报告
  this.reviewReport = reportData;
  this.isGeneratingReview = false;

  console.log('✅ 复盘报告生成完成:', reportData);

  alert('✅ 项目复盘报告已生成!\n\n您可以查看详情或导出报告。');
}

6.5.2 导出报告

// 导出复盘报告
exportReviewReport(format: 'pdf' | 'excel'): void {
  if (!this.reviewReport) {
    alert('请先生成复盘报告');
    return;
  }

  console.log(`📤 导出复盘报告 (${format})`);

  if (format === 'excel') {
    this.exportAsExcel(this.reviewReport);
  } else {
    this.exportAsPDF(this.reviewReport);
  }
}

private exportAsExcel(data: any): void {
  // 转换为CSV格式
  let csvContent = '\uFEFF'; // UTF-8 BOM

  // 项目概况
  csvContent += '=== 项目概况 ===\n';
  csvContent += `项目名称,${data.projectInfo.name}\n`;
  csvContent += `项目ID,${data.projectInfo.id}\n`;
  csvContent += `总耗时,${this.calculateProjectDuration()}天\n\n`;

  // SOP执行数据
  csvContent += '=== SOP执行数据 ===\n';
  csvContent += '阶段,计划时长,实际时长,状态,评分\n';
  data.sopData.stageDetails.forEach((stage: any) => {
    csvContent += `${stage.stage},${stage.plannedDuration},${stage.actualDuration},${stage.status},${stage.score}\n`;
  });

  // 优化建议
  csvContent += '\n=== 优化建议 ===\n';
  csvContent += '优先级,类别,问题,建议,预期提升\n';
  data.suggestions.forEach((s: any) => {
    csvContent += `${s.priorityText},${s.category},"${s.problem}","${s.solution}",${s.expectedImprovement}\n`;
  });

  // 创建下载
  const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
  const link = document.createElement('a');
  const url = URL.createObjectURL(blob);

  link.href = url;
  link.download = `项目复盘报告_${data.projectInfo.name}_${this.formatDate(new Date())}.csv`;
  link.click();

  URL.revokeObjectURL(url);

  console.log('✅ 报告已导出为Excel');
  alert('✅ 报告已导出!\n\n文件已下载到您的下载文件夹。');
}

7. 权限控制

7.1 角色权限矩阵

操作 客服 设计师 组长 技术 财务
查看售后板块
启动自动结算
上传支付凭证
发送图片给客户
合成全景图
生成评价链接
创建投诉记录
处理投诉
生成复盘报告
导出复盘报告

7.2 权限检查实现

// 检查尾款结算权限
canInitiateSettlement(): boolean {
  return this.isTechnicalView();
}

// 检查全景图合成权限
canSynthesizePanoramic(): boolean {
  return this.isTechnicalView();
}

// 检查投诉处理权限
canHandleComplaints(): boolean {
  return this.isTeamLeaderView() || this.isCustomerServiceView();
}

// 检查复盘报告权限
canGenerateReviewReport(): boolean {
  return this.isTeamLeaderView() || this.isTechnicalView();
}

8. 数据流转

8.1 售后流程总览

sequenceDiagram
    participant Tech as 技术
    participant System as 系统
    participant Payment as 支付网关
    participant CS as 客服
    participant Customer as 客户

    Tech->>System: 启动自动结算
    System->>Payment: 激活支付监听
    System->>CS: 通知跟进尾款
    CS->>Customer: 发送支付请求
    Customer->>Payment: 完成支付
    Payment->>System: 支付通知
    System->>System: 解锁渲染大图
    System->>CS: 通知发送大图
    CS->>Customer: 发送渲染大图
    System->>CS: 生成评价链接
    CS->>Customer: 发送评价链接
    Customer->>System: 提交评价
    System->>Tech: 生成复盘报告

8.2 数据同步机制

// 售后数据同步到项目
private syncAfterCareDataToProject(): void {
  if (!this.project) return;

  this.project.afterCare = {
    settlement: this.settlementRecord,
    panoramic: this.panoramicSynthesisHistory,
    reviews: this.customerReviews,
    complaints: this.complaints,
    reviewReport: this.reviewReport
  };

  // 同步到服务器
  this.projectService.updateProject(this.project).subscribe({
    next: (result) => {
      console.log('✅ 售后数据已同步到项目');
    },
    error: (error) => {
      console.error('❌ 售后数据同步失败:', error);
    }
  });
}

9. 异常处理

9.1 支付监听失败

// 支付监听连接失败处理
private handlePaymentMonitoringError(error: any): void {
  console.error('支付监听连接失败:', error);

  // 降级为手动模式
  this.miniprogramPaymentStatus = 'error';

  alert(`⚠️ 支付自动监听失败\n\n请使用"上传支付凭证"功能手动确认支付。`);

  // 显示手动上传入口
  this.showManualPaymentVoucherUpload = true;
}

9.2 全景图合成失败

// 全景图合成失败处理
private handlePanoramicSynthesisError(error: any): void {
  console.error('全景图合成失败:', error);

  this.isSynthesizingPanoramic = false;

  let errorMessage = '全景图合成失败';

  if (error.code === 'INSUFFICIENT_IMAGES') {
    errorMessage = '图片数量不足,至少需要6张图片';
  } else if (error.code === 'INVALID_FORMAT') {
    errorMessage = '图片格式不支持,请使用JPG或PNG格式';
  }

  alert(`❌ ${errorMessage}\n\n请检查后重试。`);
}

9.3 评价链接过期

// 检查评价链接是否过期
checkReviewLinkExpiry(linkId: string): boolean {
  const link = this.customerReviewLinks.find(l => l.id === linkId);

  if (!link) return true;

  if (link.status === 'expired') return true;

  // 检查是否超过有效期
  if (new Date() > link.expiresAt) {
    link.status = 'expired';
    return true;
  }

  return false;
}

// 重新生成过期的评价链接
regenerateReviewLink(oldLinkId: string): void {
  const oldLink = this.customerReviewLinks.find(l => l.id === oldLinkId);

  if (!oldLink) return;

  // 将旧链接标记为过期
  oldLink.status = 'expired';

  // 生成新链接
  this.generateReviewLink();

  alert('✅ 已重新生成评价链接!\n\n旧链接已失效,请使用新链接。');
}

10. 性能优化

10.1 报告生成优化

// 使用Worker生成大型报告
private generateReportWithWorker(data: any): void {
  if (typeof Worker !== 'undefined') {
    const worker = new Worker(new URL('./report-generator.worker', import.meta.url));

    worker.onmessage = ({ data }) => {
      console.log('报告生成完成:', data);
      this.reviewReport = data.report;
      this.isGeneratingReview = false;
    };

    worker.postMessage({ type: 'generate', data });
  } else {
    // 降级为同步生成
    this.generateReportSync(data);
  }
}

10.2 图片压缩

// 压缩全景图用于预览
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);
      };

      img.src = e.target?.result as string;
    };

    reader.readAsDataURL(file);
  });
}

10.3 数据缓存

// 缓存复盘报告数据
private cacheReviewReport(report: any): void {
  try {
    localStorage.setItem(
      `review-report-${this.projectId}`,
      JSON.stringify(report)
    );
    console.log('✅ 复盘报告已缓存');
  } catch (error) {
    console.warn('缓存失败:', error);
  }
}

// 加载缓存的报告
private loadCachedReviewReport(): any | null {
  try {
    const cached = localStorage.getItem(`review-report-${this.projectId}`);
    if (cached) {
      return JSON.parse(cached);
    }
  } catch (error) {
    console.warn('加载缓存失败:', error);
  }
  return null;
}

11. 测试用例

11.1 尾款结算测试

describe('Final Payment Settlement', () => {
  it('should initiate auto settlement by technical user', () => {
    component.roleContext = 'technical';
    spyOn(component, 'isAllDeliveryCompleted').and.returnValue(true);

    component.initiateAutoSettlement();

    expect(component.miniprogramPaymentStatus).toBe('active');
    expect(component.settlementRecord).toBeDefined();
  });

  it('should reject non-technical users', () => {
    component.roleContext = 'designer';
    spyOn(window, 'alert');

    component.initiateAutoSettlement();

    expect(window.alert).toHaveBeenCalledWith(jasmine.stringContaining('仅技术人员'));
  });

  it('should unlock images after payment received', () => {
    component.renderLargeImages = [
      { id: '1', name: 'img1.jpg', url: 'blob:1', locked: true },
      { id: '2', name: 'img2.jpg', url: 'blob:2', locked: true }
    ];

    component.onPaymentReceived({ amount: 75000, method: 'wechat' });

    expect(component.renderLargeImages.every(img => !img.locked)).toBe(true);
  });
});

11.2 投诉处理测试

describe('Complaint Handling', () => {
  it('should create complaint manually', () => {
    component.roleContext = 'team-leader';
    component.complaintFormData = {
      source: 'manual',
      stage: '渲染',
      reason: '质量问题',
      description: '渲染效果不符合预期',
      severity: 'medium',
      tags: ['设计质量']
    };

    component.submitComplaint();

    expect(component.complaints.length).toBeGreaterThan(0);
  });

  it('should detect keywords and create complaint', () => {
    const message = '我对渲染效果很不满意,要求退款';

    component.monitoringKeywords = ['不满意', '退款'];

    component.onKeywordDetected(message, '不满意');

    expect(component.complaints.length).toBeGreaterThan(0);
    expect(component.complaints[0].severity).toBe('high');
  });
});

11.3 复盘报告测试

describe('Review Report Generation', () => {
  it('should generate complete review report', () => {
    component.generateReviewReport();

    // Wait for generation
    setTimeout(() => {
      expect(component.reviewReport).toBeDefined();
      expect(component.reviewReport.sopData).toBeDefined();
      expect(component.reviewReport.experience).toBeDefined();
      expect(component.reviewReport.suggestions.length).toBeGreaterThan(0);
    }, 3000);
  });

  it('should export report as Excel', () => {
    component.reviewReport = mockReviewReport;

    spyOn(document, 'createElement').and.callThrough();

    component.exportReviewReport('excel');

    expect(document.createElement).toHaveBeenCalledWith('a');
  });
});

文档版本:v1.0.0 创建日期:2025-10-16 最后更新:2025-10-16 维护人:产品团队

相关文档