Files
xh-medical-sign-web/clinical-web/docs/reports-backend-requirements.md
T

229 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 报表分析后端接口交付需求
> 事实基准:用户于 2026-09-02 导出的 default_OpenAPI.json,OpenAPI 3.1.0,MEDISIGN 患者电子签 API v1。
>
> 本文的所有“建议新增”接口均不在当前 OpenAPI 中。它们是后端交付需求,前端在接口实际发布前不得直接调用。
## 1. 交付目标
当前 API 只提供签署任务、模板、院区和科室等原始数据。报表页为了展示筛选、四张指标卡、签署量趋势、科室签署分布和明细列表,需要在浏览器中循环读取全部有权限的签署任务后自行聚合。
后端需要提供权限内聚合、分页明细和正式导出的报表能力,使前端不再全量读取历史任务,也不再自行定义时间、状态、分母和时区口径。
## 2. 当前 OpenAPI 已有能力与不足
| 已有接口 | 可复用能力 | 不能解决的问题 |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
| GET /api/v1/sign-tasks | 任务状态、createdAt、signedAt、expiredAt、院区/科室 ID、患者和就诊脱敏快照;支持 page、size、keyword、status、signMethod、campusId、departmentId、createdFrom、createdTo | 没有服务端指标、趋势、科室分布、按 signedAt/expiredAt 查询或真实签署耗时 |
| GET /api/v1/templates/available-versions | 可用模板版本、模板名称和分类 | 不是历史任务的不可变模板快照;历史模板不可见时无法可靠补齐名称和类别 |
| GET /api/v1/campuses | 院区分页查询 | 默认分页大小为 20,不能直接作为完整筛选字典 |
| GET /api/v1/departments | 科室分页查询,可按 campusId 筛选 | 默认分页大小为 20,不能直接作为完整筛选字典 |
当前导出的 OpenAPI 中没有 reports、analytics、statistics 或 export 资源。
## 3. 页面五个区域的后端交付范围
| 页面区域 | 后端应返回的结果 | 当前前端临时实现 |
| ------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------ |
| 搜索/筛选栏 | 统一筛选参数、可用院区/科室字典、明确时间维度 | 前端过滤已加载任务;无任务 keyword 搜索框 |
| 四张指标卡 | 发起数、签署完成数、按时签署率、真实签署耗时及样本量 | 前端按 createdAt、signedAt、expiredAt 聚合;耗时暂以 signedAt - createdAt 代替 |
| 签署量趋势图 | 按 signedAt 和指定时区的自然日聚合 | 前端扫描所有已加载任务 |
| 科室签署分布 | 科室 ID、名称和按相同筛选条件聚合的数量 | 前端按 visitSnapshot.departmentName 聚合 |
| 签署明细列表 | 服务端分页、稳定排序、历史模板和组织展示快照 | 前端拉取全部任务后一次性渲染 |
## 4. P0:报表概览聚合接口
**建议新增:GET /api/v1/reports/signing/overview**
接口只返回四张指标卡、签署趋势和科室分布,不返回大量明细。
所有请求使用现有 X-Token 鉴权方式,并通过统一 ApiResponse 返回。
### 查询参数
| 参数 | 类型 | 说明 |
| ----------------------- | ----------------- | -------------------------------------------------- |
| createdFrom / createdTo | ISO-8601 datetime | 发起任务和创建时间口径的闭区间 |
| signedFrom / signedTo | ISO-8601 datetime | 签署完成、趋势和完成分布的闭区间 |
| expiredFrom / expiredTo | ISO-8601 datetime | 超时统计的闭区间 |
| campusId | UUID | 院区筛选;不传表示调用人权限范围内的全部院区 |
| departmentId | UUID | 科室筛选 |
| templateCategory | string | 模板稳定分类编码,不能用中文名称关键词推断 |
| templateVersionId | UUID | 模板版本筛选 |
| signMethod | PAD 或 SMS | 签署方式筛选 |
| timezone | IANA timezone | 默认建议固定为 Asia/Shanghai,并由服务端校验白名单 |
### data 返回字段
| 字段 | 说明 |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| timezone | 本次统计实际使用的时区 |
| metrics.initiatedCount | 创建时间在范围内的任务数 |
| metrics.signedCount | 状态为 SIGNED 且 signedAt 在范围内的任务数 |
| metrics.onTimeSignedCount | signedAt 小于等于 expiredAt 的已签署任务数 |
| metrics.onTimeSampleCount | 有明确终态时间的 SIGNED 与 EXPIRED 任务数;VOIDED、FAILED 和未完成任务不进入分母 |
| metrics.onTimeRate | 分子除以分母;无样本时返回 null |
| metrics.averageSignDurationSeconds | 真实签署耗时均值;无有效样本时返回 null |
| metrics.durationSampleCount | 参与耗时计算的样本数 |
| trend | 数组,每项包含 date 与 signedCount |
| departments | 数组,每项包含 departmentId、departmentName、initiatedCount、signedCount、onTimeSignedCount、onTimeSampleCount、onTimeRate |
### 成功响应示例
```json
{
"code": "0",
"message": "OK",
"data": {
"timezone": "Asia/Shanghai",
"metrics": {
"initiatedCount": 132,
"signedCount": 108,
"onTimeSignedCount": 101,
"onTimeSampleCount": 115,
"onTimeRate": 0.8783,
"averageSignDurationSeconds": 1860,
"durationSampleCount": 103
},
"trend": [{ "date": "2026-08-01", "signedCount": 18 }],
"departments": [
{
"departmentId": "550e8400-e29b-41d4-a716-446655440001",
"departmentName": "心内科",
"initiatedCount": 25,
"signedCount": 18,
"onTimeSignedCount": 17,
"onTimeSampleCount": 19,
"onTimeRate": 0.8947
}
]
},
"traceId": "report-overview-example",
"timestamp": "2026-09-02T08:00:00Z"
}
```
统计口径必须由服务端固定。特别是趋势必须按 signedAt 聚合,超时必须按 expiredAt 聚合,不能用 createdAt 或 updatedAt 代替。
## 5. P0:报表明细分页接口
**建议新增:GET /api/v1/reports/signing/tasks**
该接口复用第 4 节全部筛选参数,并增加下列分页参数:
| 参数 | 类型 | 说明 |
| ------------- | -------------------------- | ----------------------------------------------------------- |
| page | integer | 页码,从 1 开始,默认 1 |
| size | integer | 每页条数,范围 1–200,默认 20 |
| sort | string | 仅允许白名单字段;默认 createdAt,DESC,id,DESC,保证稳定排序 |
| timeDimension | CREATED、SIGNED 或 EXPIRED | 明细列表的主时间维度;默认 CREATED |
每条明细至少返回:
- taskId、status、signMethod、createdAt、signedAt、expiredAt、voidedAt;
- 脱敏患者展示字段,不返回身份证号、完整手机号或完整病历;
- templateVersionId、templateName、templateCategory;
- campusId、campusName、departmentId、departmentName;
- signDurationSeconds;没有可靠样本时返回 null。
模板、院区和科室展示字段应是任务快照,或由服务端在完成权限过滤后关联返回。前端不能只依赖当前可用模板版本来补历史明细。
### 成功响应示例
分页结构沿用当前契约的 records、page、size、total、pages 字段:
```json
{
"code": "0",
"message": "OK",
"data": {
"records": [
{
"taskId": "650e8400-e29b-41d4-a716-446655440001",
"status": "SIGNED",
"signMethod": "SMS",
"createdAt": "2026-08-01T01:00:00Z",
"signedAt": "2026-08-01T01:23:40Z",
"expiredAt": "2026-08-02T01:00:00Z",
"voidedAt": null,
"patient": { "name": "张*", "mobileMasked": "138****0000" },
"templateVersionId": "750e8400-e29b-41d4-a716-446655440001",
"templateName": "诊疗知情同意书",
"templateCategory": "INFORMED_CONSENT",
"campusId": "850e8400-e29b-41d4-a716-446655440001",
"campusName": "示例院区",
"departmentId": "550e8400-e29b-41d4-a716-446655440001",
"departmentName": "心内科",
"signDurationSeconds": 1420
}
],
"page": 1,
"size": 20,
"total": 132,
"pages": 7
},
"traceId": "report-tasks-example",
"timestamp": "2026-09-02T08:00:00Z"
}
```
## 6. P1:正式导出接口
**建议新增:**
- POST /api/v1/reports/signing/export-jobs
- GET /api/v1/reports/signing/export-jobs/{jobId}
- GET /api/v1/reports/signing/export-jobs/{jobId}/download
创建任务时复用概览和明细的筛选条件,可附带 format 为 CSV 或 XLSX 以及可导出列集。响应返回 jobId、任务状态和文件有效期。
导出任务必须:
- 在服务端重新执行调用人的数据范围、院区/科室权限和模板 ACL;
- 记录操作人、筛选条件摘要、生成时间、结果数量、文件过期时间和 traceId;
- 异步处理大数据量,提供 PENDING、RUNNING、SUCCEEDED、FAILED、EXPIRED 等状态;
- 只返回当前角色允许查看的脱敏字段,不导出 Token、身份证号、完整手机号、完整病历或签名原图。
创建任务成功时,可按当前统一响应包装返回:
```json
{
"code": "0",
"message": "OK",
"data": {
"jobId": "950e8400-e29b-41d4-a716-446655440001",
"status": "PENDING",
"expiresAt": "2026-09-09T08:00:00Z"
},
"traceId": "report-export-example",
"timestamp": "2026-09-02T08:00:00Z"
}
```
## 7. 过渡方案:补强原始签署任务接口
如果 P0 报表资源暂时无法排期,至少扩展现有 GET /api/v1/sign-tasks:
- 增加 signedFrom、signedTo、expiredFrom、expiredTo,并明确它们与 createdFrom、createdTo 的组合语义;
- 返回不可变的 templateName、templateCategory、campusName、departmentName 快照;
- 返回 signStartedAt、signDurationSeconds 或等价审计耗时字段;
- 固定默认排序,并保证 page、size、total、pages 在权限过滤后稳定一致;
- 院区和科室接口应支持完整可用字典,或明确允许 size=200 和稳定排序,避免筛选项只获得默认第一页。
## 8. 权限、错误与审计要求
- 所有建议接口沿用 X-Token、统一 ApiResponse、traceId 和 UTC timestamp。
- 聚合和分页前必须在服务端执行数据范围、模板 ACL、院区和科室权限;前端不能承担授权过滤。
- 时间边界按 timezone 计算;若不支持动态时区,应在契约中固定 Asia/Shanghai。
- 建议错误语义:400 参数或日期范围错误,401 未登录或令牌失效,403 无报表权限,404 导出任务不存在或已过期,429 导出频率受限,500 聚合或文件生成失败。
- 指标分母、状态排除规则和时区属于契约的一部分;变更时需要可追溯的版本说明。
## 9. 联调验收标准
- 同一筛选条件下,四张指标卡、趋势、科室分布和明细具有可解释且一致的时间口径。
- 延迟签署任务只落入 signedAt 对应的趋势日期;过期任务只按 expiredAt 统计。
- 无有效签署耗时时返回 null,前端显示为 —,不填固定分钟数。
- 明细分页总数和排序稳定,翻页不重复、不遗漏。
- 服务端返回和导出文件均已完成权限过滤,不含敏感明文。
- 导出任务可以查询状态、审计、过期和受权限保护的下载。