当患者与医生/家属建立绑定关系后,允许被绑定的医生或家属查询该患者的健康数据(血糖、血压、心率、体征等)。本设计描述目标、接口、权限校验、实现要点、测试与文档规范,并参照项目已经存在的经验文档(如 ID 精度、Swagger 泛型文档化)。
currentUserId。UserBindingService.checkUserBinding)。R<T> + Page<T> VO)。docs/Swagger泛型返回类型字段信息不显示问题解决方案.md,在 Controller 层使用 @ApiResponse 与 @Schema(implementation = ...) 标注返回类型;参照 docs/前端ID精度丢失问题解决方案.md 将 id 字段返回为 string。在每类健康数据 Controller(BloodGlucoseDataController, BloodPressureDataController, HeartRateDataController, PhysicalDataController 等)增加接口 list-by-bound-user:
/blood-glucose/list-by-bound-user。patientUserId(Long)、bindingType(String, 可选)、BaseQueryRequest(分页、时间范围)AuthInterceptor 提供的 currentUserId 来判断请求人的身份。userBindingService.checkUserBinding(patientUserId, currentUserId),若不存在返回 R.fail(ErrorCode.DATA_ACCESS_DENIED);否则继续查询并返回数据。Service 层提供 listDataByPatient(patientUserId, BaseQueryRequest) 等方法,返回 Page<TResponse> 供 Controller 组装对应 PageResponse VO。
说明:仓库中部分 Service/Controller 已实现该机制(如 BloodGlucoseDataServiceImpl.listBloodGlucoseDataByPatient),实现如下:
boundUserId = SecurityUtils.getCurrentUserId(),通过 userBindingService.checkUserBinding 校验;若绑定存在则回填 bindingType 并调用 Service;patientUserId 替换原 userId 进行查询,若 bindingType=="FAMILY" 则限制 startTime 最小值为最近 365 天(此处逻辑已在 BloodGlucoseDataServiceImpl / HeartRateDataServiceImpl 中实现)。权限策略(建议):
CreateUserBindingRequest 中声明。审计日志:
list-by-bound-user 接口中记录 boundUserId, patientUserId, queryType, startTime, endTime,写入到 regular logs(仓库实现使用 logger.info(...))或独立审计表(参考 docs/DevRule/06-日志和错误处理规范.md)。如需合规审计,建议实现 patient_data_access_log 表。示例 - 血糖数据:
Controller 片段(示例,仅说明用法)
@Operation(summary = "医生/家属分页查询患者血糖数据", description = "绑定方查询患者血糖测量记录(需有绑定关系)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功查询血糖数据列表",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = work.baiyun.chronicdiseaseapp.model.vo.BloodGlucoseDataPageResponse.class))),
@ApiResponse(responseCode = "403", description = "无权限访问",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@PostMapping(path = "/list-by-bound-user", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> listByBoundUser(Long patientUserId, String bindingType, @RequestBody BaseQueryRequest req, HttpServletRequest request) {
Long boundUserId = (Long) request.getAttribute("currentUserId");
// 1. 校验参数
if (patientUserId == null) {
return R.fail(ErrorCode.PARAMETER_ERROR.getCode(), ErrorCode.PARAMETER_ERROR.getMessage());
}
// 2. 校验绑定关系
CheckUserBindingRequest checkReq = new CheckUserBindingRequest();
checkReq.setPatientUserId(patientUserId);
checkReq.setBoundUserId(boundUserId);
CheckUserBindingResponse checkResp = userBindingService.checkUserBinding(checkReq);
if (!checkResp.getExists()) {
return R.fail(ErrorCode.DATA_ACCESS_DENIED.getCode(), "当前用户与目标患者未建立绑定关系");
}
// 3. 依据权限决定查询逻辑(可在 service 层统一处理)
Page<BloodGlucoseDataResponse> page = service.listBloodGlucoseDataByPatient(patientUserId, bindingType, req);
// 下面同 list 接口组装 PageResponse,返回
}
Service 层新增方法签名示例:
Page<BloodGlucoseDataResponse> listBloodGlucoseDataByPatient(Long patientUserId, String bindingType, BaseQueryRequest request);
实现要点:
eq(BloodGlucoseData::getUserId, userId) 为 patientUserId。@ApiResponse + @Content + @Schema(implementation = ...) 来避免泛型类型在文档中缺失(参考 docs/Swagger泛型返回类型...)。String(参见 docs/前端ID精度...)。示例:
@ApiResponse(responseCode = "200", description = "查询成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = work.baiyun.chronicdiseaseapp.model.vo.BloodGlucoseDataPageResponse.class)))
bindingType = DOCTOR:
bindingType = FAMILY:
bindingType 为空:由 userBindingService.checkUserBinding 返回的绑定类型决定权限。实现小技巧:在 UserBindingService 中返回绑定类型(CheckUserBindingResponse 已有),Controller/Service 使用该返回值作为权限判断依据。
boundUserId, patientUserId, bindingType, operation, queryStart, queryEnd, resultCount。patient_data_access_log 记录详细调用历史;字段示例:id、access_time、accessor_id、patient_id、data_type、start_time、end_time、ip_address。listByBoundUser 返回或拒绝的逻辑(正常与未绑定)。Authorization: Bearer <token> 中。AuthInterceptor 将解析并注入 currentUserId。id 字段为字符串,不要当作 Number 处理(参考 docs/前端ID精度...)。BloodGlucoseDataController, BloodPressureDataController, HeartRateDataController, PhysicalDataController 等实现 list-by-bound-user。listByPatient 变体并对 bindingType 做权限过滤(仓库已实现,参见 BloodGlucoseDataServiceImpl.listBloodGlucoseDataByPatient,HeartRateDataServiceImpl.listHeartRateDataByPatient)。AND user_id = #{patientUserId} 的查询条件。logger.info 写访问日志;如需合规审计,另外实现 patient_data_access_log 表与写入逻辑。以上为实现“绑定方查询患者健康数据”功能的设计文档草案,若需要我可以继续生成 Controller / Service 的代码模版以及对应的测试用例。