feat(reports): 接入真实报表分析及分组明细与异步导出

This commit is contained in:
xy
2026-09-30 18:27:26 +08:00
parent e5f0a5ec97
commit f155c5154f
14 changed files with 928 additions and 982 deletions
+12 -14
View File
@@ -118,9 +118,9 @@ ClinicalLayout(实际导航和页面路由由授权菜单资源树提供)
└─ /system/setting 系统设置 / 菜单设置
```
「系统管理 → 报表分析」(`/report/index`,签署报表与导出任务,走 `reports/signing/*` 接口)已整体下线:页面、接口模块、`VITE_MOCK_REPORTS` 开关与路由别名一并移除。后端菜单资源树暂时仍会下发该菜单,前端在 `stores/login.ts` 的 `RETIRED_MENU_PATHS` 里统一屏蔽,侧边栏不展示、路由守卫也会重定向走,不会出现「前端暂未接入组件」的死链;后端下架该菜单资源后可以删掉这个集合。
「系统管理 → 报表分析」(旧 `/report/index` 菜单)仍由 `stores/login.ts` 的 `RETIRED_MENU_PATHS` 屏蔽,避免旧菜单资源形成死链。它与下面四个独立分析菜单使用不同的页面路由。
`/report/sign/overview` 等四个是**另一套**:「报表分析」目录下的四个独立菜单,后端目前没有任何分析聚合接口,因此只有演示数据(`VITE_MOCK_REPORT_ANALYTICS`)。路由与 `componentPath` 都以后端菜单资源树为准,不要照抄原型里的 `MENU_TREE`(原型写的是 `/report/overview` 等,与后端不一致)。
`/report/sign/overview` 等四个页面是「报表分析」目录下的独立菜单。后端 V34 迁移创建菜单资源并授权给系统管理员、医务管理和审计员;路由与 `componentPath` 仍以后端菜单资源树为准,不要照抄原型里的 `MENU_TREE`(原型写的是 `/report/overview` 等,与后端不一致)。
布局拆分为 `Menu`、`Header` 和 `Container` 三个公共组件,页面内容由各自的 View 负责。
@@ -257,31 +257,29 @@ API 模块与页面按业务域对应。工作台页面使用 `api/workbench`
## 报表分析(签署总览 / 签署量分析 / 签署时效分析 / 患者结构分析)
四个菜单共用一套数据源与筛选条件:
四个菜单共用一套后端数据源与筛选条件:
- `api/workbench/report-analytics.ts`:数据集。**后端没有任何分析聚合接口**,真实模式返回 `{ tasks: [], available: false }` 而不抛错,页面据此显示「后端尚未提供报表分析接口」的说明,不渲染一张全 0 的假报表。
- `views/reports/aggregate.ts`:全部聚合逻辑,纯函数(不依赖 DOM 与 Vue),可用 Node 直跑断言。
- `api/workbench/report-analytics.ts`:报表查询、下钻分页和导出任务客户端。
- 后端统一使用 `/api/v1/reports/signing`:`GET /options` 提供筛选项,`GET /analytics` 一次返回状态、趋势、分组、时效与人口统计聚合,`GET /analytics/tasks` 分页返回分组下钻明细;接口沿用 `report:sign:view` 权限及当前用户的数据范围。
- `views/reports/aggregate.ts`:日期窗口、展示标签与时长格式化等前端辅助逻辑;聚合指标由数据库计算,浏览器不再拉取全量任务后自行统计。
- `views/reports/filter-store.ts`:**四个菜单共用一份筛选条件**(模块级 `reactive`)。原型里它们是同一筛选条下的四个页签,各存一份会导致切页时筛选被重置、四个页面的数字看起来互相矛盾。
- `views/reports/components/ReportPageShell.vue`:标题 + 筛选条 + 数据可用性闸门 + 导出弹窗,四个页面共用;页面只填自己的图表内容。
- `views/reports/components/ReportPageShell.vue`:读取后端筛选项、共用筛选条、数据加载状态与导出弹窗;页面只填自己的图表内容。
- 图表是手写内联 SVG(`ReportBarChart` / `ReportBarList`),不引图表库:只需要柱状图与横向条形榜两种形态,引库要多几百 KB 且读不到主题 CSS 变量。渐变 id 用 `useId()` 取每实例唯一值。
- 下钻和超期清单只返回脱敏患者姓名与患者标识;人口结构接口只返回数据库聚合计数,不向浏览器传输患者姓名、年龄或性别明细。
- 导出通过 `POST /export-jobs` 创建异步任务,`GET /export-jobs/{jobId}` 轮询状态,完成后调用响应中的下载路径。后端按白名单导出脱敏字段并记录审计。部署需设置 `medisign.sign.report.export.worker.enabled=true` 启动导出 worker;默认关闭。
**演示数据的两条硬约束**:① 用固定种子的 PRNG(mulberry32),不用 `Math.random` —— 否则每次刷新数字都在跳,截图对不上、断言写不了;② 时间锚定「距今天数」,不写死日期 —— 写死之后过几天再打开,近 7 天 / 近 14 天 / 本月全是空的,看起来像筛选坏了。
**边界**:① 时效指标只统计**已签署**任务(未完成任务不是「签得快」而是「还没签完」,计入会稀释均值),样本为 0 时显示「—」而不是 0;② 患者结构统计的是**人次**而非人数,同一患者多次签署会重复计数,页面上已写明;③ 导出是前端按条件生成 CSV(UTF-8 带 BOM、CRLF),与签署记录查询共用 `utils/csv-export.ts`。**后端提供导出任务后应改为服务端导出**:导出范围与导出人必须进审计日志,浏览器里做不到。真实接入聚合接口时,后端应按页面出四个聚合接口,**不要**照搬现在的形状把全量任务明细拉到浏览器里算。
演示数据缺省开启(`VITE_MOCK_REPORT_ANALYTICS`)。
**统计口径**:时效指标只统计已签署任务;样本为 0 时显示「—」。患者结构统计的是任务人次而非去重人数,同一患者的多次签署会重复计数。筛选支持后端任务实际使用的 PAD 与 SMS 两种签署方式。
API 请求和响应类型按业务域放在 `api/workbench/types.ts`、`api/management/types.ts`;页面展示和交互类型放在对应 View 目录的 `types.ts`;全局复用类型放在 `types/common.ts`。页面提交模型会在 API 边界转换为后端 DTO,不向后端发送患者快照、文档名称或明文手机号等页面字段。
开发环境默认使用 `/api` 作为同源接口前缀,Vite 会将 `/api` 转发到 `https://ipad.shenynet.com`,因此浏览器不会直接跨域请求后端。代理配置位于 `vite.config.ts`,不改写 `/api/v1/...` 路径。联调本地后端时可用 `VITE_API_PROXY_TARGET=http://127.0.0.1:18080 npm run dev` 覆盖目标。修改代理或环境变量后需要重启 Vite 开发服务。
开发环境默认使用 `/api` 作为同源接口前缀,Vite 会将 `/api` 转发到本机 `http://127.0.0.1:18080`,因此浏览器不会直接跨域请求后端。代理配置位于 `vite.config.ts`,不改写 `/api/v1/...` 路径;启动日志会显示实际代理目标。要联调测试服务器,可用 `VITE_API_PROXY_TARGET=https://ipad.shenynet.com npm run dev` 覆盖目标。修改代理或环境变量后需要重启 Vite 开发服务。
生产环境不会使用 Vite 的开发代理,需要在 Nginx 或其他网关中配置同样的 `/api` 反向代理。Mock 开关按业务模块(对齐菜单)拆分,缺省使用演示数据的只有签署侧(签署工作台 `VITE_MOCK_SIGNING`、签署记录查询 `VITE_MOCK_SIGN_RECORDS`)与「报表分析」目录下的四个菜单(`VITE_MOCK_REPORT_ANALYTICS`,后端没有分析聚合接口),其余模块直接调用真实后端;各开关的环境变量名与缺省值见 `src/utils/mock-flags.ts`,需要临时切换时在本地 `.env.*.local` 里覆盖即可。使用本地开发代理时,`VITE_API_BASE_URL` 应填写 `/api`;如果改为直连后端,则需要后端配置允许当前前端源的 CORS。
生产环境不会使用 Vite 的开发代理,需要在 Nginx 或其他网关中配置同样的 `/api` 反向代理。Mock 开关按业务模块(对齐菜单)拆分,缺省使用演示数据的只有签署工作台(`VITE_MOCK_SIGNING`)和签署记录查询(`VITE_MOCK_SIGN_RECORDS`);报表分析使用真实后端接口。各开关的环境变量名与缺省值见 `src/utils/mock-flags.ts`,需要临时切换时在本地 `.env.*.local` 里覆盖即可。使用本地开发代理时,`VITE_API_BASE_URL` 应填写 `/api`;如果改为直连后端,则需要后端配置允许当前前端源的 CORS。
当前尚未接入的签署能力包括手写板设备桥接、线上签署页面/签名回调和打印服务。签署投递、一次性 Token 消费和真实 PNG 上传 API 已完成封装,但页面不能用 Canvas 演示数据冒充真实签名;需要接入设备适配器或患者 H5 回调后再启用。短信初次发送接口需要完整手机号,而患者查询只返回脱敏手机号,因此真实模式会要求当前操作人员确认完整投递地址;生产环境也可以改为由后端根据患者 ID 解析投递地址。签署工作台缺省即使用演示数据(`VITE_MOCK_SIGNING`),需要走真实投递时把它设为 `false`。
文档库正文编辑器:选中「患者签名」占位符后,可以自动识别 PDF 位置,或生成 PDF 预览后输入坐标。阴影框支持拖动定位,宽高输入即时更新;点击「更新该项」或保存正文写入位置。正文变化后需重新生成预览。预览使用示例患者,实际患者文字长度可能影响排版,建议为填写项预留固定空间。重复的患者签名占位符不会自动选取。后端在保存及发布时检查页码与页面边界。
### 工作台患者填写项
真实 PAD 任务的勾选表格、结构化单选/多选、文本与日期项,使用后端 `/v1/sign-tasks/{taskId}/form` 返回的字段定义和规范化正文。每次修改串行保存完整患者答案,使用 `expectedRevision` 防止覆盖其他端的修改;保存失败会阻止签署。系统快照及医护已填项只读。上传签名时带 `signer.answerRevision`,后端完成最终必填校验并固化答案。已签任务和记录详情读取签署快照,下载 PDF 使用同一份答案。