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

23 KiB
Raw Blame History

患者列表、动态菜单与权限一致性审计

审计日期:2026-08-10
管理端唯一事实来源:D:\web\zyt\admin\src\views\**
Python 对照范围:D:\web\zyt\app\src\doctor_workstation\**

1. 结论摘要

审计项 结论 摘要
“患者列表”真实业务页面识别 EXACT 医生工作站语义下的真实页面是 first_visit/my_patients/index.vue,不是同样含“患者列表”字样的 doctor/progress.vue
Python 患者列表基础查询 PARTIAL 使用同一“我的患者”业务语义、相同五种状态和服务端数据范围,但只实现了主列表的子集。
患者详情与历史关联 MISSING Python 没有进入诊单编辑/只读详情链,也没有详情请求;右侧内容直接取列表行及其 raw
患者订单管理、面诊进度 MISSING 管理端同一路由内的两个完整工作区在 Python 中均不存在。
五模块主导航 PARTIAL Python 解析并保存 mySelf.menu,登录时只判断菜单非空;Shell 随后忽略菜单树,使用固定五项加扁平权限码。
按钮权限 PARTIAL 多数已有写操作有权限门禁,但存在 canonical 权限码偏差、额外别名放行、患者动作大量缺失;处方库所有者判断存在 fail-open。
医生角色/部门数据范围 PARTIAL 患者主列表的请求未注入任意医生 ID,保持服务端权威范围;但 Python 丢弃 extend.scope、汇总和 ownership 模式,且缺少订单/进度工作区。

判定口径:

  • EXACT:当前证据范围内,业务入口、条件、权限或数据语义一致。
  • PARTIAL:主链存在,但字段、筛选、动作、菜单约束或范围提示不完整/不一致。
  • MISSING:管理端已存在的业务能力,在 Python 中未找到入口或调用链。

2. 证据边界

  1. 本报告没有读取 D:\web\zyt\admin\src\views 之外的管理端文件。因此可以确认 Vue 页面实际引用的 API 函数名、参数和交互,但不能读取 @/api/** 的实现来反推真实 HTTP URL。
  2. views/** 内没有 auth.admin/mySelf 调用或该接口返回的真实菜单 JSON。五个主页面的 paths/component/perms/sort/is_show/is_disable 精确菜单记录无法从本次允许范围内恢复。
  3. 管理端的菜单管理页证明动态菜单记录至少分别保存“路由路径”“组件路径”“权限字符”“是否显示”“菜单状态”:D:\web\zyt\admin\src\views\permission\menu\edit.vue:41-94,125-150;其中隐藏菜单仍可访问的语义写在 D:\web\zyt\admin\src\views\permission\menu\edit.vue:135-137。因此,不能把单一扁平权限码等同于完整路由记录。
  4. 对服务端是否再次执行权限/所有权校验不作推断;本报告的 P0 是客户端授权门禁一致性问题。

3. “患者列表”真实页面识别

3.1 主页面:first_visit/my_patients/index.vue

确认依据:

  • 页面直接声明“一诊 / 我的患者”,并说明“患者、挂号与诊单信息按当前角色和部门数据范围展示”:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:5-10
  • 同一路由内明确包含“患者列表 / 订单管理 / 面诊进度”三个工作区:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:15-20
  • 主表由 myPatientLists 驱动,并异步装载诊单编辑、预约、订单和进度组件:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:330-344,404-409

结论:Python PatientsPage 应对标这一页面及其子工作区,而不应只对标某张通用患者/挂号表。

3.2 排除项:doctor/progress.vue

该文件虽在表头使用“患者列表”,但它是医生面诊进度看板:

  • 页面要求先选择医生,再展示该医生挂号:D:\web\zyt\admin\src\views\doctor\progress.vue:57-88
  • 组件名为 doctorProgressAPI 为 doctorListsappointmentListsD:\web\zyt\admin\src\views\doctor\progress.vue:176-182
  • 它显式传 role_id: 1 拉医生,并用 doctor_id + progress_board=1 拉挂号;注释说明不按医生/医助角色收窄:D:\web\zyt\admin\src\views\doctor\progress.vue:408-448,460

因此该页面是跨医生进度看板,不是“我的患者”主数据页。把它的数据范围套到患者模块会错误扩大语义范围。

4. 管理端患者模块真实合同

4.1 主列表、筛选和服务端范围

能力 管理端证据
关键词 患者姓名、手机号、助理、医生:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:23-43
状态 未预约、待面诊、已完成、已过号:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:346-393
挂号日期 今日、明日、后天、近 7 天、近 30 天、自定义区间:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:46-69,395-401
汇总 今日/明日/后天预约人数来自 pager.extend.summary/datesD:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:72-88,411-416
范围提示 顶部和表格显示 pager.extend.scope.labelD:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:90-96,417
列表字段 患者性别年龄/脱敏手机、归属助理、预约医生及状态、预约时间、复诊次数、确认信息、诊单日期:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:105-148

主列表 API 符号为 myPatientLists,调用参数为 keyword/status_filter/start_date/end_date/pageD:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:330-336,380-408

4.2 行级动作与权限码

动作 管理端权限/条件 证据
编辑诊单 tcm.diagnosis/edit D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:152,418,492-498
只读查看 tcm.diagnosis/readonlyDetail D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:153,419,500-510
预约/取消挂号 tcm.diagnosis/guahao;取消仅状态 1/4 D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:154,179-186,420,681-696
指派/重新指派医助 tcm.diagnosis/assign D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:155-162,421,527-575
补全身份证 复用 tcm.diagnosis/edit D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:163-170,422,578-628
诊单二维码 复用挂号权限,且要求有效挂号、状态 1、医生 ID D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:171-178,630-673

只读诊单不是列表内的摘要面板。页面先从动态路由中查找 meta.perms === 'tcm.diagnosis/readonlyDetail',再携带诊单 ID 跳转:D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:500-510

4.3 订单管理子工作区

  • 数据口径明确为“当前患者范围内”的处方业务订单,且“订单创建人不参与数据归属判断”:D:\web\zyt\admin\src\views\first_visit\my_patients\components\OrderPanel.vue:3-10
  • 支持处方审核、支付单审核、履约状态、关键词与日期筛选:D:\web\zyt\admin\src\views\first_visit\my_patients\components\OrderPanel.vue:14-79
  • 汇总包含订单数、有效金额、待审核、完成/签收、拒收数和拒收率:D:\web\zyt\admin\src\views\first_visit\my_patients\components\OrderPanel.vue:82-123,289-297
  • 列表显示订单/患者/处方与诊单/金额/双审/履约/支付单/助理/医生/创建人:D:\web\zyt\admin\src\views\first_visit\my_patients\components\OrderPanel.vue:125-188
  • 数据由 myPatientOrderLists 分页加载:D:\web\zyt\admin\src\views\first_visit\my_patients\components\OrderPanel.vue:234-244,282-286
  • 订单按钮以 canonical tcm.prescriptionOrder/* 权限和业务状态共同判定;完整矩阵在 D:\web\zyt\admin\src\views\first_visit\my_patients\components\order-actions.ts:32-124,包括详情、编辑、双审及撤回、快递、补支付、完成、退款、撤回、上传药房。

4.4 面诊进度子工作区

  • 服务端通过 extend.schedule_mode 决定“按本人归属”或“与排班合并”,而不是客户端按角色 ID 猜测:D:\web\zyt\admin\src\views\first_visit\my_patients\components\ProgressPanel.vue:7-26,206-214
  • “本人患者”语义、近七日安排、医生聚合、候诊队列均在同一工作区:D:\web\zyt\admin\src\views\first_visit\my_patients\components\ProgressPanel.vue:31-117,120-178
  • API 符号为 myPatientProgressLists,数据范围标签来自 extend.scope.label,本人患者由 row.is_self_patient 标识:D:\web\zyt\admin\src\views\first_visit\my_patients\components\ProgressPanel.vue:185-203,263,308-309
  • 每 15 秒自动刷新:D:\web\zyt\admin\src\views\first_visit\my_patients\components\ProgressPanel.vue:318-325

4.5 详情与历史关联链

管理端患者行会进入诊单编辑或只读详情,而后继续关联:

  • 只读页真实标题为“患者信息详情”,无权限/不存在时明确空态:D:\web\zyt\admin\src\views\tcm\diagnosis\readonly.vue:4-36
  • 基本信息、完整病历、日常记录、医生备注/舌苔/报告:D:\web\zyt\admin\src\views\tcm\diagnosis\readonly.vue:38-72
  • 业务订单、视频回放、聊天、医助指派历史、挂号历史分别按权限展示:D:\web\zyt\admin\src\views\tcm\diagnosis\readonly.vue:74-146
  • 详情由 diagnosisReadonlyDetail({id}) 拉取,备注另用 getDoctorNotes({diagnosis_id}) 补齐:D:\web\zyt\admin\src\views\tcm\diagnosis\readonly.vue:151-188,209-234
  • 编辑页还提供处方病历、订单、视频、聊天、指派与挂号记录分页签:D:\web\zyt\admin\src\views\tcm\diagnosis\edit.vue:655-759
  • 挂号历史不是列表行快照,而是按诊单 ID 单独调用 appointmentLists,携带 diag_scope_relax: 1、最多 500 条:D:\web\zyt\admin\src\views\tcm\diagnosis\components\AppointmentRecordPanel.vue:79-89,139-152
  • 指派历史同样按诊单 ID 调用 tcmDiagnosisAssignLogListD:\web\zyt\admin\src\views\tcm\diagnosis\components\AssignLogPanel.vue:47-57,102-125
  • 业务订单按 context_diagnosis_id + patient_id + scene=diagnosis_edit 独立分页:D:\web\zyt\admin\src\views\tcm\diagnosis\components\PatientOrderList.vue:118-148,170-179

5. Python 患者模块逐项对照

项目 Python 当前实现 判定 说明
页面身份 标题“我的患者”,描述服务端授权范围:D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:84-92 EXACT 对标对象正确。
状态筛选 五种状态一致:D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:94-119 EXACT 状态值与管理端一致。
关键词 Python 提示“姓名、手机号或诊单号”:D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:99-103 PARTIAL 管理端还明确支持助理/医生。
日期与汇总 无日期快捷项、区间和三日汇总 MISSING 管理端证据见 4.1。
表格 患者、性别年龄、电话、进度、医助、最近预约、复诊:D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:132-196 PARTIAL 缺预约医生独立列、确认信息、诊单日期和全部操作。
列表请求 patients(keyword,status,page,page_size)D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:307-326 PARTIAL start_date/end_date;服务层正确映射 status -> status_filter
服务端范围 list_patients 使用 firstvisit.myPatient/lists,不注入医生 IDD:\web\zyt\app\src\doctor_workstation\services\repository.py:398-412 EXACT 保持服务端权威数据范围,没有客户端越权扩大证据。
extend PageResult 完整保留 extendD:\web\zyt\app\src\doctor_workstation\core\models.py:590-598,618-672;页面只读 items/totalD:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:328-335 PARTIAL scope、summary、dates 已被解析却未消费。
患者模型 覆盖主列表核心字段并保留 rawD:\web\zyt\app\src\doctor_workstation\core\models.py:241-318 EXACT 解析层可承载主表数据。
详情 选择行后直接 _render_detail(patient)D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:349-415 MISSING 没有诊单详情请求,不能等价于管理端只读/编辑页。
历史预约 从列表行 appointments 取最多 6 条:D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:417-446 MISSING 管理端按诊单单独请求最多 500 条并显示更完整字段。
患者行动作 无编辑/只读/预约/指派/补身份证/二维码/取消挂号按钮 MISSING D:\web\zyt\app\src\doctor_workstation\ui\pages\patients.py:1-454 没有动作链。
订单/进度工作区 MISSING Python 只有列表与右侧摘要。

补充:get_value 会在 dataclass 属性缺失时回退到 .rawD:\web\zyt\app\src\doctor_workstation\ui\widgets.py:48-66。因此右侧摘要“可能”显示列表响应附带的额外字段,但它仍是列表快照,不是独立详情/历史合同。

6. 五模块动态路由、API 与按钮权限复核

6.1 主路由组件与 Python 导航

模块 管理端实际组件(views 证据) Python 固定导航权限 判定
接诊台 D:\web\zyt\admin\src\views\patient\reception\index.vue:1-18 doctor.appointment/listsD:\web\zyt\app\src\doctor_workstation\ui\shell.py:40-47 PARTIAL
处方库 D:\web\zyt\admin\src\views\consumer\prescription\list.vue:1-4,234-245 tcm.prescriptionLibrary/listsD:\web\zyt\app\src\doctor_workstation\ui\shell.py:48-54 PARTIAL
已开处方 D:\web\zyt\admin\src\views\consumer\prescription\index.vue:1-4,1697-1711 tcm.prescription/listsD:\web\zyt\app\src\doctor_workstation\ui\shell.py:55-61 PARTIAL
患者 D:\web\zyt\admin\src\views\first_visit\my_patients\index.vue:5-20 firstvisit.myPatient/listsD:\web\zyt\app\src\doctor_workstation\ui\shell.py:62-68 PARTIAL
问诊/诊单 D:\web\zyt\admin\src\views\tcm\diagnosis\index.vue:726-738 tcm.diagnosis/listsD:\web\zyt\app\src\doctor_workstation\ui\shell.py:69-75 PARTIAL

这里的 PARTIAL 不是说固定权限码必然错误,而是:在 views/** 证据范围内无法恢复五条真实 mySelf.menu 记录,Python 又没有使用已经解析到的菜单树来做组件/顺序/显示/停用约束。

6.2 mySelf 菜单链

  1. Python 正确请求 auth.admin/mySelf,解析用户、权限与 menuD:\web\zyt\app\src\doctor_workstation\services\repository.py:164-195 —— EXACT
  2. Session 正确保留 menuD:\web\zyt\app\src\doctor_workstation\core\session.py:12-22 —— EXACT
  3. 正式登录只检查 session.menu 非空,然后把权限交给 ShellD:\web\zyt\app\src\doctor_workstation\app.py:413-443 —— PARTIAL
  4. Shell 注册页面时遍历固定 NAVIGATION,只调用扁平 has_permission,没有读取 session.menuD:\web\zyt\app\src\doctor_workstation\ui\shell.py:248-280 —— MISSING(菜单绑定)

实际影响:菜单中任意一项存在即可通过登录守卫;随后五模块的显示、顺序、名称及停用状态均由本地固定表决定。动态路由中的隐藏/停用/组件路径/排序语义没有落地。

6.3 按钮权限与功能矩阵

接诊台 — PARTIAL

  • 管理端备注、编辑病历、完成接诊分别使用 doctor.appointment/addDoctorNotetcm.diagnosis/editdoctor.appointment/completeD:\web\zyt\admin\src\views\patient\reception\index.vue:144-171
  • 管理端队列内“通知医助/发起通话”按钮本身没有 v-permsD:\web\zyt\admin\src\views\patient\reception\index.vue:75-90
  • Python 备注与完成权限一致,但通知增加了 doctor.appointment/notifyAssistant,视频增加了 tcm.diagnosis/videoQr;缺“编辑病历”:D:\web\zyt\app\src\doctor_workstation\ui\pages\reception.py:322-331
  • Python 的通知、备注、完成调用链存在:D:\web\zyt\app\src\doctor_workstation\ui\pages\reception.py:558-641;管理端对应 API 调用见 D:\web\zyt\admin\src\views\patient\reception\index.vue:460-527

处方库 — PARTIAL,含 P0 权限门禁问题

  • 管理端 canonical 按钮码是 wcf.prescription/add|read|edit|deleteD:\web\zyt\admin\src\views\consumer\prescription\list.vue:35-41,83-105
  • 管理端编辑/删除还要求创建人本人,或 root/角色 0、3:D:\web\zyt\admin\src\views\consumer\prescription\list.vue:278-301
  • Python 新增/编辑/删除同时接受 canonical wcf.* 和额外 tcm.prescriptionLibrary/*D:\web\zyt\app\src\doctor_workstation\ui\pages\prescription_library.py:271-279,330-348
  • Python root/角色 0、3 的例外与管理端一致,但当 user_idcreator_id 缺失时直接返回可管理:D:\web\zyt\app\src\doctor_workstation\ui\pages\prescription_library.py:460-472。管理端的 Number(row.creator_id) === Number(user.id) 不会对缺失创建人默认放行。

已开处方 — PARTIAL/MISSING actions

  • 管理端支持业务订单跳转、新增、查看、修正患者、创建订单、编辑、审核、删除,对应权限码见 D:\web\zyt\admin\src\views\consumer\prescription\index.vue:102-117,214-265
  • 业务订单导航依赖动态路由中的 meta.perms === 'tcm.prescriptionOrder/lists',菜单不存在会拒绝跳转并提示:D:\web\zyt\admin\src\views\consumer\prescription\index.vue:1862-1871
  • Python 只实现关键词/审核状态的列表和详情请求:D:\web\zyt\app\src\doctor_workstation\ui\pages\prescriptions.py:91-188,284-340;上述写操作与订单导航均不存在。

患者 — MISSING actions

完整动作矩阵见第 4.2 节;Python 患者页没有对应按钮或权限检查。

问诊/诊单 — PARTIAL/MISSING actions

  • 管理端新增、批量指派、查看、诊单、开方、预约、补身份证、指派/取消指派、视频二维码、确认二维码/取消挂号、挂号日志、订单、删除的权限矩阵见 D:\web\zyt\admin\src\views\tcm\diagnosis\index.vue:154-175,342-405
  • Python 仅保留列表筛选与 tcm.diagnosis/videoQr 视频入口:D:\web\zyt\app\src\doctor_workstation\ui\pages\consultations.py:101-107,110-236,264-323

6.4 权限匹配语义

  • PermissionSet 支持精确码、全局 *prefix/*D:\web\zyt\app\src\doctor_workstation\core\permissions.py:24-84OR 语义见 D:\web\zyt\app\src\doctor_workstation\core\permissions.py:96-104
  • UI has_permission 额外把 /. 互换后尝试匹配:D:\web\zyt\app\src\doctor_workstation\ui\widgets.py:135-180
  • 管理端视图只声明 canonical 形态,例如 tcm.diagnosis/editwcf.prescription/edit。由于本次不能读取管理端权限工具实现,不能证明管理端也接受点/斜杠变体;Python 的变体放行应判为 PARTIAL,不应当作已证明的兼容要求。

7. 医生角色与数据范围

7.1 已证明一致的部分

  • 患者列表:管理端要求按当前角色/部门展示并把最终范围作为 extend.scope.label 返回;Python 调用相同业务域列表,不传任意 doctor_id,所以没有客户端扩大数据集的证据。判定 EXACT(请求边界)
  • 处方库:普通医生只能管理自己创建的模板,root/角色 0、3 才可管理全部。Python 的正常 ID 分支与此一致。判定 EXACT(正常分支)

7.2 部分或缺失

  • Python 不显示 extend.scope.label,用户无法验证当前是本人、部门还是其他服务端范围:PARTIAL
  • Python 缺失患者订单工作区,因而没有落实“当前患者范围内、订单创建人不参与归属”的口径:MISSING
  • Python 缺失面诊进度工作区,因而没有落实服务端 schedule_mode=ownershipis_self_patient 和近七日聚合:MISSING
  • 除患者模块视图明确写出的范围文字外,接诊、处方、诊单视图没有在允许证据范围内定义服务端角色/部门过滤算法。Python 使用同域列表接口且未见额外越权 doctor_id 注入,但无法仅凭 views 宣称五模块的后端数据范围 EXACT;应保留 PARTIAL / 服务端待证

8. P0 / P1 缺口

P0

  1. 处方库所有权校验 fail-open。
    Python 在当前用户 ID 或模板 creator_id 任一缺失时允许编辑/删除:D:\web\zyt\app\src\doctor_workstation\ui\pages\prescription_library.py:460-466;管理端仅允许严格所有者匹配或 root/角色 0、3:D:\web\zyt\admin\src\views\consumer\prescription\list.vue:286-301。应改成缺失即拒绝,并只在明确 root/角色例外时放行。若服务端另有强制校验,实际数据写入风险会降低,但客户端授权偏差仍已成立。

P1

  1. 患者模块只有主表子集。 缺日期筛选、三日汇总、范围标签、确认/诊单日期列和全部行级动作。
  2. 患者详情不是管理端真实详情。 Python 不请求诊单只读/编辑详情,病历摘要与最多 6 条历史预约依赖列表行快照,可能为空或陈旧。
  3. 患者订单管理与面诊进度两个工作区完全缺失。 同时丢失订单数据范围、订单操作权限矩阵、ownership 模式和 15 秒候诊刷新。
  4. Shell 忽略 mySelf.menu 已解析的动态菜单只用于“非空”登录守卫;五模块由固定表和扁平权限决定,未落实组件路径、显示/停用、排序和真实菜单成员关系。
  5. 处方库权限码存在额外别名放行。 Python 接受 tcm.prescriptionLibrary/add|edit|delete,而视图仅证明 canonical wcf.prescription/*;应在拿到真实 mySelf.permissions/menu 样本后收敛。
  6. 通用权限帮助器接受 //. 互换。 该兼容未被 views-only 证据证明,可能让非 canonical grant 意外点亮按钮。
  7. 接诊台按钮矩阵不一致。 Python 比管理端额外门禁通知/视频,同时缺少 tcm.diagnosis/edit 的编辑病历动作。
  8. 已开处方和问诊页均被压缩为只读子集。 管理端已有的新增/编辑/审核/删除/订单/预约/指派等受权操作未落地。

9. 建议的验收优先级

  1. 先修复处方库所有权 fail-open,并用“缺 creator_id、缺当前用户 ID、本人、他人、root、角色 0/3”六组合同测试覆盖。
  2. 让 Shell 以 Session.menu 为主约束,再叠加权限码;不要仅以硬编码五项替代动态菜单。
  3. 将患者页拆成与管理端一致的三个工作区,优先补真实只读详情/历史请求和主表行级动作。
  4. 消费 PageResult.extend.scope/summary/dates/schedule_mode,把服务端权威范围直接呈现给医生。
  5. 获取一份真实医生角色 mySelf 响应样本后,补做菜单 paths/component/perms/is_show/is_disable/sort 的精确合同测试;这一步无法由 views/** 单独完成。