Просмотр исходного кода

docs(dev): 添加多项健康数据功能设计文档

- 新增体格数据功能设计文档,涵盖上报、查询、删除接口及权限审计
- 新增地理位置服务设计文档,定义最近位置查询接口与改进建议
- 新增心率数据功能设计文档,包括数据上报与绑定方查询逻辑
- 新增用户绑定功能设计文档,支持创建、删除与检查绑定关系
- 新增血压数据功能设计文档,明确血压测量值的处理流程
- 新增血糖数据功能设计文档,规范血糖记录的增删查操作
mcbaiyun 1 месяц назад
Родитель
Сommit
b2c3c38dcc

+ 140 - 0
docs/Dev/modules/体格数据功能设计文档.md

@@ -0,0 +1,140 @@
+**体格数据功能设计文档**
+
+**概述**: 本文档描述系统中体格(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 范围限制等),并在必要处记录操作审计日志。

+ 87 - 0
docs/Dev/modules/地理位置服务设计文档.md

@@ -0,0 +1,87 @@
+**地理位置服务设计文档**
+
+**概述**: 本文档描述系统提供的地理位置相关接口(当前仅包含“最近位置”查询),包含外部服务调用、错误处理、配置建议与使用示例。对应实现:`src/main/java/.../controller/GeoController.java`。
+
+**目标**: 根据给定经纬度返回最近的位置信息字符串,供前端展示或作为地址回填使用;对外部地理服务调用做统一封装、错误与超时处理,以及可配置的服务端点。
+
+**接口清单**:
+- **获取最近位置**
+  - 路径: `GET /geo/nearest`
+  - 描述: 根据 `latitude` 与 `longitude` 查询最近位置字符串(同步返回)。
+  - 请求参数(Query):
+    - `latitude` (double) - 纬度,必填
+    - `longitude` (double) - 经度,必填
+  - 响应: `R<String>`,成功返回 200 和字符串内容;失败返回 `ErrorCode.GEO_REQUEST_FAILED`。
+  - 示例:
+
+```
+GET /geo/nearest?latitude=30.12345&longitude=114.12345
+```
+
+**实现细节(目前代码)**
+- Controller 使用注入的 `RestTemplate` 直接拼接请求 URL:
+  - 默认硬编码外部地址为 `http://45.207.222.6/geo/nearest.php?latitude={latitude}&longitude={longitude}`。
+  - 使用 `restTemplate.getForEntity(url, String.class)` 获取原始响应体并直接返回给客户端。
+  - 发生异常时捕获并记录日志,返回 `R.fail(ErrorCode.GEO_REQUEST_FAILED)`。
+
+**问题与改进建议**
+1. 配置化外部服务地址与超时
+   - 把外部 URL 提取到 `application.yml`(例如 `geo.service.url`),避免在代码中硬编码 IP/路径,方便切换环境与容错。
+   - 使用带连接/读取超时的 `RestTemplate`(或 `WebClient`)并从配置加载超时值,防止外部服务阻塞线程。
+
+2. 错误处理与降级策略
+   - 明确区分网络错误、HTTP 非 200 响应与响应解析错误,设置不同日志级别与错误码(例如 `GEO_SERVICE_UNAVAILABLE`)。
+   - 建议实现简单的熔断/重试策略:当外部服务短期内失败多次时返回降级提示或缓存上次成功结果。
+   - 对常见异常(连接超时、读取超时、SocketException)给出可操作日志,便于后续排查。
+
+3. 响应格式与语义
+   - 当前直接返回外部服务的原始字符串;建议统一约定返回内容格式(例如 JSON 包装 { "address": "...", "source": "providerA" }),便于前端解析与兼容多个提供方。
+   - 若外部服务返回 HTML 或非 JSON 字符串,建议在后端做清洗或封装,确保返回给客户端的是可预测的结构或明确的错误信息。
+
+4. 缓存与频次限制
+   - 对同一经纬度(或经纬度格网)短时间内的重复请求进行缓存(例如 1 分钟),减少对外部服务依赖并降低延迟。
+   - 在高并发场景考虑在 Controller 层或 API 网关做节流/限流。
+
+5. 安全与审计
+   - 若使用收费或有请求配额的第三方服务,请在调用日志中记录 provider、请求结果码与耗时,用于计费和问题追踪。
+   - 记录调用链日志:请求来源(userId / IP)、输入经纬度、外部请求耗时与返回状态。
+
+**建议的配置示例 (application.yml)**
+
+```yaml
+geo:
+  service:
+    url: "http://45.207.222.6/geo/nearest.php"
+    connect-timeout-ms: 2000
+    read-timeout-ms: 3000
+    cache-ttl-seconds: 60
+```
+
+**参考实现建议要点**
+- 使用 `RestTemplateBuilder` 或 `WebClient` 从配置构造带超时的 HTTP 客户端。
+- 将实际调用封装到 `GeoService`,Controller 只负责参数校验与统一响应。
+- 在 `GeoService` 中实现:配置化 URL、参数编码、错误分类、重试/熔断策略、缓存层(如 Caffeine 或 Redis)的接入点。
+
+**错误码与日志策略**
+- 继续使用 `ErrorCode.GEO_REQUEST_FAILED` 作为通用失败返回;可考虑增加 `GEO_SERVICE_UNAVAILABLE`(已在 `ErrorCode` 中预留)用于第三方服务不可用场景。
+- 记录日志字段:`userId`(如果可用)、`latitude`、`longitude`、`externalUrl`、`statusCode`、`latencyMs`、`errorMessage`。
+
+**示例:改进后的调用流程(伪代码)**
+
+1. Controller 校验入参(latitude/longitude)
+2. Controller 调用 `geoService.getNearest(latitude, longitude)`
+3. `GeoService`:
+   - 检查缓存(key = 经度/纬度格网或经纬度拼接)
+   - 若缓存命中返回缓存值
+   - 构建外部请求 URL(来自配置),执行 HTTP 请求并记录耗时
+   - 根据响应解析出地址字符串,缓存并返回
+   - 捕获异常并抛出明确的业务异常,Controller 根据异常返回对应错误码
+
+**示例响应**
+- 成功:`R.success(200, "ok", "湖北省武汉市xx区...")`
+- 失败:`R.fail(ErrorCode.GEO_REQUEST_FAILED.getCode(), ErrorCode.GEO_REQUEST_FAILED.getMessage(), null)`
+
+**后续工作建议**
+- 将外部 URL 配置化并添加超时、重试与熔断策略。 
+- 考虑对经纬度查询结果做缓存,降低请求量并改善响应速度。 
+- 如需兼容多个地理服务,设计统一的适配器层以便在 provider 间切换。

+ 128 - 0
docs/Dev/modules/心率数据功能设计文档.md

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

+ 124 - 0
docs/Dev/modules/用户绑定功能设计文档.md

@@ -0,0 +1,124 @@
+**用户绑定功能设计文档**
+
+**概述**: 描述患者与医生/家属之间绑定关系的管理与查询能力。对应实现:`src/main/java/.../controller/UserBindingController.java` 与 `service/UserBindingService`,持久化表建议为 `t_user_binding`。
+
+**目标**: 提供创建/删除绑定关系、按患者或被绑定用户分页查询绑定列表、以及检测两者是否存在绑定关系的能力;并明确权限、审计与字段语义,支持业务侧基于绑定关系的授权决策(如健康数据访问)。
+
+**接口清单**:
+- **创建用户绑定关系**
+  - 路径: `POST /user-binding/create`
+  - 描述: 为患者创建与医生或家属的绑定关系。
+  - 请求体: `CreateUserBindingRequest` (application/json)
+    - `patientUserId` (Long) - 患者用户ID,必填
+    - `boundUserId` (Long) - 被绑定用户ID(医生或家属),必填
+    - `bindingType` (String) - 绑定类型(`DOCTOR` 或 `FAMILY`),必填
+  - 响应: 标准 `R` 成功/失败。创建时应避免重复绑定记录(可在 service 层做幂等处理)。
+
+- **删除用户绑定关系**
+  - 路径: `POST /user-binding/delete`
+  - 描述: 解除患者与被绑定用户之间的绑定关系。
+  - 请求体: `DeleteUserBindingRequest` (application/json)
+    - `patientUserId` (Long) - 患者用户ID,必填
+    - `boundUserId` (Long) - 被绑定用户ID,必填
+  - 响应: 标准 `R` 成功/失败。删除操作应校验调用者权限(患者本人或平台管理员/被绑定方在某些场景下也可)。
+
+- **分页查询患者的绑定关系列表**
+  - 路径: `POST /user-binding/list-by-patient`
+  - 描述: 根据患者 ID 和可选绑定类型查询该患者的绑定关系列表(医生/家属)。
+  - 参数: `patientUserId` (Long, 必填), `bindingType` (String, 可选), 请求体 `BaseQueryRequest`
+  - 响应: `UserBindingPageResponse`(内部包含 `UserBindingResponse` 列表,含被绑定用户的昵称/手机号等便捷信息)。
+
+- **分页查询用户被绑定的关系列表**
+  - 路径: `POST /user-binding/list-by-bound-user`
+  - 描述: 查询某个被绑定用户(如医生)被哪些患者绑定。
+  - 参数: `boundUserId` (Long, 必填), `bindingType` (String, 可选), 请求体 `BaseQueryRequest`
+  - 响应: 同 `list-by-patient`。
+
+- **检查用户绑定关系**
+  - 路径: `POST /user-binding/check`
+  - 描述: 检查两用户之间是否存在绑定关系,返回 `exists` 与 `bindingType`。
+  - 请求体: `CheckUserBindingRequest` (application/json)
+    - `patientUserId` (Long), `boundUserId` (Long)
+  - 响应: `CheckUserBindingResponse`:
+    - `exists` (Boolean) - 是否存在绑定关系
+    - `bindingType` (String) - 若存在,返回绑定类型(`DOCTOR`/`FAMILY`)
+
+**数据模型 (VO / PO)**
+- `CreateUserBindingRequest`:
+  - `patientUserId`: Long
+  - `boundUserId`: Long
+  - `bindingType`: String (`DOCTOR` | `FAMILY`)
+- `DeleteUserBindingRequest`:
+  - `patientUserId`: Long
+  - `boundUserId`: Long
+- `UserBindingResponse`:
+  - `id`: String
+  - `patientUserId`: String
+  - `boundUserId`: String
+  - `bindingType`: String
+  - `status`: Integer (1 有效, 0 无效)
+  - `createTime`: LocalDateTime
+  - `boundUserNickname`, `boundUserPhone` 等为附加展示字段
+- `CheckUserBindingRequest` / `CheckUserBindingResponse`:见接口
+
+**持久化建议(表 t_user_binding)**
+- 建议字段: `id`, `patient_user_id`, `bound_user_id`, `binding_type`, `status`, `created_by`, `created_at`, `updated_at`, `deleted_flag`。
+- 索引建议: `(patient_user_id)`, `(bound_user_id)`, 复合索引 `(patient_user_id, bound_user_id)` 以加速检查/删除操作。
+
+**业务规则与校验**
+- 创建绑定时:
+  - 校验 `patientUserId` 与 `boundUserId` 不为空,且二者不同。
+  - 不允许重复生效的绑定记录;如已存在有效绑定则返回幂等成功或说明已存在。
+  - 可能需要目标用户同意(可扩展为双向确认流程),当前实现为直接创建(具体流程参照 `UserBindingService`)。
+- 删除绑定时:
+  - 仅允许有权限的主体发起(患者本人、平台管理员或被绑定方在特殊策略下)。
+  - 推荐使用软删除(设置 `status=0` 或 `deleted_flag`)以保留历史审计。
+
+**权限与访问控制**
+- 创建/删除绑定:
+  - 需要登录;操作权限详见业务规则。创建可由患者或后台操作触发;删除需校验发起者身份。
+- 查询绑定列表:
+  - 查询患者的绑定关系通常允许患者本人或有管理权限的用户访问;查询被绑定用户的列表通常允许被绑定用户本人或管理员访问。
+- 检查绑定关系:
+  - 常用于其他数据访问接口(如 `list-by-bound-user` 的数据授权判断),通常不对外开放敏感信息,仅返回是否存在与绑定类型。
+
+**审计与日志**
+- 对绑定关系的创建、删除、状态变更事件写入审计日志,记录 `operatorId`、`patientUserId`、`boundUserId`、`bindingType`、`timestamp`、`ip` 等。
+- 对于通过绑定关系访问患者数据的操作(例如查看体征数据),在访问日志中记录 `accessorId`、`patientUserId`、`bindingType` 与被访问的数据类型。
+
+**错误码**
+- 常见错误码参考 `ErrorCode`:`PARAMETER_ERROR`、`SYSTEM_ERROR`、`DATA_NOT_FOUND`、`DATA_ACCESS_DENIED` 等。建议为绑定流程补充特定错误码(如 `BINDING_ALREADY_EXISTS`, `BINDING_NOT_FOUND`),以方便前端友好提示。
+
+**示例**
+- 创建绑定请求示例:
+
+```json
+{
+  "patientUserId": 1001,
+  "boundUserId": 2001,
+  "bindingType": "DOCTOR"
+}
+```
+
+- 检查绑定请求示例:
+
+```json
+{
+  "patientUserId": 1001,
+  "boundUserId": 2001
+}
+```
+
+- 检查绑定响应示例:
+
+```json
+{
+  "exists": true,
+  "bindingType": "DOCTOR"
+}
+```
+
+**扩展建议**
+- 支持绑定审批流程:发起方请求绑定 -> 目标方同意/拒绝 -> 完成绑定,适用于敏感场景。
+- 提供绑定通知:在绑定建立或解除时通知患者或绑定方(短信/模板消息/站内信)。
+- 支持绑定时限或权限粒度:例如只允许查看体征数据但不允许修改提醒等。

+ 137 - 0
docs/Dev/modules/血压数据功能设计文档.md

@@ -0,0 +1,137 @@
+**血压数据功能设计文档**
+
+**概述**: 本文档描述系统中血压(Blood Pressure)数据的上报、查询与删除能力,以及相关权限、审计与存储设计。对应代码位置:`src/main/java/.../controller/BloodPressureDataController.java`、`service/BloodPressureDataService`、数据库表 `t_blood_pressure_data`。
+
+**目标**: 为患者提供上报个人血压(收缩压/舒张压)数据的能力,并允许绑定方(医生/家属)在存在绑定关系时查询患者数据,同时保证访问审计与必要的权限校验。
+
+**接口清单**:
+- **添加血压数据**
+  - 路径: `POST /blood-pressure/add`
+  - 描述: 患者上报一条血压测量记录。
+  - 请求体: `AddBloodPressureDataRequest` (application/json)
+    - **systolicPressure**: Integer,收缩压(mmHg),必填,范围 [60, 250]
+    - **diastolicPressure**: Integer,舒张压(mmHg),必填,范围 [40, 150]
+    - **measureTime**: LocalDateTime,测量时间,必填
+  - 响应: 标准 `R` 成功/失败返回。成功返回 `200 OK` 与 message `ok`。
+  - 校验: 请求字段使用注解验证(`@NotNull`, `@Min`, `@Max`)。
+
+- **分页查询血压数据(本人)**
+  - 路径: `POST /blood-pressure/list`
+  - 描述: 当前用户根据时间范围和分页参数查询自己的血压记录。
+  - 请求体: `BaseQueryRequest` (application/json)
+    - **pageNum**: 页码,默认 1
+    - **pageSize**: 每页大小,默认 10
+    - **startTime**, **endTime**: 可选时间范围
+  - 响应: `BloodPressureDataPageResponse`,包含分页 metadata 与 `BloodPressureDataResponse` 列表。
+
+- **绑定方分页查询患者血压数据**
+  - 路径: `POST /blood-pressure/list-by-bound-user`
+  - 描述: 医生或家属查询目标患者血压记录(需有绑定关系)。
+  - 参数: `patientUserId` (Long, 必填),`bindingType` (String, 可选),请求体 `BaseQueryRequest`
+  - 权限检查流程:
+    - 获取当前发起查询的用户 `boundUserId`(从 token / SecurityUtils)
+    - 调用 `UserBindingService.checkUserBinding` 验证 `boundUserId` 与 `patientUserId` 的绑定关系
+    - 若不存在绑定关系,返回错误 `DATA_ACCESS_DENIED`(无权访问)
+    - 若存在且 `bindingType` 为空,则从检查结果中使用绑定类型
+  - 审计: 在 Controller 中记录访问日志(logger.info),包含访问方、患者、时间范围与数据类型(type=blood_pressure)。
+  - 响应: 同 `list`,返回 `BloodPressureDataPageResponse`。
+
+- **删除血压数据**
+  - 路径: `POST /blood-pressure/delete`
+  - 描述: 根据记录 ID 删除血压数据(通常仅允许记录创建者或有权限的管理方删除,具体权限在 service 层实现)。
+  - 请求体: `DeleteBloodPressureDataRequest`,包含 **id** (String,必填)
+  - 响应: 标准 `R` 成功/失败返回。
+
+**数据模型 (VO / PO)**
+- 请求/响应 VO:
+  - `AddBloodPressureDataRequest`:
+    - `systolicPressure` : Integer
+    - `diastolicPressure` : Integer
+    - `measureTime` : LocalDateTime
+  - `BloodPressureDataResponse`:
+    - `id` : String
+    - `systolicPressure` : Integer
+    - `diastolicPressure` : Integer
+    - `measureTime` : LocalDateTime
+    - `createTime` : LocalDateTime
+
+- 持久化表: `t_blood_pressure_data`(参见 `docs/DB/t_blood_pressure_data.txt`)
+  - 建议字段: `id`, `user_id`, `systolic_pressure`, `diastolic_pressure`, `measure_time`, `create_time`, `update_time`, `deleted_flag` 等
+
+**校验规则**
+- `systolicPressure` 非空且在 60-250 mmHg 之间。
+- `diastolicPressure` 非空且在 40-150 mmHg 之间。
+- `measureTime` 非空且不晚于当前时间(可在 service 层严格校验)。
+
+**权限与访问控制**
+- 上报数据: 需要登录(Token 校验),默认记录写入当前用户(user_id 从 token 中获取)。
+- 个人查询: 仅返回当前登录用户的数据。
+- 绑定方查询: 必须存在绑定关系(通过 `UserBindingService.checkUserBinding`),否则返回 `DATA_ACCESS_DENIED`。
+- 删除操作: service 层应校验调用者是否为记录所有者或拥有管理权限(未在 Controller 中显式强制,需在 service 实现)。
+
+**审计与日志**
+- 在 `list-by-bound-user` 接口中记录访问日志:记录访问方 ID、患者 ID、数据类型(blood_pressure)、查询时间范围。
+- 在新增/删除等数据变更点建议写入操作日志用于审计。
+
+**错误码**
+- 常见错误码参考 `ErrorCode` 枚举:
+  - `PARAMETER_ERROR (1001)`:请求参数缺失或校验失败
+  - `SYSTEM_ERROR (1000)`:服务器内部错误
+  - `UNAUTHORIZED (1002)`:未授权(token 无效)
+  - `DATA_ACCESS_DENIED (4001)`:绑定方访问时无权限
+  - `DATA_NOT_FOUND (4000)`:删除/查询的记录不存在
+
+**服务与 Mapper 关系**
+- Controller -> `BloodPressureDataService`(负责业务校验、权限判断、持久化调用、分页查询实现)
+- Service -> Mapper (`BloodPressureDataMapper`) 与 `t_blood_pressure_data` 表交互
+
+**兼容性与注意事项**
+- 时间字段使用 `LocalDateTime`,前端需按约定时区/格式发送(建议 ISO-8601)。
+- 分页返回使用 MyBatis-Plus `Page`,Controller 将其转换为页面友好格式 `BloodPressureDataPageResponse`。
+
+**示例**
+- 添加请求示例:
+
+```json
+{
+  "systolicPressure": 120,
+  "diastolicPressure": 80,
+  "measureTime": "2025-11-20T08:00:00"
+}
+```
+
+- 查询(分页)请求示例:
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 20,
+  "startTime": "2025-11-01T00:00:00",
+  "endTime": "2025-11-21T23:59:59"
+}
+```
+
+- 响应示例 (分页 `records` 片段):
+
+```json
+{
+  "records": [
+    {
+      "id": "bp-001",
+      "systolicPressure": 120,
+      "diastolicPressure": 80,
+      "measureTime": "2025-11-20T08:00:00",
+      "createTime": "2025-11-20T08:05:00"
+    }
+  ],
+  "total": 1,
+  "size": 20,
+  "current": 1
+}
+```
+
+**后续建议**
+- 在 Service 层补充:
+  - 删除权限检查(仅允许记录所有者或管理员删除)
+  - measureTime 合理性校验(不晚于服务器时间、历史数据限制)
+  - 为绑定方查询添加柔性权限策略(只读/可注释等)

+ 138 - 0
docs/Dev/modules/血糖数据功能设计文档.md

@@ -0,0 +1,138 @@
+**血糖数据功能设计文档**
+
+**概述**: 本文档描述系统中血糖(Blood Glucose)数据的接入、查询、删除能力及相关权限、审计和存储设计。对应代码位置:`src/main/java/.../controller/BloodGlucoseDataController.java`、`service/BloodGlucoseDataService`、数据库表 `t_blood_glucose_data`。
+
+**目标**: 为移动端(患者)和绑定方(医生/家属)提供血糖测量记录的上报与查询能力,同时确保基于绑定关系的访问控制与审计记录。
+
+**接口清单**:
+- **添加血糖数据**
+  - 路径: `POST /blood-glucose/add`
+  - 描述: 患者上报一条血糖测量记录。
+  - 请求体: `AddBloodGlucoseDataRequest` (application/json)
+    - **type**: String,测量类型(例如:`fasting`、`postprandial` 等),必填
+    - **value**: BigDecimal,血糖值(mmol/L),必填,范围 [1.0, 30.0]
+    - **measureTime**: LocalDateTime,测量时间,必填
+  - 响应: 标准 `R` 成功/失败返回。成功返回 `200 OK` 与 message `ok`。
+  - 校验: 请求字段使用注解验证(`@NotNull`, `@DecimalMin`, `@DecimalMax`)。
+
+- **分页查询血糖数据(本人)**
+  - 路径: `POST /blood-glucose/list`
+  - 描述: 当前用户根据时间范围和分页参数查询自己的血糖记录。
+  - 请求体: `BaseQueryRequest` (application/json)
+    - **pageNum**: 页码,默认 1
+    - **pageSize**: 每页大小,默认 10
+    - **startTime**, **endTime**: 可选时间范围
+  - 响应: `BloodGlucoseDataPageResponse`,包含分页 metadata 与 `BloodGlucoseDataResponse` 列表。
+
+- **绑定方分页查询患者血糖数据**
+  - 路径: `POST /blood-glucose/list-by-bound-user`
+  - 描述: 医生或家属等绑定方查询目标患者的血糖记录,前提为存在绑定关系。
+  - 参数: `patientUserId` (Long, 必填),`bindingType` (String, 可选),请求体 `BaseQueryRequest`
+  - 权限检查流程:
+    - 获取当前发起查询的用户 `boundUserId`(从 token / SecurityUtils)
+    - 调用 `UserBindingService.checkUserBinding` 验证 `boundUserId` 与 `patientUserId` 的绑定关系
+    - 若不存在绑定关系,返回错误 `DATA_ACCESS_DENIED`(无权访问)
+    - 若存在且 `bindingType` 为空,则从检查结果中使用绑定类型
+  - 审计: 在 Controller 中写入访问日志(logger.info),记录访问方、患者、时间范围与数据类型(type=blood_glucose)。
+  - 响应: 同 `list`,返回 `BloodGlucoseDataPageResponse`。
+
+- **删除血糖数据**
+  - 路径: `POST /blood-glucose/delete`
+  - 描述: 根据记录 ID 删除血糖数据(通常仅允许记录创建者或有权限的管理方删除,具体权限在 service 层实现)。
+  - 请求体: `DeleteBloodGlucoseDataRequest`,包含 **id** (String,必填)
+  - 响应: 标准 `R` 成功/失败返回。
+
+**数据模型 (VO / PO)**
+- 请求/响应 VO:
+  - `AddBloodGlucoseDataRequest`:
+    - `type` : String
+    - `value` : BigDecimal
+    - `measureTime` : LocalDateTime
+  - `BloodGlucoseDataResponse`:
+    - `id` : String
+    - `measureTime` : LocalDateTime
+    - `type` : String
+    - `value` : BigDecimal
+    - `createTime` : LocalDateTime
+
+- 持久化表: `t_blood_glucose_data`(参见 `docs/DB/t_blood_glucose_data.txt`)
+  - 建议字段: `id`, `user_id`, `type`, `value`, `measure_time`, `create_time`, `update_time`, `deleted_flag` 等
+
+**校验规则**
+- `type` 非空。
+- `value` 在 1.0 至 30.0 mmol/L 之间。
+- `measureTime` 非空且不晚于当前时间(可在 service 层严格校验)。
+
+**权限与访问控制**
+- 上报数据: 需要登录(Token 校验),默认记录写入当前用户(user_id 从 token 中获取)。
+- 个人查询: 仅返回当前登录用户的数据。
+- 绑定方查询: 必须存在绑定关系(通过 `UserBindingService.checkUserBinding`),否则返回 `DATA_ACCESS_DENIED`。
+- 删除操作: service 层应校验调用者是否为记录所有者或拥有管理权限(未在 Controller 中显式强制,需在 service 实现)。
+
+**审计与日志**
+- 在 `list-by-bound-user` 接口中记录访问日志:记录访问方 ID、患者 ID、数据类型(blood_glucose)、查询时间范围。
+- 在重要数据变更点(新增、删除)建议写入操作日志,用于追溯。
+
+**错误码**
+- 常见错误码参考 `ErrorCode` 枚举:
+  - `PARAMETER_ERROR (1001)`:请求参数缺失或校验失败
+  - `SYSTEM_ERROR (1000)`:服务器内部错误
+  - `UNAUTHORIZED (1002)`:未授权(token 无效)
+  - `DATA_ACCESS_DENIED (4001)`:绑定方访问时无权限
+  - `DATA_NOT_FOUND (4000)`:删除/查询的记录不存在
+
+**服务与 Mapper 关系**
+- Controller -> `BloodGlucoseDataService`(负责业务校验、权限判断、持久化调用、分页查询实现)
+- Service -> Mapper (`BloodGlucoseDataMapper`) 直接与 `t_blood_glucose_data` 表交互
+
+**兼容性与注意事项**
+- `list-by-bound-user` 的参数 `bindingType` 可由前端传入,也可在后端根据绑定关系重用检查结果中的绑定类型,保证行为向后兼容。
+- 时间字段使用 `LocalDateTime`,前端需按照约定时区/格式发送(建议 ISO-8601)。
+- 分页返回使用 MyBatis-Plus `Page`,Controller 将其转换为页面友好格式 `BloodGlucoseDataPageResponse`。
+
+**示例**
+- 添加请求示例:
+
+```json
+{
+  "type": "fasting",
+  "value": 5.6,
+  "measureTime": "2025-11-20T07:30:00"
+}
+```
+
+- 查询(分页)请求示例:
+
+```json
+{
+  "pageNum": 1,
+  "pageSize": 20,
+  "startTime": "2025-11-01T00:00:00",
+  "endTime": "2025-11-21T23:59:59"
+}
+```
+
+- 响应示例 (分页 `records` 片段):
+
+```json
+{
+  "records": [
+    {
+      "id": "abc123",
+      "measureTime": "2025-11-20T07:30:00",
+      "type": "fasting",
+      "value": 5.6,
+      "createTime": "2025-11-20T07:35:00"
+    }
+  ],
+  "total": 1,
+  "size": 20,
+  "current": 1
+}
+```
+
+**后续建议**
+- 在 Service 层补充:
+  - 更完整的权限检查(删除仅允许记录所有者或管理员)
+  - measureTime 的合理性检查(不应晚于服务器当前时间、时间窗口限制等)
+  - 异常分类与更细粒度错误码(如 `DATA_SAVE_FAILED`)