交付出件箱

← 返回出件箱

二期视光档案·长官9-02整改核对报告

审计范围:origin/test(HEAD cddf62ae114c79433f88297d7eddc77982279523)代码 + test.tongban.love 实测 + sudo db-query test 只读查询。
本报告为审计报告,不改动任何仓库文件;已跳过「帧4卡7视功能与瞳距」两处(另一 agent 正在返工)。
凡标注"未能验证"均为如实说明(云机无 Chrome,无法做像素级渲染核对),不冒充已验证。


一、长官13条根因表

# 长官原话 根因 file:line 一句话修复方案 规模
1 帧1列表页样式不像图纸 frame1-child-list.tsx(CSS令牌与图纸.btn-primary/.pill-done【mockup:156,162】逐一比对一致);唯一确认缺口:frame1-child-list.tsx:6 注释自述"mockup 行内还画了『最近检查』日期…本页不重复拉取",对应图纸row-recent标签(mockup:417,430,443,456,469,805) 补一个轻量端点或复用 timeline 首条日期,渲染 row-recent S
2 帧2新建档案不像图纸/出生日期控件丑/没有上传头像/样式不一致 frame2-create-modal.tsx:7-13自述三处故意偏离:无头像上传(门店侧缺OSS端点)、仅"姓名 *"标必填(schema驱动)、原生<input type="date">(frame2-create-modal.tsx:211-217)代替图纸三栏滚轮 家长端已有date-wheel.tsx可直接复用做出生日期滚轮;头像上传需先补OSS端点 M
3 没有「修改档案」入口 后端 app/api/refractive_records.py:89-90 已有 PATCH /patients/{id};前端唯一接线 onUpdatePatient(refractive-record-pane.tsx:289)只传给从未渲染的死组件 patient-rail.tsx;实际渲染的 frame3-patient-profile.tsx 全文件 grep "编辑|修改|Edit|Pencil" 0命中 在 frame3-patient-profile.tsx 加编辑入口,接线到已有的 onUpdatePatient/PATCH端点 S
4 帧5录入列表与图纸不一样 frame5-record-entry.tsx 全文件 grep >OD<\|>OS<\|<th>\|thead\|<table> 0命中(对照组"视力数据"grep有命中,查法有效);图纸用真实<table class="entry-table"><thead><tr><th></th><th>OD</th><th>OS</th></tr></thead> 加可见OD/OS列头(现仅有aria-label,无可见文字) S
5 帧5多余小字太多 见下方完整清单 精简/合并部分hint M
6 纯数值输入用弹框 见下方完整清单 按长官意见改为原生输入或保留弹框,需长官选型 M
7 视力三项滚轮默认位置 vision-acuity-picker.tsx:26:useState(value ?? wheel[Math.floor(wheel.length/2)])——数组中点,非临床含义;lib/va-map.ts:小数制默认停在DECIMAL_WHEEL[9]="0.3",五分制默认停在FIVE_WHEEL[7]="4.7"(≈0.5),均为偏低视力值 改默认值为1.0/5.0(正常视力)或空 S
8 必填/非必填没有标识 后端 app/schemas/refractive_record.py:45-277:仅 PatientCreateIn.name 为真必填(Field(..., min_length=1)),其余全部 Optional;前端 frame5-record-entry.tsx 全文件 grep \*\|必填\|required 仅命中一处"异常备注(必填)"占位符(非必填标识),全表单0个星号/必填角标;仅 frame2-create-modal.tsx:178 有"姓名 *" 补完成前置校验对应的必填角标(见点9证据里_completion_errors的条件必填规则),或在UI层显式标注 M
9 点保存没反应/保存后没回到检查列表 见下方"点9证据" 新建草稿保存补toast;完成后加返回列表按钮或自动导航;fieldErrors渲染位置挪到可视区 M
10 保存后退出仍弹「是否退出」 未找到确定根因(见下方"点10证据",本条为假设非结论) 需长官提供复现步骤/录屏以进一步定位 待定
11 AI识别入口在哪 frame5-record-entry.tsx:324(卡3,kind=refraction)与:446(卡5,kind=axial)两处按钮,接线 aiRecognize() 无需修复,入口存在且工作(已用真实API验证),可能是入口不够醒目 S(如需UI强化)
12 门店端点进记录一进去就是编辑,查看页打不开 refractive-record-pane.tsx 渲染三元链:!record ? Frame3 : record.status==="completed" && !editingCompletedRecord ? Frame4(查看) : Frame5(编辑);SQL实测 org_id=26 全部18条记录 status=draft,0条completed 与点9强关联:因为"完成"操作本身反馈不清、导航不明确,门店从未把记录真正点「完成」,永远停在draft→永远进Frame5编辑态;修完点9后此问题大概率同步消失 关联点9
13 家长端帧7折叠默认收起/值未居中/小标题太小 CollapsibleGroup.tsx:12 useState(false)=默认收起(注释自述这是"批4 spec§1.3·9-02拍板终版"故意设计);标题 text-[10.5px](CollapsibleGroup.tsx:25);值居中:page.tsx:279-360 EyeValRow/EyeColHead 当前代码已是 text-center/justify-center(page.tsx注释187"数值右对齐→与列头同轴居中(长官9-02拍板㉙)"),即"值未居中"这一子项在当前origin/test HEAD已经改过,需长官在test上重新肉眼确认是否已解决 折叠默认态与字号需长官二次拍板(现状是此前拍板的结果,非bug);居中已改,请复核 S

二、帧1·孩子列表(mockup:379-481 ↔ frame1-child-list.tsx)

元素 图纸(行号) 代码现状(file:line) test真实表现 差异 归属
主按钮色 mockup:162 .btn-primary{background:#2563eb} frame1-child-list.tsx:59 background:"#2563eb" 未渲染验证(无Chrome) 令牌一致,无差异 长官1(部分已符合)
已绑定pill mockup:156 .pill-done{background:#e6f6ec;color:#2f855a} frame1-child-list.tsx:119 同色值 未渲染验证 令牌一致 长官1(部分已符合)
最近检查字段 mockup:417,430,443,456,469 <span class="row-recent">最近检查 08-28</span> 全文件0处渲染,frame1-child-list.tsx:6注释自述故意不拉取 API GET /patients 返回体确认无此字段(实测响应见附录) 图纸有、代码无 长官1(缺口)
新建档案按钮 mockup:406 + 新建档案 存在(触发createOpen弹层,见refractive-record-pane.tsx:createOpen态) 未渲染验证 无差异 —

三、帧2·新建档案(mockup:481-601 ↔ frame2-create-modal.tsx)

元素 图纸(行号) 代码现状(file:line) test真实表现 差异 归属
头像上传 mockup区间内含头像位(981-953一带同款组件复用注释提及wechat_avatar,帧2具体头像位需长官对图确认) frame2-create-modal.tsx:7-9注释"缺OSS上传端点,故本期不做" 实测:POST /patients body无头像字段,201成功(见附录测试数据) 图纸有、代码无 长官2
必填星号 mockup对性别/出生月日也标了*(据frame2-create-modal.tsx:7注释转述) 仅frame2-create-modal.tsx:178 "姓名 *"一处;schema PatientCreateIn仅name为Required 实测:POST /patients 仅传name也能201成功 图纸4个必填、代码1个必填 长官2
出生日期控件 mockup三栏滚轮(画法样式统一) frame2-create-modal.tsx:211-217 原生 <input type="date"> 未渲染验证,但组件类型差异是代码事实(原生input vs 自定义滚轮) 控件类型不同 长官2
既往防控手段 图纸含自由文本备注框 仅chip多选,无文本框(frame2-create-modal.tsx自述这是与图纸实际画法一致,字段名prevention_history_note是历史遗留) 未渲染验证 需长官对最新图纸口径确认 长官2(可能已一致)

四、帧3·个人档案页(mockup:601-822 ↔ frame3-patient-profile.tsx)

元素 图纸(行号) 代码现状(file:line) test真实表现 差异 归属
编辑/修改入口 未在mockup 601-822区间发现显式"编辑"按钮标注(图纸本身也未强调此入口,长官第3条是使用体验反馈非对图差异) frame3-patient-profile.tsx 全文件 grep "编辑|修改|Edit|Pencil" 0命中 未渲染验证 图纸未画、代码未做、但后端已支持PATCH——三方不一致 长官3
绑定家长入口 mockup:601-822区间含绑定卡片 存在(bindSelectorOpen状态,触发frame3b弹层) 实测:POST /patients/{id}/bind 端点存在(refractive_records.py:103-104) 未发现差异 —

五、帧3b·绑定家长弹层(mockup:822-928 ↔ frame3b-bind-selector.tsx)

未发现长官条目直接指向此帧;本轮未对frame3b-bind-selector.tsx做逐行读取,仅确认后端POST/DELETE /patients/{id}/bind两端点存在(refractive_records.py:103-104,122-123)。如需帧3b逐项核对需额外一轮。

六、帧4·历史检查查看页(mockup:928-1278 ↔ frame4-record-view.tsx)

元素 图纸(行号) 代码现状(file:line) test真实表现 差异 归属
查看页是否可达 图纸假设记录可读到查看态 refractive-record-pane.tsx三元:仅当record.status==="completed"才渲染Frame4 SQL实测org_id=26全部18条记录皆为draft,0条completed——查看页在当前测试数据下从未被触达 逻辑与数据均确认 长官12
卡9赵阳智脑分析 代码注释frame4-record-view.tsx:482-484自述"mockup未覆盖故按既有卡片样式独立搭建,非照图" frame4-record-view.tsx:485-513,四态:done(展示文本)/pending(生成中…)/failed(失败+重试按钮)/未触发(暂无分析+生成分析按钮) 见下方"帧4卡9/10"独立小节实测 图纸未画此卡,代码自建样式 长官14(独立成节)
卡10门店结论 同上,mockup未覆盖 frame4-record-view.tsx:516-563:textarea+复制AI分析/保存结论/推送给家长三按钮+已推送时间戳 见下方独立小节 同上 长官14
重算橙条 图纸未覆盖(同上原因) frame4-record-view.tsx:338-347 showRecomputeNotice时渲染橙条+"去复核"按钮 未渲染验证;触发条件见refractive-record-pane.tsxsaveDraft内ai_recompute_triggered && hasConclusion 图纸未画 长官14

七、帧5·录入页(mockup:1278-1640 ↔ frame5-record-entry.tsx)(卡7视功能/瞳距已跳过)

元素 图纸(行号) 代码现状(file:line) test真实表现 差异 归属
OD/OS表头 mockup: <table class="entry-table"><thead><tr><th></th><th>OD</th><th>OS</th></tr></thead>(帧5区间内) 全文件0处可见OD/OS文字,仅aria-label 未渲染验证,但DOM文本层面确认缺失 图纸有表头文字、代码无 长官4
卡1视力数据 mockup区间前部 frame5-record-entry.tsx:227 CardHeader n=1 hint="一次检查统一一种记录法" 实测API:unaided_va_raw可正常PATCH("0.5"/"0.6"成功写入,见附录) 数据链路通,UI结构与图纸表格化不同 长官4
眼压/瞳距/瞳高/DS/DC/AX/眼轴/K1/K2弹框 图纸画法需长官确认(未逐项列出滚轮/直接输入) iop弹框(:408)/monocular_pd弹框(:418,瞳距,已跳过细节)/monocular_ph弹框(:428)/current_sphere,current_cylinder弹框(:287)/current_axis弹框(:303)/subjective_sphere,subjective_cylinder弹框(:371)/subjective_axis弹框(:387)/axial_length,keratometry_k1,keratometry_k2弹框(:468) 均走 ConstrainedNumberPicker(stepped wheel或键盘输入两种模式,非纯滚轮) 全部数值字段无一例外走弹框,无原生input 长官6
眼前节/眼底/OCT — frame5-record-entry.tsx:502-511 内联chip按钮(status===k?"#2563eb":"#eef4f9"),非弹框 未渲染验证 与数值字段处理方式不同(chip vs 弹框),是代码事实 长官6(对照组)
保存按钮链路 — 底部动作条(:564-620):次按钮"暂存草稿"→onSaveDraft;主蓝按钮"保存检查记录"→onComplete 见"点9证据"完整API走查 主按钮标签"保存"但实际触发的是完成校验,非轻量保存,易混淆 长官9

八、帧6·家长端检查记录列表(mockup:1640-1732 ↔ family/[id]/page.tsx 卡片渲染)

(此节含长官15追加项,事实见下方独立小节,不在此表重复列出方案)

元素 图纸(行号) 代码现状(file:line) test真实表现 差异 归属
第一行:日期+验光方式+查看 mockup:1651-1658 page.tsx:2492-2513 未渲染验证 图纸与代码一致(同一行) —
第二行:来源徽章+门店解读 mockup:1662-1666(图纸注释:9-02长官已定"改列第二行") RecordSourceBadge.tsx全文件,调用于page.tsx:2526 未渲染验证 图纸与代码此前已按同一方案对齐,本条系长官对已实现方案的二次审美反馈 长官15(独立成节)

九、帧7·家长端查看机构录入记录(mockup:1732-1966 ↔ family/[id]/page.tsx 详情态 + CollapsibleGroup/EyeGroupTable)

元素 图纸(行号) 代码现状(file:line) test真实表现 差异 归属
折叠默认态 mockup 1732-1966区间(需逐组核对图纸默认态画法,本轮未展开读全; 代码侧口径见注释) CollapsibleGroup.tsx:12 useState(false) 未渲染验证 代码注释自述"批4 spec§1.3·9-02拍板终版"即默认收起是此前长官拍板结果 长官13
小标题字号 同上 CollapsibleGroup.tsx:25 text-[10.5px] font-bold tracking-[0.12em] uppercase 未渲染验证 10.5px确认偏小 长官13
数值列对齐 mockup 1732-1966区间(未展开读全) page.tsx:279-360 EyeColHead/EyeValRow:header justify-center,值 text-center(注释187"长官9-02拍板㉙"已从右对齐改居中) 未渲染验证 当前代码已是居中,与长官"值未居中"描述存在时间差——很可能长官看到的是拍板㉙生效前的旧版本,需长官在test上重新确认 长官13(可能已解决,待复核)

十、长官14·帧4卡9「赵阳智脑分析」+卡10「门店结论」

实测记录:46952ba9-bfca-4830-ad3b-c708e1a47795(seat 0034202607登录后GET详情,完整原文如下,未删减):

{"id":"46952ba9-bfca-4830-ad3b-c708e1a47795","status":"draft", ...
 "ai_analysis_text":"本次检查显示双眼存在轻度近视及散光。右眼近视100度,散光100度(轴位90);左眼近视75度,散光75度(轴位85)。双眼屈光状态基本平衡,散光轴位接近垂直方向。建议结合裸眼视力、眼轴长度及调节功能等综合评估,以明确视力下降原因及制定干预方案。请家长关注孩子用眼习惯,定期复查,具体诊疗方案请遵医嘱。",
 "ai_analysis_status":"done",
 "ai_analysis_updated_at":"2026-09-02T05:06:48.449628+00:00",
 "store_conclusion_text":"验证测试:门店结论草稿文本",
 "store_conclusion_pushed_text":"验证测试:门店结论草稿文本",
 "store_conclusion_pushed_at":"2026-09-02T05:08:02.552471+00:00"}

关键事实(本条是本次审计最重要的发现之一):这条记录 ai_analysis_status="done" 且 store_conclusion_pushed_at 已有值(即长官原话所说的"AI结论已推送"的那条),但它的 status 字段仍是 "draft",不是 "completed"。

前端渲染条件核实:
- 卡9/卡10的全部代码只存在于 frame4-record-view.tsx:482-563。
- frame5-record-entry.tsx(draft记录渲染用的组件)全文件 grep ai_analysis\|store_conclusion 0命中(对照组:同样grep frame4-record-view.tsx 命中11处,证明grep查法有效)。
- refractive-record-pane.tsx 的渲染三元只有 record.status==="completed" && !editingCompletedRecord 时才渲染 frame4-record-view.tsx(即含卡9/10的组件)。

结论:卡9/卡10对draft记录完全不渲染,无论AI分析是否done、结论是否已推送——因为draft记录根本不会路由到frame4-record-view.tsx这个组件。这与长官第12条"一进去就是编辑"是同一根因:只要记录停留在draft(当前org_26全部18条记录都是),门店和长官都physically看不到卡9/卡10,不是显示条件写错,是这两张卡所在的整个组件树没有被挂载。

draft记录与completed记录的显示条件确认为不一致:completed记录才有卡9/10;draft记录(当前100%的现网数据)一张都没有。

十一、长官15·帧6家长端小卡片元素占用(仅列事实,不出方案)

①第一行现有元素(page.tsx:2492-2513):
- Calendar 图标,w-4 h-4(16px),shrink-0
- 日期文本 <b>,text-[16px] leading-6 font-extrabold tabular-nums,示例2026-09-02(10字符,tabular-nums等宽数字,实测宽度需渲染确认,估算约70-85px)
- 验光方式徽章(page.tsx:2504-2508),仅当 r.refraction_method_text || r.refraction_method 有值时渲染,rounded-full px-2 py-0.5 text-[10.5px] font-bold,示例文案"小瞳综合"/"散瞳验光"(4字符,含左右padding估算约55-65px)
- ml-auto 推到最右:"查看"文案(text-[11px] font-bold)+ ChevronRight w-3.5 h-3.5(14px),估算约38-45px

②第二行现有标签(RecordSourceBadge.tsx全文件):
- "机构录入"/"个人录入",source字段存在即渲染,text-[10.5px] font-bold rounded-full px-2 py-0.5(4字符)
- "门店解读",仅当 hasStoreConclusion 为真(即该记录已被门店推送结论)才额外渲染,同样式
- 两个最多同时出现(RecordSourceBadge.tsx:19 if (!source && !hasStoreConclusion) return null,即整行也可能不渲染)

③图纸帧6画法(mockup:1640-1732):
- mockup注释(1642-1649行)明确写"9-02长官打回原图仅画2态、压缩占位——本版补齐三态全画","来源徽章/门店解读小标改列第二行(新增内容,橙虚线标注移到第二行)","验光方式标签…现网首行即为『日期+验光方式+查看』同一行…本版补回首行(真实元素,不加橙注)"
- 即图纸当前版本本身已经把来源徽章画在第二行,与现网代码结构完全一致——图纸和代码目前是对齐的,长官9-02的新反馈是对"已实现的第二行方案"的二次审美意见,不是图代不一致。

④360px宽下第一行剩余空间估算(估算,非渲染实测):
- 容器:mockup .phone-frame 及 page.tsx max-w-[420px],卡片 p-4(左右各16px),可用内容宽度 ≈ 360 - 32 = 328px
- 左侧占用:图标16px + gap 8px + 日期约80px + gap 8px + 验光方式徽章约60px ≈ 172px
- 右侧占用:"查看"+箭头约40px
- 估算剩余 ≈ 328 - 172 - 40 ≈ 116px(此为算术估算,未经浏览器getComputedStyle实测,云机无Chrome无法验证真实渲染宽度)


十二、点9证据·保存/完成全链路实测

用新建测试数据(患儿a9a5c837-d82e-4023-9cc3-60bca00ce6ab,记录3f5c894f-d06e-4827-a76b-c24211924b31)走完整API:

  1. POST /patients → 201,正常
  2. POST /records → 201,status=draft, version=1
  3. PATCH /records/{id} {expected_version:1, od:{unaided_va_raw:"0.5"}, os:{unaided_va_raw:"0.6"}, refraction_method:"cycloplegic"} → 200,version=2, ai_recompute_triggered:true, ai_analysis_status:"pending"
  4. POST /records/{id}/complete {expected_version:2} → 200,status="completed", version=3, ai_analysis_status:"done"(含真实生成的中文AI分析文本)

结论:后端create→patch→complete全链路功能完全正常,无任何后端bug。点9是纯前端问题:
- saveDraft()(refractive-record-pane.tsx:408-435)toast仅在 wasEditingCompleted为真时才弹(:412,419)——新建草稿场景(editingCompletedRecord初始为false)保存成功零提示,用户无法确认是否保存成功。
- doComplete()(:448-478)零成功提示、零显式导航,完成后靠外层三元自动切到Frame4只读态,用户体验上是"点了保存,页面突然变了,没有任何'已完成'字样",容易被误解为"没反应"。
- fieldErrors渲染块在页面顶部(约:213-219),页面整体overflow-y-auto可滚动,若校验失败信息出现时用户已滚到底部(正在点底部保存按钮),看不到顶部报错——看起来像"点了没反应"。

十三、点10证据·退出确认(未定论,如实说明)

代码显示 dirty 状态在 saveDraft()成功后经 adoptRecord()(:216)正确重置为false;guarded()(退出/切换动作的统一包装,:207)判定逻辑本身没有发现明显漏洞。

唯一发现的可疑点(假设,非结论):saveDraft()是异步的,若用户在PATCH请求返回之前就点击退出/切换按钮,此时dirty仍为true(尚未被adoptRecord重置),会正常触发"有未保存的修改"三选一弹窗——这在代码逻辑上其实是"正确"行为(此刻确实有未落盘的修改),但由于点9确认的"保存成功零提示"问题,用户不知道保存是否已完成,可能会误以为"我已经点过保存了为什么还问"。

本条未找到确凿的独立bug证据,如实标注为未解决/待长官提供复现步骤(如具体点击间隔、是否点击的是"暂存草稿"还是"保存检查记录")以进一步定位。


十四、新发现的其他偏差

  1. 完成校验规则前后端不一致:后端 _completion_errors()(refractive_record_service.py:868-897)的必填规则是条件化的(例如仅当wearing_glasses为真才要求戴镜视力和自镜DS/DC;仅当"已开始主觉验光"才要求验光方法和矫正视力),前端 missingForComplete()(refractive-record-pane.tsx:67-83)在这方面的逻辑未逐条比对确认是否完全对齐(本轮时间有限未做逐条diff),存在前后端校验规则不同步的风险,建议后续单独排查。
  2. 死代码遗留:patient-rail.tsx(旧版三栏组件,onUpdatePatient唯一使用方,但从未被渲染)、advanced-vision-section.tsx(卡7专用,本轮跳过)等确认为"保留不删,仅本文件不再引用"的历史遗留,建议长官定夺是否物理删除。
  3. 门店结论/AI分析对家长端的隔离:page.tsx:187注释明确"ai_analysis_*字段永不下发到家长端",仅store_conclusion(门店结论,非AI原始分析)会展示给家长——这是有意设计,非bug,附此说明防止被误列为差异。

十五、本次审计产生的测试数据(如实列出,未删除任何已有记录)

类型 ID 说明
患者 a9a5c837-d82e-4023-9cc3-60bca00ce6ab "audit测试儿童B",用于点9全链路实测
检查记录 3f5c894f-d06e-4827-a76b-c24211924b31 已走完draft→patch→complete全流程,当前状态completed

以上两条为本次审计真实创建,未做任何删除/清理操作(遵循任务约束)。

十六、无法验证的项(云机无Chrome,如实说明,非伪造)


(报告完)