16 KiB
已开处方 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):完整 modelcomparison,用于报告全部页面。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 Plusel-select multiple filterable remote现有模式(处方页已有远程患者选择,但其数据类型和接口不能直接当员工选择器)。- 现有处方模型
visible_role_ids是处方审核/数据可见范围,不是分数可见范围;不要复用该字段造成整个处方行被隐藏。
仅一致度、保留 AI 报告的最小实现建议
- 服务端策略保留为独立 role IDs + admin IDs;服务端返回
can_view_agreement(名称仅建议)而不是让客户端拼白名单。root 的默认行为、未配置的初始行为须与产品决策一致。 statuses/reports/detail每个响应显式带当前 capability;未授权时剔除顶层/嵌套score、herb_score、逐味contribution以及对应聚合分母,避免旧客户端或缓存旁路。统计接口直接拒绝未授权读。enabled仍只代表分析功能总开关。- 桌面以独立布尔能力驱动第 12 列和统计按钮;第 11 列分析状态、AI 报告按钮与 reports/detail 权限保持现状。
_set_ai_columns应拆开或接受第二个参数。改变能力或请求失败时清理旧数据,不能只hide()。 - 共享报告统一把 capability 传至 AgreementBar / Workspace / HistoryPage / CandidatesPage;受限时保留完整报告、候选处方、来源、进度、复核与历史批次选择,但去掉一致度数字、趋势、贡献和相关说明。完整报告存量文本是否可能内嵌一致度,需要服务端研究确认;如允许模型原文包含此数值,仅过滤结构化字段不够。
- 新后台配置区复用现有表单样式与 roleAll;人员选择用 adminLists 或专门受配置管理权限保护的最小字段 options 接口,避免配置管理员为了选人还必须拥有完整管理员列表权限。
待用户确认:“仅一致度”是只控制分数/重合度/贡献统计,还是连逐味剂量比较也一起隐藏;后者需要隐藏总览和候选逐味相关导航,但仍可保留报告正文。整个 AI 模块方案则简单得多:按新策略门禁所有 AI 入口和 API;不能在未确认时用此方案替代仅一致度方案。
可复用测试
本次只读研究未运行测试。现有测试使用内存 fake repository + run_async immediate fixture,不需要生产 API。
app/tests/test_issued_prescription_ai.py:440test_list_batches_visible_ids_and_stops_on_hide:可延伸验证受限账号仍拉状态、只隐藏第 12 列。- 同文件
:462、:500、:525、:564:空处方、总开关关闭、缺少 statuses 权限的入口及列显隐基线。 - 同文件
:428test_view_permission_cannot_mutate:保持 reports/detail 与写权限解耦。 - 同文件
:1178test_access_revocation_clears_reports_and_all_progress:可延伸为只撤销分数能力后清空数字,但正文保留。 - 同文件
:596、:796、:977:统计 null/版本分层/排除原因;补未授权统计不发请求且清空旧图。 - 同文件
:626test_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
issued_prescription_ai.py增加统一_apply_agreement_visibility()(建议名)。_build_ui默认隐藏;_history_ready/_detail_ready应用agreement_visible is True。缺失 flag 按 false;主题切换_switch_theme会重建 UI,必须继承能力并重新应用。从有权切成无权时,先清空旧 payload/图,再渲染正文。- 顶部
self.agreement与self.agreement_note一起隐藏。注意每个模型的 retry 按钮目前经self.agreement.attach_action(key,retry)放在仪表区域(:891);如要求保留重试,需要移到进度页/独立状态区域。 - 导航为
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)和主题恢复旧页绕过。 - 最小完整隔离方式:受限时隐藏 overview 与 per_herb 导航/页面、comparison_raw 原文页;默认切 report。报告原文、candidate、sources/gaps、pipeline、original 继续可用。
HistoryPage可保持批次列表但隐藏图和 3/4 分数列;更省改动可隐藏 history 页面,顶部self.history批次选择与分页仍保持完整历史访问。 - 不能直接隐藏 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)。 StepRail的focus_values(issued_prescription_ai_console.py:475)与_render_console的 badges/focus 含差异、共识药味等派生计数。若 comparison 整体移除,清空/隐藏相关焦点行,避免把它们误显示为 0。保留来源缺口和报告数量即可。HistoryPage(pages.py:1145)左 card 当前是局部变量left;如仅隐藏趋势需将其保存为实例属性。表格 3/4 列是两模型分数,5 列算法;strata和note也有“分数可以比较”等文案,受限时应清理。set_history即使拿到剔除 comparison 的历史,也会保留这些文案,不能依赖没有数字自然隐藏。CandidatesPage的cards显示原方/候选处方;下方表格同时混合剂量与贡献。若保留候选卡片,则只隐藏/跳过逐味 body;否则整页隐藏最少。候选独立原文页tab_pages['candidate']依旧能保留原始候选数据。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、historyfixture,可用同一批次先 true 再 false,确认 score fallback 两种形状都无残留。app/tests/test_issued_prescription_ai_workspace.py:25的batch(count)与:43的workspacefixture 带真实保存逐味 identity 和 review slot;适合确认受限骨架不影响 review、report links 与布局。