assessment-market-plan.md 24 KB

DAN 测评商城化改造 — 实施计划

版本: v1.0 日期: 2026-07-10 状态: 待实施


业务模型

供应商管理员 ──维护──> 商品(单包/套餐)
     │
     ▼
商城体系 ──购买──> 家长
     │
     ▼
发放测评额度(采购与测评执行解耦)
     │
     ▼
预约 → 确定具体测评项目 → 执行测评
     │
     ▼
报告(规划师录入 / 家长上传 / 后续API自动获取)
     │
     ▼
规划师给出结构化指导意见
     │
     ▼
系统自动生成个性化任务方案
     │
     ▼
规划师审核方案 ──> 家长确认方案 ──> 执行跟踪
     │                              │
     └── 驳回/修改 ←────────────────┘
                                        │
                                        ▼
                                方案完成 → 复测提醒 → 再次购买/测评

全五阶段依赖关系

阶段一 (商品化)
   ↓
阶段二 (采购与执行解耦)  ←─ 依赖阶段一的商品购买流程
   ↓
阶段三 (报告双通道)  ←─ 独立,可与阶段二并行
   ↓
阶段四 (方案生成引擎)  ←─ 依赖阶段三的完整结果数据
   ↓
阶段五 (审核→确认→执行)  ←─ 依赖阶段四的方案生成

阶段一:测评商品化

目标

将 DAN 测评单包/套餐作为 Product 存在于商城体系中,由供应商管理员维护。

实体扩展

Product 类型扩展

// entity/Product.java — 新增常量
public static final String TYPE_ASSESSMENT = "assessment";

productType 字段已存在,无需改表。

AssessmentProduct 扩展实体

DB 迁移(schema.sql + DatabaseInitializer

CREATE TABLE IF NOT EXISTS assessment_products (
    id              BIGINT AUTO_INCREMENT PRIMARY KEY,
    product_id      BIGINT NOT NULL COMMENT '关联products表',
    assessment_type VARCHAR(20) NOT NULL COMMENT 'single=单包, bundle=套餐',
    total_sessions  INT NOT NULL DEFAULT 1 COMMENT '包含测评次数',
    validity_days   INT NOT NULL DEFAULT 365 COMMENT '有效期(天)',
    guide_scope     VARCHAR(50) DEFAULT 'all' COMMENT '规划师范围: all/assigned/auto',
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_product_id (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评商品扩展信息';

实体

// entity/AssessmentProduct.java
@Data
@TableName("assessment_products")
public class AssessmentProduct implements Serializable {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long productId;
    private String assessmentType;  // single / bundle
    private Integer totalSessions;
    private Integer validityDays;
    private String guideScope;       // all / assigned / auto
    private Date createdAt;
    private Date updatedAt;
}

DTO

// dto/ProductCreateRequest.java
@Data
public class ProductCreateRequest {
    private Product product;
    private AssessmentProduct assessmentExt;  // productType=assessment 时必填
}

Service

// service/AssessmentProductService.java
@Service
public class AssessmentProductService {
    @Resource private AssessmentProductMapper mapper;

    public AssessmentProduct getByProductId(Long productId) { ... }
    
    @Transactional
    public void saveOrUpdate(Long productId, AssessmentProduct ext) {
        mapper.delete(new LambdaQueryWrapper<AssessmentProduct>()
                .eq(AssessmentProduct::getProductId, productId));
        ext.setId(null);
        ext.setProductId(productId);
        mapper.insert(ext);
    }
}

Controller 改造

AdminProductController.create() 改造:当 productType=assessment 时,同时保存 AssessmentProduct 扩展信息。

管理端前端

新建 cfc-web/views/admin/assessment-products.vue

  • 测评商品专用管理页
  • 商品表单:类型选择"测评"时展开额外字段(测评类型、次数、有效期、规划师范围)
  • 商品列表:类型标识、次数、状态列

购买流程

测评商品作为普通商品在商城展示,走现有 ProductOrder 体系:

用户浏览商城 → 点击测评商品 → 商品详情页
                     ↓
             选择购买数量 → 加入购物车/立即购买
                     ↓
             填写购买信息
                     ↓
             提交 → ProductOrder.create() → payment → paySuccess()

ProductOrderService.paySuccess() 中判断 productType=assessment 时,调用 AssessmentQuotaService.grantQuota()(阶段二实现,先留桩)。测评商品为虚拟商品,支付成功自动确认收货。

验证标准

□ 管理员可在管理端创建/编辑/上下架测评商品
□ 测评商品在商城展示,用户可下单购买
□ 购买后走 ProductOrder 流程,productType='assessment'
□ 管理端订单列表可区分测评商品订单
□ 供应商可管理自己的测评商品

阶段二:采购与测评执行解耦

目标

购买测评商品后获得"测评额度",具体测什么在测评时确定,而不是购买时绑定。

测评额度表 assessment_quotas

DB 迁移

CREATE TABLE IF NOT EXISTS assessment_quotas (
    id                BIGINT AUTO_INCREMENT PRIMARY KEY,
    quota_no          VARCHAR(32) NOT NULL COMMENT '额度编号',
    family_id         BIGINT NOT NULL COMMENT '家庭ID',
    child_id          BIGINT COMMENT '指定孩子ID(NULL=不限)',
    product_order_id  BIGINT NOT NULL COMMENT '来源订单ID',
    product_id        BIGINT NOT NULL COMMENT '商品ID',
    product_name      VARCHAR(100) NOT NULL COMMENT '商品名快照',
    total_sessions    INT NOT NULL DEFAULT 1 COMMENT '总次数',
    remaining_sessions INT NOT NULL DEFAULT 1 COMMENT '剩余次数',
    validity_start    DATETIME NOT NULL COMMENT '有效期开始',
    validity_end      DATETIME NOT NULL COMMENT '有效期截止',
    status            VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT 'active/expired/used_all/cancelled',
    created_at        DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_family (family_id),
    INDEX idx_order (product_order_id),
    UNIQUE KEY uk_quota_no (quota_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评额度表';

实体

// entity/AssessmentQuota.java
@Data
@TableName("assessment_quotas")
public class AssessmentQuota implements Serializable {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String quotaNo;
    private Long familyId;
    private Long childId;
    private Long productOrderId;
    private Long productId;
    private String productName;
    private Integer totalSessions;
    private Integer remainingSessions;
    private Date validityStart;
    private Date validityEnd;
    private String status;        // active / expired / used_all / cancelled
    private Date createdAt;
    private Date updatedAt;
}

测评执行记录表 assessment_executions

DB 迁移

CREATE TABLE IF NOT EXISTS assessment_executions (
    id                BIGINT AUTO_INCREMENT PRIMARY KEY,
    quota_id          BIGINT NOT NULL COMMENT '关联额度ID',
    child_id          BIGINT NOT NULL COMMENT '被测评孩子',
    guide_id          BIGINT COMMENT '执行规划师ID',
    assessment_type   VARCHAR(30) COMMENT '本次具体测评类型(如attention/memory/full)',
    appointment_id    BIGINT COMMENT '关联预约ID',
    result_id         BIGINT COMMENT '关联测评结果ID',
    status            VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT 'pending/in_progress/completed/cancelled',
    scheduled_date    DATETIME COMMENT '预约执行时间',
    completed_at      DATETIME COMMENT '实际完成时间',
    notes             VARCHAR(500) COMMENT '备注',
    created_at        DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_quota (quota_id),
    INDEX idx_child (child_id),
    INDEX idx_appointment (appointment_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评执行记录表';

额度服务 AssessmentQuotaService

核心方法:

方法 说明 调用方
grantQuota(ProductOrder) 支付成功后发放额度 ProductOrderService.paySuccess()
consumeQuota(quotaId, childId) 预约时消耗一次额度 AssessmentAppointmentService.createAppointment()
getAvailableQuotas(familyId, childId) 查询家庭可用额度列表 前端展示
getQuotaDetail(quotaId) 额度使用明细 前端展示

支付回调集成

// ProductOrderService.paySuccess() 中新增
if ("assessment".equals(order.getProductType())) {
    assessmentQuotaService.grantQuota(order);
}

预约流程改造

改造前:预约时创建订单 + 预约 改造后:预约时消耗额度 → 创建预约 → 关联执行记录

// AssessmentAppointmentController.createAppointment() 改造
// 1. 消耗额度(内含校验)
int remaining = quotaService.consumeQuota(quotaId, childId);
// 2. 创建预约(只创建预约,不再创建订单)
AssessmentAppointment appointment = appointmentService.createAppointment(...);
// 3. 关联执行记录
executionService.linkAppointment(quotaId, childId, appointment.getId());

旧 Controller 清理

端点 处理
AssessmentOrderController.* 标记 @Deprecated,保留兼容
DanAssessmentController 中的订单方法 标记 @Deprecated

前端:我的额度页面

新建 pages/assessment/quota.vue:展示家庭可用额度(剩余次数、有效期、进度条)

预约流程改造apply.vue 中先选择额度包 → 选择孩子 → 选择规划师+日期 → 提交

验证标准

□ 购买测评商品后自动发放额度到家庭
□ 家庭可用额度列表 API 返回正确
□ 预约时消耗额度,剩余次数递减
□ 额度过期自动标记 expired
□ 执行记录完整追溯:订单→额度→预约→结果
□ 前端"我的额度"页面展示所有可用额度
□ 预约流程改为先选额度再预约

阶段三:报告双通道 + 规划师指导意见

目标

家长可上传报告,规划师录入结果时附带结构化指导意见,预留 API 自动获取扩展点。

DanAssessmentResult 增加来源字段

DB 迁移

ensureColumn("dan_assessment_results", "source",
    "VARCHAR(20) DEFAULT 'planner_entry' COMMENT '报告来源: planner_entry/parent_upload/auto_fetch'");

状态常量

public static final String SOURCE_PLANNER_ENTRY = "planner_entry";
public static final String SOURCE_PARENT_UPLOAD = "parent_upload";
public static final String SOURCE_AUTO_FETCH = "auto_fetch";

历史数据回填 source = 'planner_entry'

家长端上传报告 API

@PostMapping("/api/dan-assessment/result/upload")
public Result<DanAssessmentResult> uploadReport(
    @RequestParam("file") MultipartFile file,
    @RequestParam("childId") Long childId,
    @RequestParam(value = "notes", required = false) String notes) {
    // 1. 权限校验
    // 2. 保存文件(FileStorageService)
    // 3. 基础解析元信息
    // 4. 创建结果记录(source=parent_upload, status=parent_uploaded)
    // 5. 异步触发深度解析(OCR)
}

文件存储服务

// service/FileStorageService.java
public String upload(MultipartFile file, String subDir) {
    // UUID文件名 + 保存到本地文件系统
    // 返回相对URL
}

报告解析服务

// service/ReportParseService.java
// extractMeta(): 提取文件名、日期等元信息(同步)
// asyncDeepParse(): 异步OCR深度解析(当前为桩,后续扩展)

// ★ SPI 扩展点:ReportFetchProvider
public interface ReportFetchProvider {
    boolean supports(String sourceType);
    DanAssessmentResult fetch(Long childId, Map<String, Object> params);
}

规划师指导意见结构化

DB 迁移

CREATE TABLE IF NOT EXISTS growth_guidances (
    id                BIGINT AUTO_INCREMENT PRIMARY KEY,
    result_id         BIGINT NOT NULL COMMENT '关联测评结果ID',
    dimension         VARCHAR(30) NOT NULL COMMENT '维度: attention/focus/memory/logic/emotion/general',
    score             INT COMMENT '该维度得分(0-100)',
    suggestion        TEXT NOT NULL COMMENT '建议内容',
    priority          INT DEFAULT 0 COMMENT '优先级(0=普通, 1=重要, 2=紧急)',
    sort_order        INT DEFAULT 0 COMMENT '排序',
    created_at        DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at        DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_result (result_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='测评指导意见(结构化)';

API

端点 方法 说明
/api/dan-assessment/result/{resultId}/guidance/save POST 保存结构化指导意见
/api/dan-assessment/result/{resultId}/guidance GET 获取指导意见

前端改造

页面 改动
upload-report.vue(新建) 家长上传报告页面(选择孩子、文件、备注)
results.vue 展示报告来源标签(规划师录入/家长上传/系统获取)
record.vue 指导意见改为结构化录入(每个维度独立建议框 + 优先级选择)

规划师确认家长上传报告

@PostMapping("/api/dan-assessment/result/{resultId}/confirm-upload")
// 规划师补充结构化评分后确认,status→completed

验证标准

□ 测评结果来源字段正确记录
□ 家长可上传 PDF/JPG 格式报告
□ 上传后状态为 parent_uploaded,等待规划师确认
□ 规划师可查看上传的报告文件并补充结构化评分
□ 指导意见从大文本升级为结构化多维度存储
□ record.vue 录入界面支持每个维度独立指导意见
□ 报告列表展示来源标签和图标

阶段四:结果→任务方案自动生成引擎

架构

DanAssessmentResult + GrowthGuidance[]
        │
        ▼
AssessmentPlanGenerator
        │
    ┌───┴───┐
    ▼       ▼
  规则引擎  AI增强(后续SPI)
    │       │
    └───┬───┘
        ▼
TaskPlanInstance + TaskPlanItem[]
        │
        ▼
  规划师审核 → 家长确认 → 执行跟踪(阶段五)

规则配置表 assessment_plan_rules

DB 迁移

CREATE TABLE IF NOT EXISTS assessment_plan_rules (
    id              BIGINT AUTO_INCREMENT PRIMARY KEY,
    dimension       VARCHAR(30) NOT NULL COMMENT '测评维度: attention/focus/memory/logic/emotion',
    score_min       INT NOT NULL COMMENT '分数下限(含)',
    score_max       INT NOT NULL COMMENT '分数上限(含)',
    template_id     BIGINT NOT NULL COMMENT '关联TaskTemplatePackage ID',
    priority        INT DEFAULT 0 COMMENT '优先级',
    is_active       TINYINT(1) DEFAULT 1 COMMENT '是否启用',
    created_at      DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_dimension (dimension),
    INDEX idx_score (score_min, score_max)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='方案生成规则';

核心引擎 AssessmentPlanGenerator

// 三步流程:
// 1. collectScores(result): 从测评结果提取各维度得分
// 2. matchRules(scores): 规则匹配 → 低分维度→对应训练模板包
// 3. buildPlan(result, packages): 合并模板 → TaskPlanInstance

// 关键逻辑:
// - 匹配到多个模板时,取最长周期为主方案,其余合并
// - 所有维度正常 → 生成维持型方案
// - 方案状态为 generated(非直接 active)
// - 方案关联 source_result_id 可追溯

TaskPlanInstance 扩展

DB 迁移

ensureColumn("task_plan_instances", "source",
    "VARCHAR(20) DEFAULT 'manual' COMMENT '方案来源: manual/assessment/template'");
ensureColumn("task_plan_instances", "source_result_id",
    "BIGINT COMMENT '关联测评结果ID(来源为assessment时)'");
ensureColumn("task_plan_instances", "remark",
    "TEXT COMMENT '方案备注(含指导意见摘要)'");

触发点

触发点 位置 说明
规划师提交结果 AssessmentService.recordResult() 结果 completed 时自动触发
规划师确认家长上传报告 DanAssessmentController.confirmUpload() 确认后触发
AI 自动获取报告完成 ReportFetchProvider(后续) 占位

预设训练模板

TaskTemplatePackage 中预置(管理端创建):

模板名称 维度 周期 任务示例
注意力基础训练(14天) attention 14天 舒尔特方格、静坐冥想、找不同
注意力基础训练(7天) attention 7天 同上短周期
注意力进阶训练(21天) attention 21天 舒尔特×2、冥想10分钟、拼图、阅读报告
专注力基础训练(14天) focus 14天 番茄钟、单任务专注、干扰抑制
记忆力训练(14天) memory 14天 数字记忆、图像记忆、故事复述
逻辑力训练(14天) logic 14天 数独、逻辑推理、分类游戏
综合维持方案(7天) general 7天 阅读、运动、情绪日记

管理端规则配置

新建 cfc-web/views/admin/assessment-plan-rules.vue

  • 规则列表(维度/分数区间/关联模板/优先级/启用状态)
  • 规则 CRUD

验证标准

□ 规则表可配置:维度 + 分数区间 → 训练模板
□ 管理端有规则 CRUD 页面
□ 规划师提交测评结果后自动调用生成器
□ 匹配到多条规则时合并为一个方案
□ 方案状态为 generated(非直接激活)
□ 所有维度正常时生成维持型方案
□ 方案关联 source_result_id,可追溯来源
□ 方案 remark 包含指导意见摘要
□ 规则不匹配时优雅降级

阶段五:方案审核→确认→执行跟踪

状态机

generated → pending_review → active → completed
    │            │              │
    └──→ cancelled ←───────────┘
         (任何状态可取消)
    generated: 系统生成,待规划师审核
    pending_review: 规划师已审核,待家长确认
    active: 已确认,执行中
    completed: 全部完成
    cancelled: 取消

TaskPlanInstance 扩展审核字段

DB 迁移

ensureColumn("task_plan_instances", "reviewed_by", "BIGINT COMMENT '审核人(规划师ID)'");
ensureColumn("task_plan_instances", "reviewed_at", "DATETIME COMMENT '审核时间'");
ensureColumn("task_plan_instances", "review_comment", "VARCHAR(500) COMMENT '审核意见'");
ensureColumn("task_plan_instances", "confirmed_by", "BIGINT COMMENT '确认人(家长ID)'");
ensureColumn("task_plan_instances", "confirmed_at", "DATETIME COMMENT '确认时间'");
ensureColumn("task_plan_instances", "activated_at", "DATETIME COMMENT '激活时间'");

API 清单

端点 角色 说明
POST /api/guide/plans/pending-review 规划师 获取待审核方案列表
POST /api/guide/plans/{id}/detail 规划师/家长 方案详情(含所有任务项)
POST /api/guide/plans/{id}/review 规划师 审核方案(approve/reject)
POST /api/guide/plans/{id}/items/adjust 规划师 调整方案任务项
POST /api/plans/pending-confirm 家长 获取待确认方案列表
POST /api/plans/{id}/confirm 家长 确认/退回方案
POST /api/plans/{id}/progress 家长 方案执行进度
POST /api/plans/{id}/pause 家长 暂停方案
POST /api/plans/{id}/resume 家长 恢复方案
POST /api/plans/{id}/complete 系统 完成方案(触发复测提醒)

每日任务生成

// service/TaskGenerationService.java
// generateTodayTasks(planId): 从 TaskPlanItem → 当日 Task 实例

// task/PlanTaskGenerator.java (定时任务)
// @Scheduled(cron = "0 30 5 * * ?")
// 每天 5:30 生成所有活跃方案的当日任务

复测提醒

方案完成时根据 DanAssessmentResult.nextReviewDate 发送提醒:

┌─ 🎉 方案已完成! ───────────────┐
│  DAN专注力训练方案(14天)          │
│  完成度: 100%                     │
│                                  │
│  建议在 2026-08-10 前进行复测     │
│  [立即预约复测]  [稍后再说]       │
└──────────────────────────────────┘

前端页面清单

页面 角色 说明
pages/guide/plan-review.vue 规划师 待审核方案列表 + 审核详情页
pages/plans/pending-confirm.vue 家长 待确认方案列表 + 确认详情页
pages/plans/dashboard.vue 家长 方案总览(进行中/已完成/待确认)
pages/plans/daily-view.vue 孩子 今日训练任务(执行入口)
pages/plans/weekly-view.vue 家长 周视图进度

验证标准

□ 方案生成后状态为 generated,不直接激活
□ 规划师可查看待审核方案列表和详情
□ 规划师可调整方案任务项(增删改)
□ 规划师审核通过→status=pending_review
□ 家长确认→status=active,退回→返规划师
□ 方案激活后自动生成当日任务
□ 每日定时任务生成活跃方案的当日任务
□ 方案执行进度可追踪
□ 方案完成后触发复测提醒
□ 可暂停和恢复执行中的方案

全五阶段文件清单

新增文件

阶段 后端 前端
AssessmentProduct.java, AssessmentProductMapper.java, AssessmentProductService.java, ProductCreateRequest.java views/admin/assessment-products.vue
AssessmentQuota.java, AssessmentExecution.java, AssessmentQuotaMapper.java, AssessmentExecutionMapper.java, AssessmentQuotaService.java, AssessmentExecutionService.java, AssessmentQuotaController.java, AssessmentQuotaVO.java pages/assessment/quota.vue
GrowthGuidance.java, GrowthGuidanceMapper.java, GrowthGuidanceService.java, FileStorageService.java pages/assessment/upload-report.vue
AssessmentPlanRule.java, AssessmentPlanRuleMapper.java, AssessmentPlanGenerator.java, AssessmentPlanRuleService.java, AdminPlanRuleController.java views/admin/assessment-plan-rules.vue
GuidePlanReviewController.java, PlanConfirmController.java, TaskGenerationService.java, PlanTaskGenerator.java pages/guide/plan-review.vue, pages/plans/pending-confirm.vue, pages/plans/dashboard.vue, pages/plans/daily-view.vue, pages/plans/weekly-view.vue

修改文件

阶段 后端 前端
Product.java, AdminProductController.java, ProductOrderService.java pages/shop/detail.vue
ProductOrderService.java, AssessmentAppointmentController.java, AssessmentOrderController.java, DanAssessmentController.java apply.vue, purchase.vue
DanAssessmentResult.java, ReportParseService.java, DanAssessmentController.java record.vue, results.vue
AssessmentService.java, TaskPlanInstance.java, TaskPlanService.java, DanAssessmentController.java
TaskPlanInstance.java, TaskPlanService.java

Sprint 建议

Sprint 1: 阶段一 + 阶段三(可并行)
Sprint 2: 阶段二(依赖阶段一)+ 阶段五前端原型(与阶段四并行)
Sprint 3: 阶段四 + 阶段五后端
Sprint 4: 联调 + 测试 + 部署