版本: v1.0 日期: 2026-07-10 状态: 待实施
供应商管理员 ──维护──> 商品(单包/套餐)
│
▼
商城体系 ──购买──> 家长
│
▼
发放测评额度(采购与测评执行解耦)
│
▼
预约 → 确定具体测评项目 → 执行测评
│
▼
报告(规划师录入 / 家长上传 / 后续API自动获取)
│
▼
规划师给出结构化指导意见
│
▼
系统自动生成个性化任务方案
│
▼
规划师审核方案 ──> 家长确认方案 ──> 执行跟踪
│ │
└── 驳回/修改 ←────────────────┘
│
▼
方案完成 → 复测提醒 → 再次购买/测评
阶段一 (商品化)
↓
阶段二 (采购与执行解耦) ←─ 依赖阶段一的商品购买流程
↓
阶段三 (报告双通道) ←─ 独立,可与阶段二并行
↓
阶段四 (方案生成引擎) ←─ 依赖阶段三的完整结果数据
↓
阶段五 (审核→确认→执行) ←─ 依赖阶段四的方案生成
将 DAN 测评单包/套餐作为 Product 存在于商城体系中,由供应商管理员维护。
// entity/Product.java — 新增常量
public static final String TYPE_ASSESSMENT = "assessment";
productType 字段已存在,无需改表。
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/ProductCreateRequest.java
@Data
public class ProductCreateRequest {
private Product product;
private AssessmentProduct assessmentExt; // productType=assessment 时必填
}
// 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);
}
}
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'
□ 管理端订单列表可区分测评商品订单
□ 供应商可管理自己的测评商品
购买测评商品后获得"测评额度",具体测什么在测评时确定,而不是购买时绑定。
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;
}
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='测评执行记录表';
核心方法:
| 方法 | 说明 | 调用方 |
|---|---|---|
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());
| 端点 | 处理 |
|---|---|
AssessmentOrderController.* |
标记 @Deprecated,保留兼容 |
DanAssessmentController 中的订单方法 |
标记 @Deprecated |
新建 pages/assessment/quota.vue:展示家庭可用额度(剩余次数、有效期、进度条)
预约流程改造:apply.vue 中先选择额度包 → 选择孩子 → 选择规划师+日期 → 提交
□ 购买测评商品后自动发放额度到家庭
□ 家庭可用额度列表 API 返回正确
□ 预约时消耗额度,剩余次数递减
□ 额度过期自动标记 expired
□ 执行记录完整追溯:订单→额度→预约→结果
□ 前端"我的额度"页面展示所有可用额度
□ 预约流程改为先选额度再预约
家长可上传报告,规划师录入结果时附带结构化指导意见,预留 API 自动获取扩展点。
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'。
@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[]
│
▼
规划师审核 → 家长确认 → 执行跟踪(阶段五)
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='方案生成规则';
// 三步流程:
// 1. collectScores(result): 从测评结果提取各维度得分
// 2. matchRules(scores): 规则匹配 → 低分维度→对应训练模板包
// 3. buildPlan(result, packages): 合并模板 → TaskPlanInstance
// 关键逻辑:
// - 匹配到多个模板时,取最长周期为主方案,其余合并
// - 所有维度正常 → 生成维持型方案
// - 方案状态为 generated(非直接 active)
// - 方案关联 source_result_id 可追溯
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 页面
□ 规划师提交测评结果后自动调用生成器
□ 匹配到多条规则时合并为一个方案
□ 方案状态为 generated(非直接激活)
□ 所有维度正常时生成维持型方案
□ 方案关联 source_result_id,可追溯来源
□ 方案 remark 包含指导意见摘要
□ 规则不匹配时优雅降级
generated → pending_review → active → completed
│ │ │
└──→ cancelled ←───────────┘
(任何状态可取消)
generated: 系统生成,待规划师审核
pending_review: 规划师已审核,待家长确认
active: 已确认,执行中
completed: 全部完成
cancelled: 取消
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 '激活时间'");
| 端点 | 角色 | 说明 |
|---|---|---|
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 1: 阶段一 + 阶段三(可并行)
Sprint 2: 阶段二(依赖阶段一)+ 阶段五前端原型(与阶段四并行)
Sprint 3: 阶段四 + 阶段五后端
Sprint 4: 联调 + 测试 + 部署