# 接诊台 / 问诊列表:管理后台到医生桌面的逐项一致性审计 审计日期: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 id;payload 优先真实 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`) | exact(PC 周期)/ 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 的可写版本在 edit;readonly 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-10:Consultation 未正式解析 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 数组;删除附件的三字段合同固定。