**体格数据功能设计文档** **概述**: 本文档描述系统中体格(Physical)数据的上报、查询与删除能力,以及相关权限、审计与存储设计。对应代码位置:`src/main/java/.../controller/PhysicalDataController.java`、`service/PhysicalDataService`、数据库表 `t_physical_data`。 **目标**: 允许患者上报身高/体重等体格测量值,系统可计算并返回 BMI;允许绑定方(医生/家属)在存在绑定关系时查询患者体格数据;保证访问受限并记录审计日志以便追溯。 **接口清单**: - **添加体格数据** - 路径: `POST /physical/add` - 描述: 患者上报一条体格测量记录(身高、体重)。 - 请求体: `AddPhysicalDataRequest` (application/json) - **height**: BigDecimal,身高(cm),必填,范围 [100, 250] - **weight**: BigDecimal,体重(kg),必填,范围 [20, 300] - **measureTime**: LocalDateTime,测量时间,必填 - 服务端行为: - 计算 BMI = weight(kg) / (height(m))^2 并持久化(或在返回时计算) - 将记录 user_id 设为当前 token 对应用户 - 响应: 标准 `R` 成功/失败返回。 - 校验: 注解校验(`@NotNull`, `@DecimalMin`, `@DecimalMax`),并在 service 层校验合理时间窗口。 - **分页查询体格数据(本人)** - 路径: `POST /physical/list` - 描述: 当前用户根据时间范围和分页参数查询自己的体格记录。 - 请求体: `BaseQueryRequest` (application/json) - **pageNum**: 页码,默认 1 - **pageSize**: 每页大小,默认 10 - **startTime**, **endTime**: 可选时间范围 - 响应: `PhysicalDataPageResponse`,包含分页 metadata 与 `PhysicalDataResponse` 列表(含 `bmi` 字段)。 - **绑定方分页查询患者体格数据** - 路径: `POST /physical/list-by-bound-user` - 描述: 医生或家属查询目标患者体格记录(需有绑定关系)。 - 参数: `patientUserId` (Long, 必填),`bindingType` (String, 可选),请求体 `BaseQueryRequest` - 权限检查流程: - 获取当前发起查询的用户 `boundUserId`(从 token / SecurityUtils) - 调用 `UserBindingService.checkUserBinding` 验证绑定关系 - 若不存在绑定关系,返回 `DATA_ACCESS_DENIED` - 若存在且 `bindingType` 为空,则从检查结果中使用绑定类型 - 审计: 记录访问日志(logger.info),包含访问方、患者、时间范围与数据类型(type=physical)。 - 响应: 同 `list`,返回 `PhysicalDataPageResponse`。 - **删除体格数据** - 路径: `POST /physical/delete` - 描述: 根据记录 ID 删除体格数据(通常仅允许记录创建者或有权限的管理方删除,具体权限在 service 层实现)。 - 请求体: `DeletePhysicalDataRequest`,包含 **id** (Long,必填) - 响应: 标准 `R` 成功/失败返回。 **数据模型 (VO / PO)** - 请求/响应 VO: - `AddPhysicalDataRequest`: - `height` : BigDecimal (cm) - `weight` : BigDecimal (kg) - `measureTime` : LocalDateTime - `PhysicalDataResponse`: - `id` : String - `height` : BigDecimal - `weight` : BigDecimal - `bmi` : BigDecimal - `measureTime` : LocalDateTime - `createTime` : LocalDateTime - 持久化表: `t_physical_data`(建议字段) - 建议字段: `id`, `user_id`, `height_cm`, `weight_kg`, `bmi`, `measure_time`, `create_time`, `update_time`, `deleted_flag` 等 **校验规则** - `height` 非空且 100-250 cm。 - `weight` 非空且 20-300 kg。 - `measureTime` 非空且不晚于服务器当前时间(service 层可增强校验)。 **权限与访问控制** - 上报数据: 需要登录,记录写入当前用户(user_id 从 token 中获取)。 - 个人查询: 仅返回当前登录用户的数据。 - 绑定方查询: 必须存在绑定关系(通过 `UserBindingService.checkUserBinding`),否则返回 `DATA_ACCESS_DENIED`。 - 删除操作: service 层应校验调用者是否为记录所有者或拥有管理权限。 **审计与日志** - 在 `list-by-bound-user` 接口中记录访问日志:记录访问方 ID、患者 ID、数据类型(physical)、查询时间范围。 - 在新增/删除等关键操作处建议记录操作日志用于审计。 **错误码** - 参考 `ErrorCode` 枚举: - `PARAMETER_ERROR (1001)`:请求参数缺失或校验失败 - `SYSTEM_ERROR (1000)`:服务器内部错误 - `UNAUTHORIZED (1002)`:未授权(token 无效) - `DATA_ACCESS_DENIED (4001)`:绑定方访问时无权限 - `DATA_NOT_FOUND (4000)`:删除/查询的记录不存在 **服务与 Mapper 关系** - Controller -> `PhysicalDataService`(负责业务校验、权限判断、持久化调用、分页查询实现) - Service -> Mapper (`PhysicalDataMapper`) 与 `t_physical_data` 表交互 **兼容性与注意事项** - BMI 计算注意单位转换:height 从 cm 转为 m 后计算,即 bmi = weight / (height/100)^2。 - 时间字段使用 `LocalDateTime`,前端需按约定时区/格式发送(建议 ISO-8601)。 - 分页返回使用 MyBatis-Plus `Page`,Controller 将其转换为页面友好格式 `PhysicalDataPageResponse`。 **示例** - 添加请求示例: ```json { "height": 170.5, "weight": 65.2, "measureTime": "2025-11-20T09:00:00" } ``` - 查询(分页)请求示例: ```json { "pageNum": 1, "pageSize": 20, "startTime": "2025-11-01T00:00:00", "endTime": "2025-11-21T23:59:59" } ``` - 响应示例 (分页 `records` 片段): ```json { "records": [ { "id": "phy-001", "height": 170.5, "weight": 65.2, "bmi": 22.43, "measureTime": "2025-11-20T09:00:00", "createTime": "2025-11-20T09:05:00" } ], "total": 1, "size": 20, "current": 1 } ``` **后续建议** - 在 Service 层补充更严格的校验与权限逻辑(删除权限、measureTime 范围限制等),并在必要处记录操作审计日志。