Pārlūkot izejas kodu

specs: add growth record rebuild design

liaoxg 3 mēneši atpakaļ
vecāks
revīzija
95f74cc639

+ 244 - 0
docs/superpowers/specs/2026-06-22-growth-record-rebuild-design.md

@@ -0,0 +1,244 @@
+# 成长档案重建 — 设计规范
+
+**日期:** 2026-06-22
+**状态:** 待评审
+**关联计划:** `.omo/plans/2026-06-22-growth-record-rebuild.md`
+**范围:** cfc-backend + cfc-frontend + cfc-web
+
+---
+
+### 1. 背景
+
+当前 `growth_records` 表和 `GrowthRecordService` 存在的问题:
+
+| 问题 | 现状 |
+|------|------|
+| 无档案类型 | 仅 `externalSync` 布尔值,无法区分平台购买/自己上传/补充 |
+| 无去重机制 | 同一测评可重复建档 |
+| 无补充机制 | 无法在已有档案上追加新报告 |
+| 无报告日期提取 | 日期全靠手动,无法从报告中自动提取 |
+| 前端无上传入口 | `create.vue` 只填基本信息,无文件上传 |
+
+### 2. 三种建档流程
+
+#### 2.1 平台购买测评 → 自动同步建档
+
+```
+家长购买测评 → 规划师完成测评 → 提交 DanAssessmentResult
+    → 系统自动创建 GrowthRecord(source_type=platform_purchase)
+    → report_date 从 DanAssessmentResult.assessmentDate 取
+    → danLevel/overallScore/analysisReport 等字段从 DanAssessmentResult 直接复制
+    → 去重校验(同一 childId + assessmentDate 不重复建档)
+```
+
+**触发时机:** 规划师调用 `POST /api/dan-assessment/result/record` 提交测评结果时,自动触发。
+
+#### 2.2 用户自己上传报告建档
+
+```
+用户点击"上传报告" → 选择 PDF/图片文件
+    → 后端存储文件(复用 StorageService)
+    → 后端解析报告提取结构化数据(ReportParseService)
+    → 返回提取结果(含 report_date + 关键字段)给前端预览
+    → 用户确认/修改提取结果
+    → 后端创建 GrowthRecord(source_type=self_upload)
+    → 去重校验(同一 childId + reportDate 不重复建档)
+```
+
+**报告日期提取规则:**
+- PDF → Apache PDFBox 提取文本 → 正则匹配日期
+- 图片 → 读取 EXIF 日期(如有)
+- 提取失败 → 前端显示日期输入框,用户手动补充
+
+#### 2.3 档案补充
+
+```
+已有档案详情页 → 点击"补充报告"
+    → 两条路径(二选一):
+      a. 平台购买(同 2.1,额外传 parent_record_id)
+      b. 自己上传(同 2.2,额外传 parent_record_id)
+    → parent_record_id 关联原档案
+    → 补充后原档案仍保留(不覆盖),形成时间线
+```
+
+### 3. 数据库设计
+
+#### 3.1 growth_records 表新增字段
+
+```sql
+ALTER TABLE growth_records ADD COLUMN source_type VARCHAR(20) DEFAULT 'manual'
+    COMMENT '档案来源: platform_purchase/self_upload/supplement/manual';
+
+ALTER TABLE growth_records ADD COLUMN source_file_url VARCHAR(500)
+    COMMENT '上传报告文件URL(self_upload时有值)';
+
+ALTER TABLE growth_records ADD COLUMN order_id BIGINT
+    COMMENT '关联测评订单ID(platform_purchase时有值)';
+
+ALTER TABLE growth_records ADD COLUMN report_date DATETIME
+    COMMENT '报告测评日期(从报告内容提取)';
+
+ALTER TABLE growth_records ADD COLUMN parent_record_id BIGINT
+    COMMENT '补充来源档案ID(supplement时有值)';
+
+ALTER TABLE growth_records ADD COLUMN is_latest TINYINT(1) DEFAULT 1
+    COMMENT '是否该孩子最新档案(去重用)';
+
+ALTER TABLE growth_records ADD COLUMN dedup_hash VARCHAR(64)
+    COMMENT '去重哈希: SHA256(childId|reportDate|sourceType)';
+
+ALTER TABLE growth_records ADD COLUMN parsed_data TEXT
+    COMMENT '原始解析结果JSON(上传报告的完整提取结果)';
+
+ALTER TABLE growth_records ADD UNIQUE INDEX uk_dedup_hash (dedup_hash);
+```
+
+**字段映射:**
+
+| 字段 | 平台购买 | 自己上传 | 补充(平台) | 补充(上传) | 原手工建档 |
+|------|---------|---------|-----------|-----------|-----------|
+| `source_type` | `platform_purchase` | `self_upload` | `supplement` | `supplement` | `manual` |
+| `source_file_url` | NULL | 文件URL | NULL | 文件URL | NULL |
+| `order_id` | 测评订单ID | NULL | 测评订单ID | NULL | NULL |
+| `report_date` | assessmentDate | 正则提取 | assessmentDate | 正则提取 | NULL |
+| `parent_record_id` | NULL | NULL | 原档案ID | 原档案ID | NULL |
+| `dedup_hash` | SHA256 | SHA256 | SHA256 | SHA256 | NULL |
+| `parsed_data` | NULL | 解析JSON | NULL | 解析JSON | NULL |
+
+#### 3.2 去重规则
+
+**dedupHash = SHA256(childId + "|" + reportDate(yyyy-MM-dd) + "|" + sourceType)**
+
+去重逻辑:
+1. 插入前计算 dedupHash
+2. 查询 `WHERE dedup_hash = ? AND is_latest = 1`
+3. 存在 → 返回冲突信息(已有档案 ID 和标题)
+4. 用户选择:覆盖(旧 `is_latest=0`,插入新记录)或取消
+5. 不存在 → 直接插入
+
+supplement 类型 dedupHash 包含 parent_record_id,同一原始档案可有多份不同日期的补充。
+
+### 4. 报告解析 Service(ReportParseService)
+
+#### 4.1 新增依赖
+
+```xml
+<dependency>
+    <groupId>org.apache.pdfbox</groupId>
+    <artifactId>pdfbox</artifactId>
+    <version>2.0.31</version>
+</dependency>
+```
+
+#### 4.2 解析流程
+
+- `parseFromResult(DanAssessmentResult)` — 结构化数据直接提取(平台购买)
+- `parseReport(String localFilePath)` — PDFBox 提取文本 + 正则匹配(上传报告)
+
+#### 4.3 正则提取规则
+
+**日期:**
+- `(测评|报告|评估)日期[::]\\s*(\\d{4})年(\\d{1,2})月(\\d{1,2})日`
+- `(\\d{4})[-/.](\\d{1,2})[-/.](\\d{1,2})`
+- `(测评|报告|评估)日期[::]\\s*(\\d{4})[-/.](\\d{1,2})[-/.](\\d{1,2})`
+
+**DAN 等级:** `(?:DAN|dan)[\\s等级]*[::]\\s*([A-D])`
+
+**得分:** `{字段名}[\\s*得]*分*[::]\\s*(\\d+)`
+
+**段落:** 从 startLabel 匹配到 endLabel 之间的文本
+
+#### 4.4 ParsedReport DTO
+
+```java
+@Data @Builder
+public class ParsedReport {
+    private Date reportDate;
+    private String danLevel;
+    private Integer overallScore;
+    private Integer attentionScore;
+    private Integer focusScore;
+    private Integer memoryScore;
+    private Integer logicScore;
+    private Integer perceptionScore;
+    private Integer spatialScore;
+    private Integer processingSpeedScore;
+    private Integer emotionScore;
+    private Integer resilienceScore;
+    private String assessmentSummary;
+    private String growthSuggestions;
+    private String rawText;
+
+    public static ParsedReport empty() { return ParsedReport.builder().build(); }
+}
+```
+
+### 5. 后端 API 设计
+
+#### 5.1 修改现有接口
+
+| 接口 | 修改 |
+|------|------|
+| `POST /api/growth/record/create` | 增加 source_type/report_date/source_file_url/parsed_data/parent_record_id;增加去重校验 |
+| `POST /api/growth/record/create-with-order` | 传 source_type=platform_purchase;增加去重校验 |
+| `POST /api/growth/external/sync` | 传 source_type=platform_purchase;自动从 DanAssessmentResult 取 report_date;去重校验 |
+| `POST /api/growth/record/{id}` | 返回增加新字段 |
+
+#### 5.2 新增接口
+
+| 接口 | 说明 |
+|------|------|
+| `POST /api/growth/record/upload-and-parse` | 上传文件 → 存储 → 解析 → 返回 ParsedReport |
+| `POST /api/growth/record/supplement` | 补充建档(parent_record_id + 同 create 参数) |
+| `POST /api/growth/record/duplicate-check` | 去重检查 `{childId, reportDate, sourceType}` |
+| `POST /api/growth/record/supplements/{recordId}` | 获取某档案的所有补充记录 |
+
+#### 5.3 自动建档触发
+
+规划师提交测评结果 `POST /api/dan-assessment/result/record` 时,自动调用 `growthRecordService.createFromAssessment()`。
+
+### 6. 前端改动
+
+#### 6.1 growth/create.vue 重构
+
+两种入口:平台购买测评 / 上传报告建档。上传流程:选文件 → 上传解析 → 预览提取结果 → 用户确认 → 建档。日期提取失败时显示手动输入框。
+
+#### 6.2 growth/detail.vue 增强
+
+来源标签、报告日期、原始报告链接、"补充报告"按钮、补充记录列表。
+
+#### 6.3 growth/index.vue 增强
+
+来源标签(平台同步/自己上传/补充)、去重提示。
+
+#### 6.4 新增页面
+
+| 页面 | 路径 | 说明 |
+|------|------|------|
+| 上传报告建档 | `pages/growth/upload.vue` | 上传→解析→确认 |
+| 补充报告 | `pages/growth/supplement.vue` | 选方式→同建档流程 |
+
+#### 6.5 API 新增
+
+```javascript
+export const uploadAndParseReport = (filePath, childId) => { /* uni.uploadFile */ }
+export const supplementGrowthRecord = (data) => request('/api/growth/record/supplement', 'POST', data)
+export const checkGrowthRecordDuplicate = (data) => request('/api/growth/record/duplicate-check', 'POST', data)
+export const getSupplementRecords = (recordId) => request('/api/growth/record/supplements/' + recordId, 'POST')
+```
+
+### 7. cfc-web 管理端改动
+
+`views/teacher/GrowthRecords.vue` 表格增加列:来源类型、报告日期、是否最新、关联档案。补充记录折叠展示。
+
+### 8. 验收标准
+
+- [ ] 平台购买测评完成 → 自动创建成长档案(source_type=platform_purchase)
+- [ ] 用户上传 PDF → 解析出日期/等级/得分 → 用户确认后建档
+- [ ] 解析提取不到日期 → 用户可手动填写
+- [ ] 同一孩子同一日期同一来源 → 提示重复
+- [ ] 补充报告关联原档案,原档案不覆盖
+- [ ] 档案列表展示来源标签
+- [ ] 档案详情展示报告日期、原始报告链接
+- [ ] is_latest 标记正确
+- [ ] `mvn clean compile` 通过