Files
zyt/artifacts/prescription-ai-visibility-frontend-research.md
T
2026-10-17 16:51:48 +08:00

16 KiB
Raw Blame History

已开处方 AI 一致度可见范围:前端只读研究

日期:2026-10-17。范围:桌面 app/、后台 admin/ 的入口、登录元数据、配置承载点和测试;未修改业务代码,未调用任何生产接口。已阅读根目录 AGENTS.md;当前工作区 .trellis/workflow.md 不存在。

主任务已确定实施默认:仅一致度独立授权、保留报告;新独立设置页;角色或个人任一命中允许;空名单全隐藏;root 可管理策略,但查看分数也必须命中名单。API 使用 GET tcm.prescriptionAi/capabilities -> {agreement_visible: bool},statuses/reports 顶层及每批次附该 flag,detail 顶层附 flag;未授权剔除 score/herb_score/comparison,statistics 拒绝。下文早期建议中的命名/承载页/初始行为均以上述约定为准。

结论

  • 截图对应桌面 PrescriptionsPage 的第 12 列 与 AI 一致度。当前列显隐只跟随服务端 AI 总开关 enabled,没有按角色或个人控制一致度的能力。
  • 现有权限只拆分 statuses/reports/detail/statistics/regenerate/retry/review。reports + detail 共同允许打开报告,不区分“能看报告”和“能看分数”。单靠收回现有权限无法实现“保留 AI 报告、隐藏一致度”。
  • 仅隐藏列表列不完整:报告顶部双模型分数、逐味原文、总览结论、历史趋势、医生统计也展示一致度。
  • 桌面已经从 auth.admin/mySelf 得到用户 ID、多个角色 ID、root 和权限集合;但运行中的会话权限不适合单独承担可见范围判定。推荐服务端每次读 AI 数据返回当前账号的明确 capability,并在响应中屏蔽未授权分数;桌面统一使用这个判定渲染。
  • 后台没有现成处方 AI 配置页。最小配置入口可放现有“医生工作站”设置页单独配置区;该页目前专门管理升级包,建议新建独立读写配置接口,而不是把授权策略挂到其免登录更新检查响应。

桌面入口与显示面

位置 当前行为 仅一致度控制的接入点
app/src/doctor_workstation/ui/pages/prescriptions.py:1050 顶部 AI 一致度统计;当前只查 tcm.prescriptionAi/statistics 叠加一致度 capability;响应拒绝时清空既有统计
同文件 :1200、:1583、:1765、:1773 工具栏 AI 报告、每行报告按钮、右键 AI 报告 / 逐味对照 若保留报告应继续可用;文案可缩成 AI 报告,避免暗示全部对比可见
同文件 :1255–:1273 第 11 列 AI 状态、第 12 列与 AI 一致度;两列初始隐藏 分离两列显隐,不能继续用 _set_ai_columns 同时切换
同文件 :1289、:1344 单击两列打开同一报告;自定义 cell widget 的事件过滤也处理两列 隐藏分数时保留状态打开报告的行为
同文件 :1647–:1762 只给可见处方批量拉 statuses,缓存后渲染;enabled 同时开启两列 读 capability、清掉旧第 12 列文本/tooltip/accessible text 及缓存中的分数,保留状态轮询
app/src/doctor_workstation/ui/dialogs/diagnosis.py:1080、:1726、:1983 诊单只读页也能按 diagnosis_id 打开已开处方 AI 历史 必须走同一共享报告权限与 capability
app/src/doctor_workstation/ui/dialogs/issued_prescription_ai.py:401、:1817 can_open_issued_ai + present_issued_prescription_ai 是共享报告入口 保留 reports/detail guard,另设 can_view_agreement;不要把报告 guard 改成分数 guard
同文件 :664、:1377–:1417、:1459 AgreementBar、model score、可访问名/tooltip、原文 comparison 都有分数 分数不授权时不构建/隐藏分数区域,不填分数到隐藏控件,并清理原文内容
同文件 :1210;issued_prescription_ai_pages.py:1156、:1191 HistoryPage 从 reports.lists 渲染历史分数趋势,兼容 comparison.score 与模型顶层 score 历史仍可选;关闭趋势分数,服务端历史摘要也需要去分数
issued_prescription_ai_workspace.py:674–:687 总览结论有双方一致率、纯药味重合度和百分点差 一致度受限时不得拼出这些结论
issued_prescription_ai_pages.py:364、:464 候选与逐味页说明算法、读取贡献行 要确认“仅一致度”是否包括每味贡献;至少不能泄露 contribution 与算法分子/分母
issued_prescription_ai_console.py:260、:295、:378 顶部仪表、无障碍名称、纯药味重合度 与主报告 capability 保持一致
issued_prescription_ai.py:1705–:1813;issued_prescription_ai_pages.py:1374 医生统计对话框;分布、均值、中位数、明细 HTML 现有 statistics API 权限以外还应要求一致度可见;拒绝后整个统计关闭

补充:原处方详情 dialogs/prescription.py 没有独立已开处方一致度入口;目前可见入口集中在上述列表和诊单共享报告。后台 admin/src/views/consumer/prescription/components/PrescriptionAiReportDialog.vue 是另一个旧的处方解读报告流程,当前 admin/src/views / admin/src/api 没搜到 tcm.prescriptionAi 或一致度 UI,不应把它误当成本次双模型已开处方分析界面。

请求边界与元数据

app/src/doctor_workstation/services/repository.py:1609–:1648 透传以下服务端字典,新增 capability 无需先扩大类型 DTO:

  • statuses(ids):模型顶层 score、comparison_status,用于列表。
  • reports(prescription_id XOR diagnosis_id):历史摘要;传入 page_no/page_size。
  • detail(batch_id):完整 model comparison,用于报告全部页面。
  • statistics(date_from,date_to,doctor_id?):独立统计读接口。
  • regenerate/retry/review 是显式写接口,此需求若只限制读分数,不必连带禁止已有 AI 生成和复核动作。

现有登录承载:

  • core/models.py:127–:176 的 UserProfile 已包含 id、role_ids、root、permissions、完整 raw;多角色解析支持 role_ids / role_id / roles。
  • services/repository.py:902–:927 的 get_session() 读取 auth.admin/mySelf,构造 PermissionSet 并存 session。
  • core/permissions.py:63–:83 支持全局 * 与路由前缀 /*。ui/widgets.py 的 has_permission 已统一前端权限语义,新增功能不要另写角色硬编码。
  • 后台 admin/src/stores/modules/user.ts:110 左右同样读取 data.user/data.permissions/data.menu。服务端 AdminLogic.php:319 已填 role_ids。
  • 注意:mySelf 的 metadata 只是登录时快照;服务端改授权后应通过 AI read 响应刷新 capability,不能等用户下次登录才隐藏分数。

后台配置与选择器复用

可承载配置的页面

admin/src/views/setting/desktop_workstation/index.vue 有成熟 el-form + getData + handleSubmit + footer-btns 模式,当前管理升级策略;API 在 admin/src/api/setting/desktop_workstation.ts,对应 setting.desktop_workstation/getConfig,setConfig。可新增独立“处方 AI 一致度可见范围”设置区,提供角色多选 + 用户多选,并明确 OR 语义(命中任一角色,或被指定用户,均可见)。保存权限宜独立管理,避免升级包操作员默认拥有授权分配能力。

若希望未来扩展整个 AI 模块范围,新建 setting/prescription_ai 页面更清晰;当前没有可直接扩展的同名页。ai_mcp/grant 只管理员工外部 AI 助手令牌撤销,并非本功能授权。

角色与用户

  • admin/src/api/perms/role.ts:8 的 roleAll({}) → /auth.role/all 可复用。角色多选 UI 在 views/permission/admin/edit.vue:73–:91,optionsData.role 来自 roleAll;处方页 consumer/prescription/index.vue:3303 也有获取及失败降级模式。
  • 当前角色配置 views/permission/role/auth.vue 是菜单树,保存 menu_id 到 roleEdit;适合给配置页面授予管理权限,但无法直接表达个人白名单。
  • admin/src/api/perms/admin.ts:4 的 adminLists → /auth.admin/lists 是已实现的人员来源。AdminLists.php:82 支持 name/account 搜索,:158 支持角色筛选,:165 支持 exclude_disabled=1,列表带 id/name/account/role_id。建议用户多选下拉做姓名/账号远程分页,显示 姓名(账号)。
  • 不要直接复用 adminAll:前端虽导出了 /auth.admin/all 包装,但当前 AdminController.php 无 all(),全仓也没找到该路径映射。
  • admin/src/components 没有通用后台用户多选组件。可用 Element Plus el-select multiple filterable remote 现有模式(处方页已有远程患者选择,但其数据类型和接口不能直接当员工选择器)。
  • 现有处方模型 visible_role_ids 是处方审核/数据可见范围,不是分数可见范围;不要复用该字段造成整个处方行被隐藏。

仅一致度、保留 AI 报告的最小实现建议

  1. 服务端策略保留为独立 role IDs + admin IDs;服务端返回 can_view_agreement(名称仅建议)而不是让客户端拼白名单。root 的默认行为、未配置的初始行为须与产品决策一致。
  2. statuses/reports/detail 每个响应显式带当前 capability;未授权时剔除顶层/嵌套 score、herb_score、逐味 contribution 以及对应聚合分母,避免旧客户端或缓存旁路。统计接口直接拒绝未授权读。enabled 仍只代表分析功能总开关。
  3. 桌面以独立布尔能力驱动第 12 列和统计按钮;第 11 列分析状态、AI 报告按钮与 reports/detail 权限保持现状。_set_ai_columns 应拆开或接受第二个参数。改变能力或请求失败时清理旧数据,不能只 hide()。
  4. 共享报告统一把 capability 传至 AgreementBar / Workspace / HistoryPage / CandidatesPage;受限时保留完整报告、候选处方、来源、进度、复核与历史批次选择,但去掉一致度数字、趋势、贡献和相关说明。完整报告存量文本是否可能内嵌一致度,需要服务端研究确认;如允许模型原文包含此数值,仅过滤结构化字段不够。
  5. 新后台配置区复用现有表单样式与 roleAll;人员选择用 adminLists 或专门受配置管理权限保护的最小字段 options 接口,避免配置管理员为了选人还必须拥有完整管理员列表权限。

待用户确认:“仅一致度”是只控制分数/重合度/贡献统计,还是连逐味剂量比较也一起隐藏;后者需要隐藏总览和候选逐味相关导航,但仍可保留报告正文。整个 AI 模块方案则简单得多:按新策略门禁所有 AI 入口和 API;不能在未确认时用此方案替代仅一致度方案。

可复用测试

本次只读研究未运行测试。现有测试使用内存 fake repository + run_async immediate fixture,不需要生产 API。

  • app/tests/test_issued_prescription_ai.py:440 test_list_batches_visible_ids_and_stops_on_hide:可延伸验证受限账号仍拉状态、只隐藏第 12 列。
  • 同文件 :462、:500、:525、:564:空处方、总开关关闭、缺少 statuses 权限的入口及列显隐基线。
  • 同文件 :428 test_view_permission_cannot_mutate:保持 reports/detail 与写权限解耦。
  • 同文件 :1178 test_access_revocation_clears_reports_and_all_progress:可延伸为只撤销分数能力后清空数字,但正文保留。
  • 同文件 :596、:796、:977:统计 null/版本分层/排除原因;补未授权统计不发请求且清空旧图。
  • 同文件 :626 test_repository_exact_read_and_mutation_contracts:可加服务端 capability 透传及不误改 mutation 请求。
  • 同文件 :697、:714、:1236:追加列表缓存、右键目标、tooltip;可防分数在缓存/无障碍文本中残留。
  • app/tests/test_issued_prescription_ai_pages.py:288–:326:历史读取嵌套和扁平两种分数;:361–:418:统计图表。新增受限 capability 时两种数据形状都不显示分数。
  • app/tests/test_issued_prescription_ai_workspace.py:239:不捏造一致率差值;可加受限时不生成现有分数结论。
  • app/tests/test_permissions.py:*、前缀通配符、AND/OR、空权限语义;如果新增菜单按钮权限应保持兼容。
  • app/tests/test_repository_parity.py:可复用登录/session 的用户 ID、role_ids 解析测试结构。
  • 后台当前无处方 AI 授权配置测试;admin/tests/*.test.cjs 是 Node 内建测试模式,admin/package.json 有 type-check,没有统一 test script。应新增只针对配置读写归一化、空选择与人员搜索的必要测试,避免写只镜像模板的快照测试。

实施交接:最小切换与 fixture

  1. issued_prescription_ai.py 增加统一 _apply_agreement_visibility()(建议名)。_build_ui 默认隐藏;_history_ready / _detail_ready 应用 agreement_visible is True。缺失 flag 按 false;主题切换 _switch_theme 会重建 UI,必须继承能力并重新应用。从有权切成无权时,先清空旧 payload/图,再渲染正文。
  2. 顶部 self.agreement 与 self.agreement_note 一起隐藏。注意每个模型的 retry 按钮目前经 self.agreement.attach_action(key,retry) 放在仪表区域(:891);如要求保留重试,需要移到进度页/独立状态区域。
  3. 导航为 NAV_PRIMARY(:68),self.nav_buttons 是 self.rail.buttons(:738);overview/per_herb/history 都不是普通可见 tab,而是 rail 控制隐藏 tabBar 的 QTabWidget。隐藏导航后还要给 _goto(:1065)加拒绝/回落到 report,防 _open_supporting_report(:1097)和主题恢复旧页绕过。
  4. 最小完整隔离方式:受限时隐藏 overview 与 per_herb 导航/页面、comparison_raw 原文页;默认切 report。报告原文、candidate、sources/gaps、pipeline、original 继续可用。HistoryPage 可保持批次列表但隐藏图和 3/4 分数列;更省改动可隐藏 history 页面,顶部 self.history 批次选择与分页仍保持完整历史访问。
  5. 不能直接隐藏 overview 就结束:review_model 与完整 review 表单在 :898–:899 被 reparent 到 comparison_panel.checklist。隐藏总览会丢失现有复核 UI。若保留复核,需把 selector + review widget 移到正文旁/独立复核页;或保留 overview 骨架,只隐藏 conclusions/attribution/doses/risks 及 comparison 链接,保留 checklist。PrescriptionReviewWorkspace 的这些已命名属性在 issued_prescription_ai_workspace.py:1159–:1173,有 set_review_selector/set_review_widget(:1245、:1252)。
  6. StepRail 的 focus_values(issued_prescription_ai_console.py:475)与 _render_console 的 badges/focus 含差异、共识药味等派生计数。若 comparison 整体移除,清空/隐藏相关焦点行,避免把它们误显示为 0。保留来源缺口和报告数量即可。
  7. HistoryPage(pages.py:1145)左 card 当前是局部变量 left;如仅隐藏趋势需将其保存为实例属性。表格 3/4 列是两模型分数,5 列算法;strata 和 note 也有“分数可以比较”等文案,受限时应清理。set_history 即使拿到剔除 comparison 的历史,也会保留这些文案,不能依赖没有数字自然隐藏。
  8. CandidatesPage 的 cards 显示原方/候选处方;下方表格同时混合剂量与贡献。若保留候选卡片,则只隐藏/跳过逐味 body;否则整页隐藏最少。候选独立原文页 tab_pages['candidate'] 依旧能保留原始候选数据。
  9. issued_prescription_ai.py:1414–:1417 的 comparison HTML 应清空并跳过填充,而非仅藏页面。主界面 model_views[*]['score'] 的 text/tooltip/accessibility、AgreementBar 内 gauge 和历史 TrendBars 都要拿空数据重置,避免 capability 撤销后留下旧值。

可直接扩展 fixture:

  • app/tests/test_issued_prescription_ai.py:24 的 batch(),:45 的 Repository、:86 的 no_network(拒绝 socket)、:95 的 immediate;Repository 加 agreement_visible 属性和 capabilities 方法,read 字典统一附 flag。该组合最适合测从有权到无权的异步回调、缓存清理和主题重建。
  • app/tests/test_issued_prescription_ai_pages.py:35 的 batch(**overrides)、:66 的 per_herb、history fixture,可用同一批次先 true 再 false,确认 score fallback 两种形状都无残留。
  • app/tests/test_issued_prescription_ai_workspace.py:25 的 batch(count) 与 :43 的 workspace fixture 带真实保存逐味 identity 和 review slot;适合确认受限骨架不影响 review、report links 与布局。