Files
zyt/app/research/parity_reception_consultations.md
2026-08-10 17:29:05 +08:00

268 lines
33 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 接诊台 / 问诊列表:管理后台到医生桌面的逐项一致性审计
审计日期:2026-08-10
基准:`D:\web\zyt\admin\src\views` 中从现行路由可达的 Vue/TS 实现
对比对象:`D:\web\zyt\app\src\doctor_workstation` 当前 Python 实现
约束:本次只读审计;未修改 `src/`,未发网络请求。
## 1. 结论摘要
当前 Python 不是管理后台两个页面的等价移植,而是“接诊队列 + 简化问诊表 + 原生直呼视频”的医生端子集。端点骨架中,挂号列表、接诊聚合详情、通知、添加文字备注、完成接诊、诊单列表以及 `getCallSignature/startCall/bindCallRoom/endCall` 已接通;但今日队列边界、异步选人一致性、挂号状态判定、病例详情/编辑、开方、日常记录、备注媒体、复杂筛选和操作权限仍有显著差异。
发布阻断级结论:
1. **P0 — 接诊台快速切换患者会出现旧患者详情挂在新患者选择下。** 管理后台每次选人递增请求序号并同时校验序号和 `selectedId``D:\web\zyt\admin\src\views\patient\reception\index.vue:398-418`);Python 在任一详情请求进行中时直接拒绝下一次加载(`D:\web\zyt\app\src\doctor_workstation\ui\pages\reception.py:406-418`),旧响应只按旧 generation 接受(同文件 `:437-447`)。这会把显示、备注目标和当前队列行拆成两个患者上下文。
2. **P0 — Python 接诊队列没有“今天”条件。** 基准固定发送 `start_date=end_date=today``patient/reception/index.vue:296-305`);Python 只发送 `status/keyword/page/page_size``reception.py:344-361`)。因此历史或未来的状态 1/4 记录可能进入“今日接诊台”,并可触发通知、备注、视频和完成。
3. **P0 — 问诊列表的视频门槛读取了错误的状态域。** 基准以 `has_appointment && appointment_status===1` 为唯一可视频条件(`tcm/diagnosis/index.vue:1778-1780`H5 同口径 `index_h5.vue:949-950`)。Python 读取诊单模型的 `status`,并允许 1 或 4`consultations.py:310-317`);模型又优先取根 `status`,而不是根 `appointment_status``core/models.py:362-366,406-414`)。结果是已完成/过号记录可能仍可直呼,合法已预约记录也可能被误禁用。
其余 P1 缺口集中在:完整病例核对与只读详情、诊单编辑、从诊单开方、接诊详情字段/日常记录/备注附件、手机号脱敏、动作权限契约,以及问诊列表的筛选/状态/多挂号语义。详见第 8 节。
## 2. 判定口径与路由范围
状态含义:
- **exact**:端点、关键参数、状态门槛和结果语义均一致;不要求 Qt 与 Web 视觉相同。
- **partial**:已有对应能力,但参数、字段、权限、状态或边界行为不完整。
- **missing**:基准可达的医生核心能力在 Python 中没有入口或 repository 合同。
- **intentionally unsupported**:明确属于 H5 布局、小程序二维码或医助/后台运营角色,当前原生医生桌面选择了不同交互或没有承载;这是审计分类,不代表产品已正式批准删除。
路由证据:
- 管理后台主菜单是服务端动态路由:Vite 收集所有 `views/**/*.vue`,菜单组件名交给 `loadRouteView` 匹配(`D:\web\zyt\admin\src\router\index.ts:9-15,28-69`)。因此接诊台主文件是 `views/patient/reception/index.vue`,问诊列表 PC 主文件是 `views/tcm/diagnosis/index.vue`
- `/tcm/diagnosis/h5` 静态指向 `index_h5.vue``D:\web\zyt\admin\src\router\routes.ts:82-85`)。
- PC 问诊列表的“查看”与双击跳转隐藏路由 `/tcm/diagnosis-readonly?id=...``tcm/diagnosis/index.vue:2165-2172`),静态路由指向 `readonly.vue``router/routes.ts:87-102`)。
- `index.vue.bak` 的后缀不是 `.vue`,不会进入 `import.meta.glob('/src/views/**/*.vue')``add.vue` 没有被现行两个入口导入;两者均未作为基准。PC 虽挂载 `detail.vue`,却没有存活的 `handleDetail` 调用;该弹窗仅由 H5 卡片点击实际触发(`index_h5.vue:121,144,264-268,1013-1015`)。
Python 页面级门控与主入口一致:接诊台使用 `doctor.appointment/lists`,问诊列表使用 `tcm.diagnosis/lists``D:\web\zyt\app\src\doctor_workstation\ui\shell.py:40-47,69-75,248-264`),判定为 **exact**
## 3. 实际可达文件与依赖图
以下只列业务 Vue/TS 和决定行为的共享文件,不展开 Element Plus、基础 popup/upload 等纯框架组件。
### 3.1 接诊台
- `views/patient/reception/index.vue`:队列、轮询、详情、通知、完成、快捷文字备注、通话入口(导入证据 `:191-210`)。
- `views/patient/reception/components/NoteTimeline.vue`:备注文本、舌苔图片、检查报告、单附件删除(`:1-121,125-132`)。
- `views/tcm/diagnosis/components/PatientInfoCard.vue`:脱敏手机号、预约与人员摘要(`:1-41`)。
- `views/tcm/diagnosis/components/PatientCaseCard.vue`:病例全字段和生命体征阈值(`:1-124,128-153`)。
- `views/tcm/diagnosis/components/DailyMatrix.vue``DiagnosisTodoList.vue`:7/30/自定义日期窗、血糖血压/饮食/运动/跟踪备注/待办(`DailyMatrix.vue:317-332,342-470`)。
- `components/chat-dialog/index.vue``ChatMessageItem.vue` 及本地录制/截图/IM 工具:实时聊天与通话、通话落库、房间绑定、结束、可选本地录制;API 导入见 `chat-dialog/index.vue:129-175`
- `views/tcm/diagnosis/edit.vue` 及其下述诊单详情依赖:`DailyMatrix.vue``CaseRecordList.vue``CallRecordPanel.vue``RecordingPlaybackBlock.vue``RecordingVideoPlayer.vue``ImChatRecordPanel.vue``AssignLogPanel.vue``AppointmentRecordPanel.vue``NoteTimeline.vue``TrackingNoteTimeline.vue``PatientOrderList.vue``PrescriptionOrderDetailDrawer.vue``components/tcm-prescription/index.vue`(导入证据 `edit.vue:788-806`)。
### 3.2 问诊列表
- `views/tcm/diagnosis/index.vue`:PC 列表、全部筛选/角标、行操作、20 秒静默轮询(导入及异步组件 `:727-746`)。
- `views/tcm/diagnosis/index_h5.vue`:静态 H5 路由;复用同一 edit/detail/prescription/appointment/watch-call 组件(`:588-630`)。原生 Python 不需要复刻 H5 布局,标为 **intentionally unsupported**;但它证明的业务状态和动作边界仍计入对比。
- `views/tcm/diagnosis/readonly.vue`:PC 隐藏详情路由;复用 PatientInfo/Case、DailyMatrix、备注、通话回放、IM、指派、挂号、业务订单组件(`:151-167`)。
- `views/tcm/diagnosis/detail.vue`H5 卡片的简版诊单详情(`:1-165`)。
- `views/tcm/diagnosis/edit.vue`:新增/编辑/只读抽屉及所有扩展 Tab。
- `components/tcm-prescription/index.vue`:按诊单开方、查已有处方、作废、处方库导入和 PDF。
- `views/tcm/diagnosis/appointment.vue`:预约、排班、号源、渠道和今日重复挂号边界。
- `views/tcm/diagnosis/components/AssistantWatchCallDialog.vue`:医助 TRTC 只拉流旁观(`:23-26,120-219`)。
- 决定字段与边界的 TS`hooks/usePaging.ts``:13-58`)、`utils/perm.ts``:3-17`)、`install/directives/perms.ts``:13-32`)、`utils/diag-display.ts``utils/blood-thresholds.ts``utils/diabetes-discovery-display.ts`
## 4. 接诊台逐项差异
| 项目 | 基准证据 | Python 证据 | 判定 | 差异/影响 |
|---|---|---|---|---|
| 页面权限 | 动态菜单页面权限为 `doctor.appointment/lists` | `shell.py:40-47,248-264` | exact | 页面级一致。 |
| 状态队列 | 仅 status 1“待接诊”和 4“已过号”(`index.vue:33-45,235` | 两个 QTab 映射 1/4`reception.py:152-156,334-336`) | exact | 状态集合一致。 |
| 今日范围 | 每次请求带当天 `start_date/end_date``index.vue:296-305` | 未发送日期(`reception.py:344-361` | missing / P0 | 非今日记录污染工作台并暴露写动作。 |
| 搜索 | `patient_name`,清空也自动搜索(`index.vue:20-32,296-305,431-436`) | UI 只在回车/搜索按钮触发;repository 将 `keyword` 改为 `patient_name``reception.py:159-168,352-360`; `repository.py:231-236` | partial | 请求字段一致;清空不自动刷新。 |
| 分页与数量 | page size 15、另一状态独立 count、无限滚动、去重合并(`index.vue:212-213,296-318,359-379,606-620`) | 固定第一页 50 条,仅显示已加载条数(`reception.py:344-386` | partial | 50 条以上不可见;没有 waiting/passed 总数或加载更多。 |
| 轮询 | 5 秒链式轮询、倒计时、队列/详情/双 count、防重入、页面隐藏暂停/恢复立即刷新(`index.vue:254-259,539-592`) | 8 秒 QTimer,只刷新当前队列;页面 QWidget 隐藏时停(`reception.py:138-140,663-672` | partial | 无倒计时/另一状态 count;应用最小化不等价于 `document.hidden`;周期不同。 |
| 异步选人 | 每次选人递增 seq、清空详情、结果校验 seq + selected id`index.vue:398-418`) | 请求进行中拒绝新选人;旧结果仍应用(`reception.py:406-418,437-447` | missing / P0 | 可显示旧患者详情并对新选择行执行其他动作。 |
| 队列行字段 | id、patient/diagnosis/doctor/assistant、日期时间、性别年龄、status、处方、remark`index.vue:215-233`);实际显示姓名、性别年龄、过号、时间、医助(`:57-90` | 显示同一核心子集(`reception.py:50-95`) | exact | 行上显示字段基本一致。 |
| 聚合详情端点 | GET `/doctor.appointment/reception?id``api/patient.ts:9-12` | `get_reception()` 同端点/参数(`repository.py:242-250`) | exact | 传输合同一致。 |
| 患者/病例字段 | PatientInfo 显示脱敏电话、性别年龄、身高体重地区、预约、医生/客服、状态/开方、remark(`PatientInfoCard.vue:3-25`);PatientCase 显示基本信息、生命体征、糖尿病史、现病史、既往/其他病史、处方意见(`PatientCaseCard.vue:8-124`) | 仅预约时间/电话/医生/医助,加主诉、临床诊断、舌脉、治则摘要和开方提示(`reception.py:241-285,471-516` | partial / P1 | 大量临床字段缺失;无法达到基准的接诊前核对深度。 |
| 手机脱敏 | 两张卡都调用 `maskPhone``PatientInfoCard.vue:8`; `PatientCaseCard.vue:15` | 直接显示 `patient_phone/phone/phone_masked` 中第一个非空值(`reception.py:478-480` | missing / P1 | API 若下发明文即展示;未受 `tcm.diagnosis/phonePlain` 控制。 |
| 医生备注读取 | 聚合详情 `doctor_notes`,编辑/只读还会 GET `/doctor.appointment/doctorNotes` 并保留详情兜底(`readonly.vue:177-217`; `api/patient.ts:29-32` | 仅消费聚合详情中的 `doctor_notes/notes``reception.py:518-548`);Remote 无 `doctorNotes` 方法 | partial | 无独立补拉;备注元数据只显示 creator/time,忽略 `note_date` 语义。 |
| 文字备注写入 | `diagnosis_id + trimmed content`,最多 500 字,空值/无诊单拒绝(`index.vue:175-181,514-536` | 同端点和空值/诊单检查(`reception.py:575-601`; `repository.py:257-272`),但 QTextEdit 无 500 字限制 | partial | 端点 exact;客户端长度边界缺失。 |
| 舌苔/报告 | NoteTimeline 支持最多 99 个图片/文件、新增后刷新、单附件确认删除(`NoteTimeline.vue:3-40,52-101,195-227,260-268`; `api/patient.ts:19-40` | repository 可发送 `tongue_images/report_files`,但 UI 不展示附件、无上传/预览/删除入口(`repository.py:257-272`; `reception.py:524-548` | missing / P1 | 核心检查资料不可见、不可维护。 |
| 日常记录 | 只读 DailyMatrix,按诊单/患者加载(`index.vue:129-139`);7/30/自定义窗、trackingWindow/trackingNotes`DailyMatrix.vue:410-458,550-564,838-875` | 无 UI、模型和 repository 合同 | missing / P1 | 接诊时看不到血糖血压、饮食、运动、跟踪备注及阈值。 |
| 编辑病历 | `tcm.diagnosis/edit` 权限后打开 `edit.vue``index.vue:155-161,474-480` | 无按钮、无 `tcm.diagnosis/detail/edit` repository 方法 | missing / P1 | 接诊工作台不能维护病历。 |
| 通知医助 | POST `/doctor.appointment/notifyAssistant {id}`,防重入、成功后刷新队列与详情(`index.vue:460-471`; `api/patient.ts:14-17`) | 端点/防按钮重入一致,但只 toast、不刷新(`reception.py:558-573`; `repository.py:252-255` | partial | 后端动作 exact;页面不会立即反映服务端副作用。 |
| 完成接诊 | 仅 apt.status 1/4,权限 `doctor.appointment/complete`,不可逆确认,POST `{id: apt.id}` 后清选中并重载(`index.vue:162-171,492-511`; `api/doctor.ts:84-87` | 同权限、确认和端点(`reception.py:326-328,610-641`; `repository.py:274-277` | partial | 正常队列内近似 exact;受“非今日队列”和错患者详情竞态放大风险,UI 本身未再校验状态。 |
| 通话标识 | 缺 patient_id 拒绝;`diagnosis_id = row.diagnosis_id || row.id``index.vue:438-453` | app 同时要求 patient/diagnosis idpayload 优先真实 diagnosis_id`reception.py:643-661`; `app.py:496-537` | exact | ID 未互换。 |
| 通话生命周期 | ChatDialog 调 get signature、start、bind、end`chat-dialog/index.vue:641-695,996-1032,1089-1096` | Remote 同四端点(`repository.py:443-483`),FIFO 生命周期按 start→bind→end`video/lifecycle.py:184-209,211-263,265-292` | exact(核心) | Python 的核心通话落库顺序一致。 |
| 通话扩展 | ChatDialog 还有 IM、截图写备注、云/本地录像上传(`chat-dialog/index.vue:129-175,241-265,1276-1301` | 原生 companion 未提供这些管理后台附属入口 | partial | 核心视频可用;聊天、截图备注和本地录像回填不等价。 |
### 接诊台权限差异
- 基准悬浮备注/编辑/完成分别使用 `doctor.appointment/addDoctorNote``tcm.diagnosis/edit``doctor.appointment/complete``patient/reception/index.vue:146-171`)。Python 已实现备注与完成门控,但编辑缺失(`reception.py:322-331`)。
- 基准队列行“通知医助”和“发起通话”没有 `v-perms``index.vue:75-90`)。Python 额外要求 `doctor.appointment/notifyAssistant``tcm.diagnosis/videoQr``reception.py:322-325`)。后者在基准中是“小程序视频二维码”权限,不是直呼权限;这可能把拥有接诊页但没有二维码权限的医生挡在视频之外,判为 **partial / P1**
- NoteTimeline 的“添加文字备注”检查 addDoctorNote 权限,但两个媒体 picker 和删除图标只受 readonly 控制(`NoteTimeline.vue:3-40,64-99,150-151`)。这是基准自身的权限不对称,不能据此推导 Python 应无条件开放媒体写入。
## 5. 问诊列表逐项差异
### 5.1 列表、筛选与分页
| 项目 | 基准 | Python | 判定 |
|---|---|---|---|
| 列表端点 | GET `/tcm.diagnosis/lists``api/tcm.ts:3-6` | 同端点(`repository.py:414-440` | exact |
| 分页 | `usePaging` 默认 page size 15,发送 `page_no/page_size``usePaging.ts:13-21,25-48` | page size 20、PageResult 容错(`consultations.py:93-95,264-300` | partial |
| 默认范围 | PC/H5 均默认 `appointment_date=today``index.vue:2174-2182`; `index_h5.vue:1427-1437`) | 默认日期同为今天;Remote 在起止相等时转换为 `appointment_date``consultations.py:129-141,248-251,264-282`; `repository.py:420-424` | exact(日期) |
| 默认状态 | 基准默认今天的所有挂号语义,不额外传 appointment status | Python status 下拉默认 1,并转成 `appointment_status=1``consultations.py:121-128,276-279`; `repository.py:429-434` | partial / P1 |
| 关键词 | `keyword`,提示姓名/手机号(`index.vue:72-75,773-775` | 同字段(`consultations.py:116-120,276` | exact |
| 日期快捷项 | 前天、昨天、今天、明天、后天、全部;另有待预约、已完成、待分配及 9 路角标(`index.vue:963-1085`) | 单个起止日期控件和“今天”;无角标 | partial |
| 挂号/确认 | `has_appointment` 0/1`diagnosis_confirmed` 0/1`index.vue:78-97,804-833` | 无这两个筛选 | missing |
| 诊断/证型/医助 | 字典筛选(`index.vue:104-113,1158-1188` | 无 | missing |
| 最近挂号 | 起止日期 + 渠道,且与 appointment_date/未挂号互斥(`index.vue:114-149,1197-1228` | 多日 start/end 被隐式映射为 latest appointment 起止,但无渠道、无互斥提示(`repository.py:420-428` | partial |
| 最近指派 | 起止日期(`index.vue:124-133,1203-1216,1230-1232` | 无 | missing |
| 未服务排序 | `sort_unserved_days=asc/desc``index.vue:297-304,1234-1248` | 无 | missing |
| 待分配宽搜 | 有关键词时只保留 page、pending_assign 和 pending_assign_keyword,主动清空其他筛选(`index.vue:836-861,923-954` | 无 | intentionally unsupported(医助分配) |
| 定时刷新 | PC 20 秒且 document.hidden 时跳过(`index.vue:1098-1105`);H5 15 秒(`index_h5.vue:1418-1437` | 20 秒,页面 QWidget 隐藏时停(`consultations.py:238-240,325-334` | exactPC 周期)/ partial(可见性语义) |
| 路由 query id | `?id=` 会在异步 edit ref 就绪后打开编辑(`index.vue:1343-1357,2184-2188` | 无 deep-link | missing |
Python 的“状态下拉”不是基准筛选:它提供待接诊/取消/完成/过号(`consultations.py:121-127`),Remote 再把 3 映射为 `completed_appointment=1`,其他值映射成 `appointment_status``repository.py:429-434`)。基准 PC 没有该下拉;“已完成”定义为至少有一条 appointment.status=3`index.vue:1007-1020`),取消状态也不在展示映射中。因此这里只能判 **partial**,不能视为同一合同。
### 5.2 行字段与状态映射
基准 PC 行字段:
- 诊单 ID/NEW、患者姓名、性别年龄(`index.vue:191-218`)。
- `appointments[]` 多挂号;回退到 appointment_id/status/doctor/time 主字段;最近渠道(`:219-261,1747-1757`)。
- 确认状态来自 `DiagnosisViewRecord.some(is_confirmed == 1)``:262-267,1313-1317`)。
- 复诊时间/医生/处方已作废、助理、是否开方(`:268-296`)。
- `unserved_days` 与 last blood tooltip,阈值 null 灰、>=7 红、3-6 橙、<=2 绿(`:297-317,1319-1329`)。
- `video_call_hint.state` 映射 none/live/pending_room/label`:318-341,1373-1427`)。
Python 只显示预约时间、患者、状态、联系方式、医助、确认、处方、remark(`consultations.py:183-225`),判 **partial / P1**。具体错误为:
1. `Consultation` 模型没有正式字段 `has_appointment``appointment_status``appointments`、followup、unserved、last blood、video hint;虽保留 raw,但 UI 不读取这些字段(`core/models.py:322-350`)。
2. 模型从根 `status` 优先取值,只有根 status 缺失才使用首个 nested appointment.status`:362-366,406-414`)。基准明确把 appointment status 放在 `appointment_status`/`appointments[].status`,并将诊单本身的 status 用作启用状态。这是 P0 视频门槛错误的根因。
3. Python `diagnosis_confirmed` 属性只来自根 `confirmed/diagnosis_confirmed``:413-423`),不计算 `DiagnosisViewRecord`;存在把已确认显示成待确认的风险。
4. Python 联系方式列直接显示 `patient_phone/phone/phone_masked``consultations.py:195-201`),而基准 PC/H5 列表不展示手机号明文;编辑页仅 `tcm.diagnosis/phonePlain` 可保持明文(`edit.vue:822-856,1263-1277`)。判 **missing / P1(隐私)**
挂号状态基准只有:1=已预约、3=已完成、4=已过号(`index.vue:1732-1745`);取消仅允许 status 1/4,status 3 明确拒绝,且多挂号必须按具体 appointment id 操作(`:1782-1857`)。Python 把 2 显示为“已取消”,并把 4 也设为可直呼(`consultations.py:53-58,310-317`),不符合基准。
### 5.3 详情、编辑与操作
| 能力 | 基准行为与证据 | Python | 判定 |
|---|---|---|---|
| 只读病例详情 | PC 查看/双击需 `tcm.diagnosis/readonlyDetail`,路由 query id;加载 `/tcm.diagnosis/readonlyDetail` + doctorNotes`index.vue:345-354,2165-2172`; `readonly.vue:172-240`; `api/tcm.ts:8-11`) | 表格双击直接发起视频;无详情页/端点(`consultations.py:227-228,319-323` | missing / P1 |
| H5 简版详情 | 卡片头/体打开 `detail.vue`GET `/tcm.diagnosis/detail`,显示基础、病史、舌苔/报告、症状舌脉治则处方医嘱状态(`index_h5.vue:121,144`; `detail.vue:10-122,157-165` | 无 | missing |
| 完整诊单编辑 | `tcm.diagnosis/edit`GET detail 后 POST edit;新增用 add;手机号/身份证唯一性检查(`edit.vue:1263-1375`; `api/tcm.ts:44-61,77-90` | 无 repository 方法或 UI | missing / P1 |
| 编辑字段 | patient/id card/phone/gender/age/marital/height/weight/region/BP/glucose/type/status/source/card/current medicine/local diagnosis、全部现病史/既往史/其他史/symptoms/remark`edit.vue:86-624,916-970` | Consultation 只存列表摘要(`core/models.py:322-350` | missing / P1 |
| 编辑验证 | 姓名、手机、性别、年龄、空腹血糖、诊断类型、当地医院必填;手机号正则;身份证 18 位正则;病史发现最多 50(`edit.vue:1026-1175` | 无 | missing |
| 开方/查看 | `tcm.diagnosis/kaifang`;审核通过且未作废显示“查看”,否则“开方”;传 diagnosis id + appointment id`index.vue:354-363,1671-1694`) | 无问诊行开方入口;只有独立已开处方列表/详情 | missing / P1 |
| 处方边界 | 先按 appointment 查已有;临床诊断、至少一味药、药名/正剂量、无重复、手写签名必填;可作废但有业务订单时禁止(`tcm-prescription/index.vue:1829-1922,2219-2328` | Remote 仅 list/detail issued prescription`repository.py:354-397`),无 add/getByAppointment/void | missing / P1 |
| 预约 | `tcm.diagnosis/guahao`;医生排班/未来 7 天/可用时段;今天过去时段禁用;渠道必填;指定自媒体渠道补充必填;今天已有 status1/4 禁止重复约今天(`appointment.vue:239-420,423-448,535-619,669-724` | 无 | intentionally unsupported(后台/医助调度) |
| 取消挂号 | status1/4 可取消,3 拒绝,多 appointment 精确到子记录(`index.vue:1782-1857`; `api/doctor.ts:59-67` | 无 | intentionally unsupported(后台/医助调度) |
| 指派/取消指派 | `tcm.diagnosis/assign`;单条/批量、继承标志、取消传 assistant_id=0`index.vue:1532-1661` | 无 | intentionally unsupported(医助管理) |
| 视频二维码/确认二维码 | 仅 `has_appointment && appointment_status=1`;需 weapp config;权限 `videoQr`/`guahao``index.vue:390-391,1778-1780,1964-2055` | 用同一 `videoQr` 权限启动原生直呼(`consultations.py:102-106` | intentionally unsupported(二维码)+ partial(权限/状态复用错误) |
| 医助旁观 | 仅当前被指派医助 + `tcm.diagnosis/watchCall`pending_room 显示但不能进,live 才进;TRTC 只拉流(`index.vue:1380-1413`; `AssistantWatchCallDialog.vue:120-219`) | 无旁观角色/入口 | intentionally unsupported(医助角色) |
| 挂号日志 | `tcm.diagnosis/guahaoLogList`,空数组容错,显示 action/operator/summary/time`index.vue:1859-1897` | 无 | intentionally unsupported |
| 补身份证 | 15/18 位前端校验,POST fillIdCard 后自动年龄(`index.vue:1918-1961` | 无 | intentionally unsupported(后台资料维护) |
| 企微记录 | records/contact 并行、20 条分页、文字 note 新增/删除(`index.vue:2066-2160` | 无 | intentionally unsupported(后台企微归档) |
| 创建订单 | order_type、amount、remark 后生成订单二维码(`index.vue:1430-1515` | 无 | intentionally unsupported(后台运营) |
### 5.4 只读详情的权限化子区块
基准 `/tcm/diagnosis-readonly` 不是一个简单摘要,它按权限加载:
- `tcm.diagnosis/dailyRecord`DailyMatrix`readonly.vue:42-54,183`)。
- `doctor.appointment/addDoctorNote`NoteTimeline 的可写版本在 editreadonly route 固定只读(`readonly.vue:56-70`)。
- `tcm.diagnosis/patientOrders`:业务订单(`:74-88`)。
- `tcm.diagnosis/huifang`:通话录制回放(`:90-101`)。
- `tcm.diagnosis/chat`IM 记录(`:103-113`)。
- `tcm.diagnosis/assign``tcm.diagnosis/detail`:指派日志(`:115-131`)。
- `doctor.appointment/lists`:挂号记录(`:133-146`)。
Python 问诊列表没有选中详情容器,因此上述全部为 **missing**;其中病例、日常记录、备注、挂号记录属于医生核对链路,列 P1;订单、指派和后台 IM 可按角色继续列 intentionally unsupported。
## 6. API 覆盖矩阵
此表只包含从上述可达 view/component 实际导入的业务 API;端点定义证据集中在 `D:\web\zyt\admin\src\api\patient.ts:3-40``api\doctor.ts:49-87``api\tcm.ts:3-109,202-310,313-372,591-648`
| API 组 | 管理后台可达用途 | Python 状态 |
|---|---|---|
| `doctor.appointment/lists` | 接诊队列、预约/挂号记录、最近就诊/今日重复检查 | **exact 端点 / partial 参数**;接诊台漏 today |
| `doctor.appointment/reception` | 接诊聚合详情 | **exact** |
| `notifyAssistant` | 通知医助 | **exact 端点 / partial 刷新与权限** |
| `addDoctorNote` | 文字、舌苔、报告、通话截图 | **partial**;Remote 参数齐,UI 只有文字 |
| `doctorNotes`, `deleteDoctorNoteImage` | 备注补拉、附件删除 | **missing** |
| `doctor.appointment/complete` | 完成接诊 | **exact 端点 / partial 上下文边界** |
| `tcm.diagnosis/lists` | 问诊列表、全部角标 | **exact 端点 / partial filters/model** |
| `readonlyDetail`, `detail`, `add`, `edit`, `delete`, `checkPhone`, `checkIdCard`, `fillIdCard` | 详情与资料维护 | **missing** |
| `assign`, `assignLogList`, `getAssistants`, `watchCall` | 医助分配与旁观 | **intentionally unsupported** |
| `trackingWindow`, `trackingNotes`, `addTrackingNote` | 日常记录/备注窗 | **missing** |
| blood/diet/exercise add/edit | 编辑页日常记录 | **missing** |
| diagnosisTodo lists/add/cancel | 日常记录待办 | **missing** |
| `getCallSignature`, `startCall`, `bindCallRoom`, `endCall` | 原生实时视频核心 | **exact** |
| `getCallRecords`, `attachLocalCallRecording`, `createManualCallRecord` | 回放与人工补传 | **missing**(可选扩展) |
| `getImChatMessages`, `triggerImChatSync` | IM 归档 | **missing** |
| `prescription/listByDiagnosis`, `getByAppointment`, `add`, `void` | 病例处方与开方 | **missing / P1** |
| `prescription/detail`, `prescription/lists` | 查看已开处方 | **exact**,但只在独立页面 |
| `prescriptionLibrary/lists` | 开方时导入本人模板 | **exact endpoint**,但问诊开方 UI 缺失 |
| availableSlots/create/roster/lists | 预约 | **intentionally unsupported** |
| generateMiniProgramQrcode/generateOrderQrcode + weapp config | 二维码 | **intentionally unsupported**,由原生直呼替代 |
| WeChat records/contact/add/delete | 企微归档 | **intentionally unsupported** |
| prescriptionOrder lists/detail/logs | 只读业务订单 | **intentionally unsupported** |
Remote 的相关实现块只覆盖 appointment/reception/actions`D:\web\zyt\app\src\doctor_workstation\services\repository.py:226-277`)、diagnosis list 与四个 call endpoint`:414-483`);因此上述“missing”不是 UI 隐藏但 repository 已具备,而是端到端合同确实不存在。
## 7. 权限码对照
### 基准中实际出现
- 页面/读取:`doctor.appointment/lists``tcm.diagnosis/lists``tcm.diagnosis/readonlyDetail``tcm.diagnosis/detail``tcm.diagnosis/dailyRecord``tcm.diagnosis/patientOrders``tcm.diagnosis/huifang``tcm.diagnosis/chat`
- 接诊写动作:`doctor.appointment/addDoctorNote``doctor.appointment/complete``tcm.diagnosis/edit`
- 问诊列表动作:`tcm.diagnosis/add``edit``delete``assign``kaifang``guahao``videoQr``watchCall``guahaoLogList``order`
- 编辑扩展:`tcm.diagnosis/phonePlain``tcm.diagnosis/chufang``tcm.diagnosis/setRevisitSlotStartOffset``tcm.prescriptionOrder/detail`
证据:接诊 `patient/reception/index.vue:146-171`;问诊 PC `tcm/diagnosis/index.vue:318-400`readonly `readonly.vue:42-146`edit `edit.vue:674-748,822-856`PatientOrderList `components/PatientOrderList.vue:20-33,99-114`
### Python 实际门控
- 页面:两个页面码 **exact**`shell.py:40-75`)。
- 接诊:`notifyAssistant``videoQr``complete``addDoctorNote``reception.py:322-331`);缺 edit。
- 问诊:只有 `videoQr``consultations.py:102-106`);其余动作根本没有 UI。
- Python PermissionSet 本身支持 `*`、all/any`core/permissions.py:60-104`),问题在页面选择了哪些码,而不是权限容器能力。
特别注意:Web 的 `v-perms` 是数组 OR`install/directives/perms.ts:13-32`),`hasPermission()` 工具是数组 AND`utils/perm.ts:3-17`);当前页面绝大多数调用只有一个码,唯一显式 OR 的 assign/detail 是两个独立调用(`readonly.vue:117-120`),所以本次差异不受二者实现差别影响。
## 8. P0 / P1 缺口清单(按严重度排序)
### P0
1. **RACE-RECEPTION-01:快速切换患者导致详情与选择错配。** 修复验收应覆盖 A 请求未返回时选 B,A/B 任意顺序返回后 UI、备注 diagnosis_id、通知/完成 appointment_id 和视频 payload 必须全部指向 B;详见第 1、4 节证据。
2. **SCOPE-RECEPTION-02:接诊列表漏 `start_date=end_date=today`。** 必须在 UI 或 repository 的专用 reception query 中固定今日,不能让通用 appointment list 隐式决定。
3. **STATE-CALL-03:问诊视频使用 `status` 而非 `has_appointment + appointment_status`,且错误允许 4。** 模型和 UI 都需改;只修 UI 的字段名仍会被当前 dataclass 的根 status 优先级遮蔽。
### P1
1. **DETAIL-04:问诊列表没有 `readonlyDetail`/详情入口,双击反而直呼。** 医生无法先核对完整病例、日常记录、备注和挂号历史。
2. **EDIT-05:接诊台和问诊列表均无诊单详情/edit 合同。** 基准接诊台明确提供“编辑病历”。
3. **RX-06:没有从诊单开方/查看/作废的工作流。** 独立“已开处方”页面不能替代 `diagnosis_id + appointment_id` 上下文开方。
4. **RECEPTION-DETAIL-07:接诊详情只显示摘要,缺完整 PatientCase、日常记录、备注附件和附件删除。**
5. **PRIVACY-08:接诊和问诊列表可能直接显示明文手机号,未遵守基准的 mask/phonePlain 边界。**
6. **PERM-09:用 `tcm.diagnosis/videoQr` 保护原生直呼、用未在基准按钮上出现的 `doctor.appointment/notifyAssistant` 保护通知,可能让合法接诊医生缺动作;同时缺 `tcm.diagnosis/edit` 入口。** 需要后端权限清单确认专用 call permission,而不是继续借用二维码码。
7. **MODEL-10Consultation 未正式解析 appointment_status、has_appointment、多挂号、DiagnosisViewRecord、unserved/video hint 等;当前确认和状态显示不可靠。**
8. **FILTER-11:问诊筛选缺 has_appointment、confirmed、诊断类型、证型、医助、最新挂号渠道、最新指派、未服务排序及顶部 count;Python 自创 status 下拉又改变默认结果集。**
9. **BOUNDARY-12:文字备注没有 500 字客户端限制;通知成功不刷新;完成动作未在 handler 中二次验证 1/4。**
不列 P0/P1 的 intentionally unsupported 项:H5 响应式布局、小程序/订单二维码、批量指派、医助旁观、企微后台归档、后台订单创建和排班预约。若产品决定医生桌面也承担医助/运营职责,应把这些重新分类为 missing 并另立需求。
## 9. 基准自身的可疑点(不可静默照搬)
以下均是当前 `admin/src/views` 的真实行为,本报告仅记录,不用其他资料修正它:
1. 接诊台“编辑病历”把 `diag.patient_id` 传给 `edit.open('edit', id)``patient/reception/index.vue:474-476`),而 edit 将该 id 直接传给 `/tcm.diagnosis/detail``tcm/diagnosis/edit.vue:1263-1265,1288-1302`);问诊列表传的则是 `row.id``index.vue:1667-1669`)。这是明显 ID 口径冲突。
2. PC/H5 的“视频二维码”请求把 `diagnosis_id` 赋成 `row.appointment_doctor_id``index.vue:1987-1993`; `index_h5.vue:1262-1268`),而“诊单二维码”正确使用 `row.id``index.vue:2037-2042`)。Python 原生直呼当前没有复制此错误。
3. `PatientCaseCard` 的 consultation_type 三元两边都返回“复诊”(`PatientCaseCard.vue:148-151`)。
4. 接诊行通知/通话无权限指令、NoteTimeline 媒体写入未复用 addDoctorNote 门控,见第 4 节;应先确认服务端授权模型,再决定桌面行为。
## 10. 建议的最小合同测试
后续实现至少应加入不发网络的合同测试:
1. Reception request 必含 status、today start/end、patient_name、page_no/page_size。
2. A/B 详情乱序返回不会产生跨患者详情或写动作目标。
3. Consultation row 的 `status=1, appointment_status=3` 不可视频;`status=1, has_appointment=1, appointment_status=1` 可视频;status 4 一律不可从问诊列表直呼。
4. `DiagnosisViewRecord=[{is_confirmed:1}]` 显示已确认;多 appointments 保留并正确挑选 active appointment。
5. 无 phonePlain 权限时列表和详情只出现掩码。
6. readonlyDetail、edit 和 prescription 动作分别由准确权限码控制;缺码时不创建可调用控件。
7. 备注 501 字在客户端拒绝;媒体 note payload 保留 tongue/report 数组;删除附件的三字段合同固定。