# 诊单视觉重构最终业务回归审计(三次终验) - 终验日期:2026-08-10 - 范围:生产视频/IM、医生备注与日常记录 mutation、处方/订单、多挂号精确取消、诊单 Shell owner、列表 QR/日志/订单;并复核上一版已关闭的权限、DTO、隐私与 generation 项。 - 基准:当前最新 Python 源码与测试,以及 `D:/web/zyt/admin`、`D:/web/zyt/server` 的实际 admin 行为和服务端控制器/Logic。 - 约束:只读业务终验;未安装依赖、未打包、未访问线上接口。除更新本报告外,未修改业务源码或测试。 ## 最终结论 第三次终验后的剩余项为: - **P0:0,明确无 P0。** - **P1:1 项 OPEN。** 诊单列表“创建订单”已调用真实 `order.order/create`,但没有消费返回的 `order_no` 并调用现成的 `generate_order_qrcode`,admin 的付款二维码闭环仍中断。 - **P2:2 项 OPEN。** 一是日常矩阵不能编辑已有血糖/饮食/运动记录;二是视频回放只显示每条记录的第一个 URL,且没有 admin 的“向具体通话记录追加回放”入口。 - **服务端不可实现限制:不计入 P0/P1/P2。** 医生备注与跟踪备注的服务端契约只支持按当天追加;服务端没有任意旧正文覆盖或整条删除 endpoint,桌面端无法在不扩展后端的前提下实现这些动作。 - 其余本轮复核项均 **CLOSED**:生产视频/IM 取数与同步、备注追加/附件、日常新增与待办、处方动作、订单详情/偏移、精确挂号取消、列表视频/确认 QR 与日志、真实 Shell 60%/move-resize/close generation。 - 定向离线回归:**128/128 passed**。 | 复核项 | 三次终验状态 | 最终等级 | 摘要 | |---|---|---:|---| | 生产视频/IM `only_archived`、sync、上传、回放 | **OPEN(主体 CLOSED)** | **P2** | 生产接口、同步、工具栏上传和首个回放可用;多 URL 与指定 call-record 追加入口缺失 | | 医生备注 mutation | **CLOSED** | — | 追加正文、上传舌象/报告、删除单附件均走真实合同;正文覆盖/整条删除属后端无 endpoint 限制 | | 日常记录 mutation | **OPEN(新增 CLOSED)** | **P2** | 新增血糖/饮食/运动/跟踪备注及待办新增/取消可用;已有三类记录不能编辑 | | 处方/详情订单动作 | **CLOSED** | — | 开方、处方查看、业务订单列表/详情与诊次偏移均真实落库/取数 | | 多挂号精确取消 | **CLOSED** | — | 每个挂号卡携带精确 ID,最终调用 `doctor.appointment/cancel` | | 诊单 owner、60%、move-resize、generation | **CLOSED** | — | 每次打开重绑真实 Shell,宽度为 Shell 60%,移动/缩放/关闭同步且所有异步代次失效 | | 列表 QR / 日志 / 创建订单 | **OPEN(QR/日志/创建主体 CLOSED)** | **P1** | 视频/确认 QR、日志、创建订单均真实调用;创建成功后的支付 QR 未接入 UI | 上一版已关闭的 `phonePlain`、canonical `diagnosis_type`、`viewOnly` endpoint 分流、detail 失败禁存、身份证 15/18 位校验、权限 Tab 裁剪与隐私脱敏,本轮定向回归均未发现反弹。 ## 逐项证据 ### 1. 生产视频/IM:`[OPEN / P2,主体 CLOSED]` 已关闭部分: - `RemoteDoctorRepository` 已实现生产 `getCallRecords`、手工 call record、附件关联及“上传 → 创建记录 → 关联”顺序:`src/doctor_workstation/services/repository.py:1875-1943`。这与 admin `CallRecordPanel` 的真实流程一致:`D:/web/zyt/admin/src/views/tcm/diagnosis/components/CallRecordPanel.vue:109-180`。 - IM 首次读取明确发 `only_archived=1`,同步走 `triggerImChatSync`:`src/doctor_workstation/services/repository.py:1945-1965`;dialog 的 lazy 查询也固定 `only_archived=True`:`src/doctor_workstation/ui/dialogs/diagnosis.py:2016-2038`。admin 对照为 `D:/web/zyt/admin/src/views/tcm/diagnosis/components/ImChatRecordPanel.vue:148-185`。 - edit 态视频工具栏调用真实上传,成功后按 media generation 重新加载;聊天同步成功后延时重新读取归档:`src/doctor_workstation/ui/dialogs/diagnosis.py:2541-2611`。聊天图片/文件只能通过 HTTP(S) 安全打开:`:2652-2658`。 - production/server 合同存在:`D:/web/zyt/server/app/adminapi/controller/tcm/DiagnosisController.php:397-438`、`:505-538`;归档快路径在 `D:/web/zyt/server/app/adminapi/logic/tcm/DiagnosisLogic.php:1015-1045`。 仍 OPEN 的 P2: - 服务端返回 `recording_urls_list` 数组:`D:/web/zyt/server/app/adminapi/logic/tcm/DiagnosisLogic.php:1810-1833`;admin 把完整数组交给播放器,并在每个具体 call row 提供上传入口:`D:/web/zyt/admin/src/views/tcm/diagnosis/components/CallRecordPanel.vue:30-72`。 - Python `_fill_video` 只取 `raw_urls[0]`,其余回放不可见,且视频表没有指定 `call_record_id` 的上传操作列:`src/doctor_workstation/ui/dialogs/diagnosis.py:1126-1135`、`:2844-2881`。虽然 repository 已支持 `call_record_id`(`src/doctor_workstation/services/repository.py:1894-1913`),UI 没有把它接出来。 该问题不造成错误写入,且工具栏上传与一个回放可用,故按 **P2** 而非 P1 计。 ### 2. 医生备注:`[CLOSED;后端限制已明确]` - NotesTimeline 已暴露新增、上传、删除单附件与打开附件信号:`src/doctor_workstation/ui/diagnosis_drawer.py:1804-1810`、`:1820-1851`、`:1887-1961`;dialog 绑定真实方法:`src/doctor_workstation/ui/dialogs/diagnosis.py:989-997`、`:2376-2487`。 - production DTO 分别走 `doctor.appointment/addDoctorNote`、`doctor.appointment/doctorNotes`、`doctor.appointment/deleteDoctorNoteImage`:`src/doctor_workstation/services/repository.py:935-985`。权限分别按新增与删除附件 endpoint 裁剪:`src/doctor_workstation/ui/dialogs/diagnosis.py:478-483`、`:1233-1241`。 - admin 自身也只有追加、上传、删除单附件动作:`D:/web/zyt/admin/src/views/patient/reception/components/NoteTimeline.vue:195-268`;API 定义仅三条:`D:/web/zyt/admin/src/api/patient.ts:19-40`。 服务端不可实现边界: - controller 仅暴露 `addDoctorNote`、`doctorNotes`、`deleteDoctorNoteImage`,随后类即结束:`D:/web/zyt/server/app/adminapi/controller/doctor/AppointmentController.php:168-198`。 - `DoctorNoteLogic::addOrAppend` 对当天正文使用换行追加,不支持任意旧正文覆盖:`D:/web/zyt/server/app/adminapi/logic/doctor/DoctorNoteLogic.php:12-76`;唯一删除逻辑只删除附件数组中的单项:`:111-136`。 因此“任意旧笔记正文覆盖/整条删除”不是桌面端漏接已有合同,而是服务端没有 endpoint;本报告将其列为**后端限制,不计客户端 P0/P1/P2**。 ### 3. 日常记录:`[OPEN / P2,新增 CLOSED]` 已关闭部分: - edit 态已提供血糖、饮食、运动、跟踪备注新增按钮和待办新增/取消;只读态/无权限态隐藏:`src/doctor_workstation/ui/diagnosis_drawer.py:1363-1371`、`:1431-1450`、`:1481-1511`、`:1760-1791`。 - dialog 将 signal 接到真实 add/todo repository,并为每次 mutation 使用 diagnosis-id + generation 校验后强制刷新:`src/doctor_workstation/ui/dialogs/diagnosis.py:1089-1101`、`:2175-2358`。 - production 合同为 `tcm.bloodRecord/add`、`tcm.dietRecord/add`、`tcm.exerciseRecord/add`、`tcm.diagnosis/addTrackingNote`、todo add/cancel:`src/doctor_workstation/services/repository.py:1745-1765`、`:1778-1805`、`:1847-1873`。 仍 OPEN 的 P2: - admin 点击已有单元格时会把记录(含 `id`)放入表单,保存时分别调用 blood/diet/exercise edit:`D:/web/zyt/admin/src/views/tcm/diagnosis/components/DailyMatrix.vue:921-1013`。 - Python matrix 明确 `NoEditTriggers`、`NoSelection`,只有 `daily_add_requested`,没有已有记录 edit signal:`src/doctor_workstation/ui/diagnosis_drawer.py:1366-1371`、`:1455-1463`。dialog 也只选择 `add_*`:`src/doctor_workstation/ui/dialogs/diagnosis.py:2260-2284`。 - 这不是后端限制:production repository 已实现三类 edit:`src/doctor_workstation/services/repository.py:1767-1776`、`:1787-1796`、`:1807-1818`。 跟踪备注例外:server 设计就是“仅给今天追加”。controller 仅有 add/list:`D:/web/zyt/server/app/adminapi/controller/tcm/DiagnosisController.php:180-214`;`TrackingNoteLogic` 只有 `addOrAppend/getByDiagnosis`,类在第 95 行结束:`D:/web/zyt/server/app/adminapi/logic/tcm/TrackingNoteLogic.php:8-95`。其任意旧正文覆盖/整条删除同样属于后端无 endpoint 限制,不计客户端问题。 ### 4. 处方与详情订单动作:`[CLOSED]` - “开具处方”打开真实 `PrescriptionEditorDialog`,accepted 后调用 `create_prescription`,并携带 diagnosis/appointment/case snapshot:`src/doctor_workstation/ui/dialogs/diagnosis.py:2489-2539`。 - 历史处方的“查看”打开真实 `PrescriptionDetailDialog`:`:2791-2842`。 - 业务订单 lazy 列表、详情权限/真实 repository 门禁、异步 order generation 及详情 dialog 均已接通:`:1998-2000`、`:2069-2074`、`:2883-2991`。 - 全局诊次偏移调用真实 `setRevisitSlotStartOffset`,并有独立 generation:`:2613-2650`。 未发现处方或详情订单的假动作/越权入口,本项 **CLOSED**。 ### 5. 多挂号精确取消:`[CLOSED]` - 每个可取消挂号卡按精确 primary key 生成按钮,signal 携带原 record 与该 appointment-id:`src/doctor_workstation/ui/diagnosis_index_widgets.py:150-163`、`:1121-1148`。歧义的行级“取消挂号”仍只在唯一挂号时出现:`:1403-1410`。 - handler 选择当前诊单后再次按 ID 和状态校验,确认前后都防 stale,最终仅调用 exact capability:`src/doctor_workstation/ui/pages/consultations.py:1868-1921`。 - production 方法明确走 `doctor.appointment/cancel`,不再误用患者工作区 endpoint:`src/doctor_workstation/services/repository.py:1545-1555`。 - 新测试覆盖两个挂号、超 32 位 appointment-id、错误诊单、状态刷新、权限/能力缺失与 canonical endpoint:`tests/test_diagnosis_index_visual.py:340-379`、`tests/test_consultations_parity_ui.py:610-774`、`tests/test_diagnosis_detail_contract.py:90-105`。 本项 **CLOSED**。 ### 6. 真实 Shell owner / 60% / move-resize / generation:`[CLOSED]` - `_owner` 不再在 page 构造期缓存;每次 `open_for/showEvent` 都从当前 `parent.window()` 重绑真实 Shell:`src/doctor_workstation/ui/dialogs/diagnosis.py:526-527`、`:1314-1349`、`:1398-1408`。 - overlay 与 owner 同位置/尺寸,宽屏 drawer 固定为 owner 宽度 60%;监听 owner 的 Resize/Move/Show/Close:`:1283-1299`、`:1351-1371`。 - `reject/done` 及 owner Close 会使 detail/save/orders/order-detail/daily/notes/media/offset/所有 lazy-tab generation 全部失效:`:1373-1383`、`:1681-1700`。 真实 `ShellWindow -> QStackedWidget -> ConsultationsPage -> cached DiagnosisDialog` offscreen 探针结果: ```text OPEN owner_shell=True shell=(1200,760) dialog=(1200,760) panel=720 expected=720 pos=(80,60) MOVED shell=(1024,650) dialog=(1024,650) panel=614 expected=614 pos=(190,140) CLOSE visible=False generation=True media=True ``` 本项 **CLOSED**。现有 `tests/test_diagnosis_drawer_visual.py:837-861` 仍只用直接顶层 owner;本次额外真实 Shell 探针补足了业务终验,但建议后续把该探针固化成回归测试。 ### 7. 列表 QR / 日志 / 订单:`[OPEN / P1,主体 CLOSED]` 已关闭部分: - 页面只在 canonical 权限与真实 repository 方法同时存在时展示视频 QR、确认 QR、日志、订单入口:`src/doctor_workstation/ui/pages/consultations.py:704-715`、`:1005-1030`。 - QR DTO 校验 patient/doctor/share-user/diagnosis,日志使用 diagnosis-id,所有菜单请求均受 selection generation 保护:`:1972-2091`。 - production repository 真实调用 `generateMiniProgramQrcode`、`guahaoLogList`、`order.order/create`:`src/doctor_workstation/services/repository.py:1967-2060`。测试验证 exact DTO、权限裁剪与 stale callback:`tests/test_diagnosis_detail_contract.py:85-127`、`tests/test_consultations_parity_ui.py:777-958`。 仍 OPEN 的 P1: - admin 创建订单成功后读取 `order_no`,立即调用 `generateOrderQrcode` 并展示付款 QR:`D:/web/zyt/admin/src/views/tcm/diagnosis/index.vue:1474-1509`。 - Python `_create_diagnosis_order` 把真实 create 放进通用 `_run_mutation`;该 helper 丢弃 `_result`,只 toast + refresh,因此永远不会生成付款 QR:`src/doctor_workstation/ui/pages/consultations.py:2093-2128`、`:2460-2479`。 - repository 明明已有 `generate_order_qrcode`:`src/doctor_workstation/services/repository.py:2062-2076`,但 UI 源码没有任何调用。现有测试也只分别测试“UI 创建订单”和“repository 可生成 QR”,未覆盖两者串联:`tests/test_consultations_parity_ui.py:777-872`、`tests/test_diagnosis_detail_contract.py:88-89`。 订单已实际创建却不展示付款入口,用户容易停在半完成状态或重复建单;这是主流程闭环缺失,按 **P1 OPEN** 计。 ## 离线验证记录 使用项目现有 `.venv`,设置 `QT_QPA_PLATFORM=offscreen`、`PYTHONDONTWRITEBYTECODE=1`,禁用 pytest cache,并把 basetemp 放在系统临时目录;未安装任何依赖: ```text pytest -q -p no:cacheprovider \ tests/test_diagnosis_detail_contract.py \ tests/test_diagnosis_index_visual.py \ tests/test_diagnosis_drawer_visual.py \ tests/test_appointment_drawer_visual.py \ tests/test_consultations_parity_ui.py \ tests/test_prescription_security_ui.py \ tests/test_patients_ui.py \ tests/test_repository_parity.py \ tests/test_permissions.py 128 passed ``` 测试退出码为 0;`--collect-only -q` 分文件计数合计 128。另执行了上述真实 Shell owner 探针,退出码为 0。 ## 发布判断 当前不存在错误 endpoint、歧义挂号取消、权限泄漏、隐私明文回流或关闭后旧异步回调落地等 P0 风险。生产视频/IM、备注追加、日常新增、处方、订单详情及列表主要动作均已从“假动作/未接入”进展为真实 repository 行为。 但若发布标准是“完整保留 admin 的订单支付闭环与所有已存在的数据编辑/回放能力”,当前仍不满足:**剩余 P0 0 / P1 1 / P2 2**。其中任意旧医生/跟踪笔记正文覆盖或整条删除必须先扩展 server endpoint,不能作为桌面端单独修复项。