实测 POST /api/health/report/proxy-parse(admin token + 真实 PDF)返回 code:200,响应体缺:draftId、extractedName、extractedReportNumber、extractedReportDate、payload、needBind。
实测数据库:proxy-parse 未创建任何草稿记录。
后果:前端 previewResult 无 draftId → 点「确认入库」命中 if (!this.previewResult || !this.previewResult.draftId) → 直接 warning「没有可确认的草稿」→ 主链路(上传→预览→确认入库)完全跑不通;预览页「被检测人/报告编号/报告日期/健康总分/菌群健康分/肠型」6 行全 '-'。
根因:LangGraph /api/v1/report/parse 不建草稿、不返 draftId;非代传链路是「Java 先建草稿 → 调 LangGraph → updatePayload → buildParseResultFromLg 组装返回体」,代传链路把这两步都省了,直接透传 LangGraph 原始返回。
连带:draftId == null → updateNote 分支被跳过 → 备注连草稿表都没进。
修复方向:proxyParsePreview 复用非代传链路模式。
用人工构造的 pending 草稿(draft 98,payload 691KB,remark=test note)+ subjectId 1018 调 proxy-confirm,返回 {reportId:36, subjectId:1018, status:"confirmed"}。
查 health_reports WHERE id=36:proxy_uploader_id=1001 ✅ / proxy_upload_note=NULL ❌ / ai_analysis=NULL ❌ / remark=「募极生物肠道菌群检测报告」(硬编码)。
后端日志 SQL:UPDATE health_reports SET ... remark=...募极生物肠道菌群检测报告... proxy_uploader_id=1001 ... —— UPDATE 语句中根本不包含 proxy_upload_note 字段,坐实 P0-1 与 P0-2。
FamilyAccessInterceptor 在 enforcement 模式(默认)。实测:
proxy-parse 无 token → JwtInterceptor 走 setAnonymousUser 放行 → Controller isProxyUploader 返回 false → 业务 code 500「无权限」而非 HTTP 401/403。proxy-parse/proxy-confirm 均不在 LOGIN_REQUIRED_PATHS。 实测确认角色校验发生在 storageService.storeFile 之前 → 无凭据请求不会写入 OSS,刷存储风险排除;残余问题是错误码语义错误导致前端拦截器不触发登录跳转。
本次验证共产生以下测试数据,均已在验证结束后主动删除(连同关联的 health_indicators / health_gut_flora / health_disease_risks / report_food_suitability / report_payload_logs 记录):
关于第七节「测试数据清理记录」:测试库写入已全部清理(明细见第七节)。仅对象存储侧残留本次上传的 3 份测试 PDF(JDCloud OSS 测试桶 cfc 的 uploads/2026/10/25/ 下),因对象存储删除不可逆、避免误删他人对象而保留。如需复现上述用例,请按正文各节重新构造草稿与账号(脚本要点:health_report_drafts 插入 pending 行、payload_json 取自 id=4、expire_at = NOW() + 72h、remark 写备注;users 插入 family_id=NULL 且 teacher_family_ids 为空的 teacher 账号;basic_quota_usage(1001, REPORT_READING) 的 used_count 已被重置回 0)。
关于「最小化 LangGraph 桩件」:本次 127.0.0.1:9000 并非返回伪造响应的假桩,而是挂载仓库内真实 cfc-langgraph/app/api/report_parse.py 与 analyze.py 两个 FastAPI 路由的最小装载(完整 app.main:app 因本机 sqlite<3.35 无法启动 chromadb);/api/v1/report/parse 实测返回真实解析结果(format=beijing、overview.report_number=2025-200-89746 等)。/api/v1/analyze 的 500 根因已定位为最小装载缺少 langgraph checkpointer 的 thread_id 配置(ValueError: Checkpointer requires one or more of the following 'configurable' keys),属验证环境限制而非产品缺陷。
关于「跨家庭 subjectId 错绑」的额外实锤(第八节未列):auditConfirm 不校验 subjectId 是否属于草稿的 familyId。用 family=2009 的草稿 id=99 调 proxy-confirm {draftId:99, subjectId:1014}(1014 属 family 2005)→ 返回 200,落库 health_reports#37: family_id=2009, subject_id=1014,即报告挂在 2009 家、被检测人却是 2005 家成员。与 P1-2 叠加后,「无家庭绑定账号」可向任意家庭写入并绑定到别家成员的报告。该行已随 id=37 一并清理。
关于「admin 端报告对象下拉」补充:admin(userId=1001, family 2009)调 /api/family/member/list {"familyId":2005} → 403(admin 并不豁免 FamilyAccessInterceptor,仅 /api/admin/** 路径豁免);而 /api/admin/families 对 admin 返回全部 16 个家庭(ids: 2018…2004, 1)→ admin 页面选择除 2009 外的家庭后「报告对象」下拉必为空。teacher 侧 /api/admin/families 返回 0 条(按 teacher_family_ids 过滤)→ 家庭下拉本身为空,流程无法起步。
关于编译/构建命令的实际形态:仓库根目录无聚合 pom.xml(cfc-backend/pom.xml 为独立模块),故 mvn -pl cfc-backend -am compile 在根目录报 Could not find the selected project in the reactor: cfc-backend;等价命令在模块内执行通过:cd cfc-backend && mvn -pl . compile -DskipTests → BUILD SUCCESS(exit 0)。cfc-web 无 lint 脚本(package.json scripts 仅 dev/serve/prebuild/build/bump/test/test:coverage),故只跑 npm run build。