# 成长档案重建 — 设计规范 **日期:** 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 org.apache.pdfbox pdfbox 2.0.31 ``` #### 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` 通过