PROJECT-TO-CASE-AUTO-CREATE.md 12 KB

项目自动转案例功能实现文档

功能概述

当项目进入"售后归档"阶段时,系统自动将项目数据转换并添加到Case(案例库)表中,实现项目到案例的无缝转换。

实现时间

2025-10-29

核心功能

1. 自动触发机制

  • 触发条件: 项目阶段更新为"售后归档"
  • 触发位置: ProjectService.updateProjectStage()方法
  • 执行逻辑: 异步创建案例,不阻塞主流程

2. 数据映射策略

Project表 + Product表 → Case表的完整映射:

系统字段

company: Project.company           // 企业指针
isDeleted: false                   // 软删除标记(默认false)

基础信息

name: Project.title                // 案例名称来自项目标题

关联关系

project: Project.objectId          // 关联原项目
designer: Project.assignee || Project.designer  // 设计师
team: Project.team || Project.department        // 团队
address: Project.address           // 客户地址(可选)

媒体资源

coverImage: images[0]              // 第一张图片作为封面
images: [
  ...Project.data.images,          // 项目图片
  ...Product.attachments[].url     // 产品附件图片
]

数值信息

totalPrice: Project.totalAmount || Project.orderAmount || Project.data.budget.total

时间节点

completionDate: new Date()         // 当前时间作为完工时间

标签/分类

tag: [
  ...Project.tags,                 // 项目标签
  Project.style                    // 风格
]

状态标记

isPublished: false                 // 默认不发布,需要审核
isExcellent: false                 // 默认非优秀案例
index: 0                          // 默认排序

info对象(必填分类信息)

info: {
  area: Project.data.area || Project.area || 100,
  projectType: Project.data.projectType || "家装",
  roomType: Project.data.roomType || "三居室",
  spaceType: Project.data.spaceType || "平层",
  renderingLevel: Project.data.renderingLevel || "中端"
}

data对象(扩展数据)

data: {
  // 装修规格信息
  renovationSpec: {
    roomAreas: Project.data.specMap.roomAreas,
    renovationType: Project.data.renovationType,
    renovationScope: Project.data.specMap.renovationScope,
    constructionItems: Project.data.specMap.constructionItems
  },
  
  // 产品详细信息
  productsDetail: Product[].map(p => ({
    productId: p.id,
    productName: p.name,
    category: p.category,
    brand: p.brand,
    quantity: p.quantity,
    unitPrice: p.price,
    totalPrice: p.quantity * p.price,
    spaceArea: p.spaceArea,
    specifications: p.specifications
  })),
  
  // 预算信息
  budget: Project.data.budget,
  
  // 时间线信息
  timeline: Project.data.timeline,
  
  // 设计亮点
  highlights: Project.data.highlights,
  
  // 设计特色
  features: Project.data.features,
  
  // 设计挑战
  challenges: Project.data.challenges,
  
  // 材料信息
  materials: Project.data.materials,
  
  // 客户信息
  clientInfo: Project.data.clientInfo,
  
  // 图片详细信息
  imagesDetail: {
    beforeRenovation: [],
    afterRenovation: Product.attachments[],
    videos: Product.attachments[type='video'],
    panoramas: Product.attachments[type='panorama']
  }
}

代码实现

1. 新建服务文件

文件: yss-project/src/app/services/project-to-case.service.ts

核心方法:

  • onProjectStageChanged(projectId, newStage) - 监听阶段变化
  • createCaseFromProject(projectId) - 从项目创建案例
  • checkCaseExists(projectId) - 检查是否已创建过案例

2. 集成到ProjectService

文件: yss-project/src/app/services/project.service.ts

修改内容:

导入服务

import { ProjectToCaseService } from './project-to-case.service';

注入依赖

constructor(private projectToCaseService: ProjectToCaseService) {}

在updateProjectStage中调用

.then(savedProject => {
  console.log(`✅ 项目阶段已更新并保存到数据库: ${stage}`);
  
  // 🎯 如果进入"售后归档"阶段,自动创建案例
  if (stage === '售后归档') {
    this.projectToCaseService.onProjectStageChanged(projectId, stage)
      .then(() => {
        console.log('✅ 项目已自动添加到案例库');
      })
      .catch(err => {
        console.error('❌ 自动创建案例失败:', err);
      });
  }
  
  observer.next(project);
  observer.complete();
})

完整流程

用户操作:项目推进到"售后归档"阶段
   ↓
ProjectService.updateProjectStage('售后归档')
   ↓
保存阶段到Parse数据库
   - Project.currentStage = '售后归档'
   - Project.stage = '售后归档'
   - Project.status = '已完成'
   ↓
触发自动创建案例逻辑
   ↓
ProjectToCaseService.onProjectStageChanged()
   ↓
检查是否已创建过案例
   - Query Case表 where project = projectId
   ↓
如果未创建,执行创建
   ↓
获取项目数据
   - Query Project表 include(assignee, designer, team, customer, address)
   ↓
获取产品数据
   - Query Product表 where project = projectId include(attachments)
   ↓
提取并映射数据
   - 基础信息
   - 关联关系
   - 媒体资源(图片、视频)
   - 数值信息(价格)
   - 标签分类
   - info对象(必填字段)
   - data对象(扩展字段)
   ↓
创建Case对象并保存
   - const caseObj = new CaseClass()
   - caseObj.set(...)
   - await caseObj.save()
   ↓
控制台输出
   ✅ 案例创建成功: caseId
   ✅ 项目已自动添加到案例库
   ↓
案例库自动显示新案例
   - 访问 @case-library/ 即可看到

Parse数据库查询

查询某个项目的关联案例

const query = new Parse.Query('Case');
query.equalTo('company', companyPointer);
query.equalTo('project', projectPointer);
query.notEqualTo('isDeleted', true);
const cases = await query.find();

查询所有已发布案例

const query = new Parse.Query('Case');
query.equalTo('company', companyPointer);
query.equalTo('isPublished', true);
query.notEqualTo('isDeleted', true);
query.descending('createdAt');
const cases = await query.find();

按风格筛选案例

const query = new Parse.Query('Case');
query.equalTo('company', companyPointer);
query.containsAll('tag', ['现代简约', '北欧风']);
query.notEqualTo('isDeleted', true);
const cases = await query.find();

案例库显示

案例库页面(@case-library/)已经实现了完整的显示逻辑:

1. 案例卡片显示

  • 封面图片
  • 案例名称
  • 设计师
  • 项目类型
  • 面积
  • 价格
  • 标签

2. 筛选功能

  • 项目类型(工装/家装)
  • 空间类型(平层/复式/别墅/自建房)
  • 渲染水平(高端/中端/低端)
  • 面积范围
  • 关键词搜索

3. 详情面板

  • 完整项目信息
  • 所有图片展示
  • 产品列表
  • 预算明细
  • 时间线
  • 客户评价

数据完整性保证

必填字段验证

创建案例时会验证以下必填字段:

✅ name: 案例名称(来自项目标题)
✅ project: 关联项目指针
✅ designer: 设计师指针
✅ team: 团队指针
✅ coverImage: 封面图片
✅ images: 图片数组(至少1张)
✅ totalPrice: 总价
✅ completionDate: 完工时间
✅ tag: 标签数组(至少1个)
✅ info.area: 面积
✅ info.projectType: 项目类型
✅ info.roomType: 户型
✅ info.spaceType: 空间类型
✅ info.renderingLevel: 渲染水平

缺失字段处理

如果某些字段缺失,使用默认值:

area: projectData.area || project.get('area') || 100
projectType: projectData.projectType || '家装'
roomType: projectData.roomType || '三居室'
spaceType: projectData.spaceType || '平层'
renderingLevel: projectData.renderingLevel || '中端'

错误处理

1. 项目不存在

if (!project) {
  throw new Error('项目不存在');
}

2. 缺少设计师

if (!designer) {
  throw new Error('项目缺少设计师信息');
}

3. 缺少团队

if (!team) {
  throw new Error('项目缺少团队信息');
}

4. 重复创建检查

const existingCase = await this.checkCaseExists(projectId);
if (existingCase) {
  console.log('项目已有关联案例,跳过创建');
  return;
}

5. 异步错误处理

this.projectToCaseService.onProjectStageChanged(projectId, stage)
  .catch(err => {
    console.error('❌ 自动创建案例失败:', err);
    // 不阻塞主流程,仅记录错误
  });

测试场景

场景1:正常流程

步骤:

  1. 创建一个新项目
  2. 完成各个阶段(订单分配 → 需求沟通 → ... → 售后归档)
  3. 推进到"售后归档"阶段

预期结果:

  • ✅ 控制台显示:✅ 项目阶段已更新并保存到数据库: 售后归档
  • ✅ 控制台显示:📦 项目 xxx 进入售后归档阶段,准备创建案例...
  • ✅ 控制台显示:✅ 案例创建成功: caseId
  • ✅ 控制台显示:✅ 项目已自动添加到案例库
  • ✅ 访问案例库页面能看到新创建的案例

场景2:重复创建检测

步骤:

  1. 对一个已创建过案例的项目
  2. 再次推进到"售后归档"阶段(或手动触发)

预期结果:

  • ✅ 控制台显示:⚠️ 项目 xxx 已有关联案例,跳过创建
  • ✅ 不会创建重复案例

场景3:数据缺失处理

步骤:

  1. 创建一个数据不完整的项目(缺少设计师或团队)
  2. 推进到"售后归档"阶段

预期结果:

  • ❌ 控制台显示:❌ 创建案例失败: 项目缺少设计师信息
  • ❌ 不会创建案例,但不影响项目阶段更新

场景4:图片提取

步骤:

  1. 创建项目并上传多个产品
  2. 每个产品添加附件图片
  3. 推进到"售后归档"

预期结果:

  • ✅ 案例的images字段包含所有产品的附件图片
  • ✅ 第一张图片自动设为coverImage
  • data.imagesDetail正确分类图片(装修前/后、视频、全景)

案例库显示验证

访问:http://localhost:4200/customer-service/case-library

预期显示内容

  1. 案例卡片

    • 封面图片正确显示
    • 案例名称 = 项目标题
    • 设计师名称
    • 项目类型标签
    • 面积、价格信息
  2. 详情面板

    • 所有图片可以浏览
    • 产品列表完整
    • 预算信息准确
    • 关联项目链接可点击
  3. 筛选功能

    • 按项目类型筛选有效
    • 按面积范围筛选有效
    • 搜索功能正常

后续优化建议

1. 案例审核流程

  • 添加审核状态字段:reviewStatus: '待审核' | '已通过' | '已拒绝'
  • 内部人员审核后才发布到案例库

2. 批量操作

  • 批量选择项目转为案例
  • 批量发布/下架案例

3. 数据增强

  • 自动生成案例描述(基于项目数据)
  • AI生成标签和关键词
  • 自动选择最佳封面图

4. 通知机制

  • 案例创建成功后通知设计师
  • 案例被分享时通知相关人员

5. 统计分析

  • 案例浏览量统计
  • 案例分享次数
  • 热门案例排行

开发者备注

  • 案例创建是异步操作,不会阻塞项目阶段更新
  • 所有Parse操作都包含错误处理
  • 使用company字段实现多租户隔离
  • isDeleted字段实现软删除
  • isPublished控制案例是否对外显示
  • 图片URL需要是完整的可访问地址

相关文件

新建文件

  • yss-project/src/app/services/project-to-case.service.ts

修改文件

  • yss-project/src/app/services/project.service.ts

相关文件(无需修改)

  • yss-project/src/app/services/case.service.ts - 案例CRUD服务
  • yss-project/src/app/pages/customer-service/case-library/case-library.ts - 案例库页面
  • yss-project/src/app/pages/customer-service/case-library/case-library.html - 案例库模板