患者提醒数据管理功能旨在为慢性病患者提供个性化的健康提醒服务,包括血压、血糖、心率等健康数据测量提醒以及用药提醒。每个患者只有一条提醒设置记录,包含所有提醒开关和时间点设置。系统将根据设置的时间推送提醒通知。
根据项目数据库表设计规范(参见 docs/OLD/DevRule/07-数据库规范.md),创建患者提醒设置表:
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | BIGINT(20) | 主键ID,使用雪花算法(MyBatis-Plus ASSIGN_ID) |
| patient_user_id | BIGINT(20) | 患者用户ID,每个患者只有一条记录 |
| is_notification_enabled | TINYINT(1) | 是否启用消息通知总开关 (0-禁用, 1-启用)。在 Java 实体中以 Byte 类型保存,服务层对其值作 Boolean 转换(1=true)。 |
| is_subscription_available | TINYINT(1) | 一次性订阅开关,服务器每发送消息后需重新申请用户授权 (0-需要重新授权, 1-已授权可发送)。在实体中为 Byte,对外 VO 封装为 Boolean。 |
| is_blood_pressure_enabled | TINYINT(1) | 测量血压提醒开关 (0-禁用, 1-启用)。实体字段为 Byte,响应层转为 Boolean。 |
| blood_pressure_times | TEXT | 测量血压的时间点(JSON格式存储,参考用药管理功能设计文档) |
| is_blood_sugar_enabled | TINYINT(1) | 测量血糖提醒开关 (0-禁用, 1-启用)。实体字段为 Byte,响应层转为 Boolean。 |
| blood_sugar_times | TEXT | 测量血糖的时间点(JSON格式存储) |
| is_heart_rate_enabled | TINYINT(1) | 测量心率提醒开关 (0-禁用, 1-启用)。实体字段为 Byte,响应层转为 Boolean。 |
| heart_rate_times | TEXT | 测量心率的时间点(JSON格式存储) |
| is_medication_enabled | TINYINT(1) | 用药提醒开关 (0-禁用, 1-启用)。实体字段为 Byte,响应层转为 Boolean。 |
| version | INT(11) | 版本号(乐观锁) |
| create_user | BIGINT(20) | 创建者ID |
| create_time | DATETIME | 创建时间,默认值CURRENT_TIMESTAMP |
| update_user | BIGINT(20) | 更新者ID |
| update_time | DATETIME | 更新时间,默认值CURRENT_TIMESTAMP,更新时自动设置为当前时间 |
| remark | VARCHAR(255) | 备注 |
遵循的数据库规范要点:
BIGINT 主键并通过雪花算法生成(MyBatis-Plus ASSIGN_ID)。version、create_user、create_time、update_user、update_time)由 BaseEntity 与 CustomMetaObjectHandler 自动填充。V{version}__{description}.sql)。示例建表(参考):
CREATE TABLE `t_patient_reminder` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`patient_user_id` bigint(20) NOT NULL COMMENT '患者用户ID',
`is_notification_enabled` tinyint(1) DEFAULT '1' COMMENT '是否启用消息通知总开关 (0-禁用, 1-启用)',
`is_subscription_available` tinyint(1) DEFAULT '1' COMMENT '一次性订阅开关 (0-需要重新授权, 1-已授权可发送)',
`is_blood_pressure_enabled` tinyint(1) DEFAULT '1' COMMENT '测量血压提醒开关 (0-禁用, 1-启用)',
`blood_pressure_times` text COMMENT '测量血压的时间点(JSON格式存储)',
`is_blood_sugar_enabled` tinyint(1) DEFAULT '0' COMMENT '测量血糖提醒开关 (0-禁用, 1-启用)',
`blood_sugar_times` text COMMENT '测量血糖的时间点(JSON格式存储)',
`is_heart_rate_enabled` tinyint(1) DEFAULT '1' COMMENT '测量心率提醒开关 (0-禁用, 1-启用)',
`heart_rate_times` text COMMENT '测量心率的时间点(JSON格式存储)',
`is_medication_enabled` tinyint(1) DEFAULT '1' COMMENT '用药提醒开关 (0-禁用, 1-启用)',
`version` int(11) DEFAULT '0' COMMENT '版本号(乐观锁)',
`create_user` bigint(20) DEFAULT NULL COMMENT '创建者ID',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_user` bigint(20) DEFAULT NULL COMMENT '更新者ID',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`remark` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_patient_user_id` (`patient_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='患者提醒设置表';
本功能设计中不使用枚举类型,所有提醒类型直接作为字段存储在患者提醒设置表中。注意:实体层字段在数据库中以 TINYINT(1) 保存(Java 实体使用 Byte),但对外 VO 使用 Boolean,并在 Service 层做相应的转换。
return code == null ? null : ReminderType.fromCode(code);
}
@Override
public ReminderType getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String code = rs.getString(columnIndex);
return code == null ? null : ReminderType.fromCode(code);
}
@Override
public ReminderType getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String code = cs.getString(columnIndex);
return code == null ? null : ReminderType.fromCode(code);
}
}
## 4. 实体类设计
### 4.1 PO实体类
#### 4.1.1 患者提醒设置实体类 (PatientReminder.java)
java package work.baiyun.chronicdiseaseapp.model.po;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode;
@Schema(description = "患者提醒设置表") @TableName("t_patient_reminder") @Data @EqualsAndHashCode(callSuper = false) public class PatientReminder extends BaseEntity {
@Schema(description = "患者用户ID")
@TableField("patient_user_id")
private Long patientUserId;
@Schema(description = "是否启用消息通知总开关 (0-禁用, 1-启用)")
@TableField("is_notification_enabled")
// 注意:数据库中为 TINYINT(1),实体类 `PatientReminder` 使用 `Byte`,响应 VO 仍是 Boolean
private Boolean notificationEnabled;
@Schema(description = "一次性订阅开关 (0-需要重新授权, 1-已授权可发送)")
@TableField("is_subscription_available")
// 注意:数据库中为 TINYINT(1),实体类 `PatientReminder` 使用 `Byte`,响应 VO 仍是 Boolean
private Boolean subscriptionAvailable;
@Schema(description = "测量血压提醒开关 (0-禁用, 1-启用)")
@TableField("is_blood_pressure_enabled")
// 注意:数据库中为 TINYINT(1),实体类 `PatientReminder` 使用 `Byte`,响应 VO 仍是 Boolean
private Boolean bloodPressureEnabled;
@Schema(description = "测量血压的时间点(JSON格式存储)")
@TableField("blood_pressure_times")
private String bloodPressureTimes;
@Schema(description = "测量血糖提醒开关 (0-禁用, 1-启用)")
@TableField("is_blood_sugar_enabled")
// 注意:数据库中为 TINYINT(1),实体类 `PatientReminder` 使用 `Byte`,响应 VO 仍是 Boolean
private Boolean bloodSugarEnabled;
@Schema(description = "测量血糖的时间点(JSON格式存储)")
@TableField("blood_sugar_times")
private String bloodSugarTimes;
@Schema(description = "测量心率提醒开关 (0-禁用, 1-启用)")
@TableField("is_heart_rate_enabled")
// 注意:数据库中为 TINYINT(1),实体类 `PatientReminder` 使用 `Byte`,响应 VO 仍是 Boolean
private Boolean heartRateEnabled;
@Schema(description = "测量心率的时间点(JSON格式存储)")
@TableField("heart_rate_times")
private String heartRateTimes;
@Schema(description = "用药提醒开关 (0-禁用, 1-启用)")
@TableField("is_medication_enabled")
// 注意:数据库中为 TINYINT(1),实体类 `PatientReminder` 使用 `Byte`,响应 VO 仍是 Boolean
private Boolean medicationEnabled;
}
### 4.2 VO对象
#### 4.2.1 请求对象
java // PatientReminderRequest.java package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.List; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@Schema(description = "患者提醒设置请求") @Data public class PatientReminderRequest {
@Schema(description = "是否启用消息通知总开关")
private Boolean notificationEnabled;
@Schema(description = "一次性订阅开关")
private Boolean subscriptionAvailable;
@Schema(description = "测量血压提醒开关")
private Boolean bloodPressureEnabled;
@Schema(description = "测量血压的时间点列表")
private List<String> bloodPressureTimes;
@Schema(description = "测量血糖提醒开关")
private Boolean bloodSugarEnabled;
@Schema(description = "测量血糖的时间点列表")
private List<String> bloodSugarTimes;
@Schema(description = "测量心率提醒开关")
private Boolean heartRateEnabled;
@Schema(description = "测量心率的时间点列表")
private List<String> heartRateTimes;
@Schema(description = "用药提醒开关")
private Boolean medicationEnabled;
}
// PatientReminderQueryRequest.java package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode;
@Schema(description = "患者提醒设置查询请求") @Data @EqualsAndHashCode(callSuper = true) public class PatientReminderQueryRequest extends BaseQueryRequest {
@Schema(description = "患者用户ID")
private Long patientUserId;
@Schema(description = "是否启用消息通知总开关")
private Boolean notificationEnabled;
@Schema(description = "测量血压提醒开关")
private Boolean bloodPressureEnabled;
@Schema(description = "测量血糖提醒开关")
private Boolean bloodSugarEnabled;
@Schema(description = "测量心率提醒开关")
private Boolean heartRateEnabled;
@Schema(description = "用药提醒开关")
private Boolean medicationEnabled;
}
#### 4.2.2 响应对象
java // PatientReminderResponse.java package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.List;
@Schema(description = "患者提醒设置响应") @Data public class PatientReminderResponse {
@Schema(description = "记录ID")
private String id;
@Schema(description = "患者用户ID")
private String patientUserId;
@Schema(description = "是否启用消息通知总开关")
private Boolean notificationEnabled;
@Schema(description = "一次性订阅开关")
private Boolean subscriptionAvailable;
@Schema(description = "测量血压提醒开关")
private Boolean bloodPressureEnabled;
@Schema(description = "测量血压的时间点列表")
private List<String> bloodPressureTimes;
@Schema(description = "测量血糖提醒开关")
private Boolean bloodSugarEnabled;
@Schema(description = "测量血糖的时间点列表")
private List<String> bloodSugarTimes;
@Schema(description = "测量心率提醒开关")
private Boolean heartRateEnabled;
@Schema(description = "测量心率的时间点列表")
private List<String> heartRateTimes;
@Schema(description = "用药提醒开关")
private Boolean medicationEnabled;
@Schema(description = "创建时间")
private java.time.LocalDateTime createTime;
}
// PatientReminderOverviewResponse.java package work.baiyun.chronicdiseaseapp.model.vo;
import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data;
@Schema(description = "患者提醒概览响应") @Data public class PatientReminderOverviewResponse {
@Schema(description = "患者提醒设置")
private PatientReminderResponse reminder;
}
## 5. Mapper接口设计
java // PatientReminderMapper.java package work.baiyun.chronicdiseaseapp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import work.baiyun.chronicdiseaseapp.model.po.PatientReminder; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param;
@Mapper public interface PatientReminderMapper extends BaseMapper {
PatientReminder selectByPatientUserId(@Param("patientUserId") Long patientUserId);
}
## 6. Service层设计
### 6.1 接口定义
java // PatientReminderService.java package work.baiyun.chronicdiseaseapp.service;
import work.baiyun.chronicdiseaseapp.model.vo.PatientReminderOverviewResponse; import work.baiyun.chronicdiseaseapp.model.vo.PatientReminderRequest; // Update operations are handled by savePatientReminder, no separate UpdatePatientReminderRequest implementation is required.
public interface PatientReminderService {
/**
* 获取患者提醒概览
*/
PatientReminderOverviewResponse getReminderOverview();
/**
* 保存患者提醒设置(创建或更新)
*/
void savePatientReminder(PatientReminderRequest request);
/**
* 删除患者提醒设置
*/
void deletePatientReminder(Long id);
}
### 6.2 实现类
java // PatientReminderServiceImpl.java package work.baiyun.chronicdiseaseapp.service.impl;
import com.fasterxml.jackson.core.type.TypeReference; import work.baiyun.chronicdiseaseapp.util.JsonUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import work.baiyun.chronicdiseaseapp.mapper.PatientReminderMapper; import work.baiyun.chronicdiseaseapp.model.po.PatientReminder; import work.baiyun.chronicdiseaseapp.model.vo.PatientReminderOverviewResponse; import work.baiyun.chronicdiseaseapp.model.vo.PatientReminderResponse; import work.baiyun.chronicdiseaseapp.model.vo.PatientReminderRequest; import work.baiyun.chronicdiseaseapp.service.PatientReminderService; import work.baiyun.chronicdiseaseapp.util.SecurityUtils; import java.util.List;
@Service public class PatientReminderServiceImpl implements PatientReminderService {
@Autowired
private PatientReminderMapper patientReminderMapper;
@Override
public PatientReminderOverviewResponse getReminderOverview() {
Long userId = SecurityUtils.getCurrentUserId();
// 注意:getReminderOverview 是只读方法,不会修改提醒设置;保存逻辑和订阅清理在 savePatientReminder 中完成。
PatientReminder patientReminder = patientReminderMapper.selectOne(
new QueryWrapper<PatientReminder>().eq("patient_user_id", userId)
);
PatientReminderOverviewResponse response = new PatientReminderOverviewResponse();
if (patientReminder != null) {
PatientReminderResponse reminderResponse = new PatientReminderResponse();
BeanUtils.copyProperties(patientReminder, reminderResponse);
reminderResponse.setId(patientReminder.getId().toString());
reminderResponse.setPatientUserId(patientReminder.getPatientUserId().toString());
// 通过 byte->Boolean 映射:1 -> true, others -> false
reminderResponse.setNotificationEnabled(patientReminder.getNotificationEnabled() != null && patientReminder.getNotificationEnabled() == 1);
reminderResponse.setSubscriptionAvailable(patientReminder.getSubscriptionAvailable() != null && patientReminder.getSubscriptionAvailable() == 1);
reminderResponse.setBloodPressureEnabled(patientReminder.getBloodPressureEnabled() != null && patientReminder.getBloodPressureEnabled() == 1);
reminderResponse.setBloodSugarEnabled(patientReminder.getBloodSugarEnabled() != null && patientReminder.getBloodSugarEnabled() == 1);
reminderResponse.setHeartRateEnabled(patientReminder.getHeartRateEnabled() != null && patientReminder.getHeartRateEnabled() == 1);
reminderResponse.setMedicationEnabled(patientReminder.getMedicationEnabled() != null && patientReminder.getMedicationEnabled() == 1);
// 时间点字段使用 JsonUtils 解析为 List<String>
reminderResponse.setBloodPressureTimes(JsonUtils.fromJson(patientReminder.getBloodPressureTimes(), new TypeReference<List<String>>() {}));
reminderResponse.setBloodSugarTimes(JsonUtils.fromJson(patientReminder.getBloodSugarTimes(), new TypeReference<List<String>>() {}));
reminderResponse.setHeartRateTimes(JsonUtils.fromJson(patientReminder.getHeartRateTimes(), new TypeReference<List<String>>() {}));
response.setReminder(reminderResponse);
}
return response;
}
注:若患者尚未设置提醒,getReminderOverview 将返回成功响应,但 data.reminder 为 null。调用方应对这种情况做兼容处理(例如在前端显示 "尚未设置提醒" 的提示)。
@Override
@Transactional(rollbackFor = Exception.class)
public void savePatientReminder(PatientReminderRequest request) {
Long userId = SecurityUtils.getCurrentUserId();
// 检查是否已存在记录
PatientReminder existing = patientReminderMapper.selectByPatientUserId(userId);
if (existing == null) {
// 创建患者提醒设置
PatientReminder patientReminder = new PatientReminder();
patientReminder.setPatientUserId(userId);
// 设置默认值与类型转换(Boolean -> Byte),默认值与代码中一致
patientReminder.setNotificationEnabled(request.getNotificationEnabled() != null ? (request.getNotificationEnabled() ? (byte)1 : (byte)0) : (byte)1);
patientReminder.setSubscriptionAvailable(request.getSubscriptionAvailable() != null ? (request.getSubscriptionAvailable() ? (byte)1 : (byte)0) : (byte)1);
patientReminder.setBloodPressureEnabled(request.getBloodPressureEnabled() != null ? (request.getBloodPressureEnabled() ? (byte)1 : (byte)0) : (byte)1);
patientReminder.setBloodPressureTimes(JsonUtils.toJson(request.getBloodPressureTimes()));
patientReminder.setBloodSugarEnabled(request.getBloodSugarEnabled() != null ? (request.getBloodSugarEnabled() ? (byte)1 : (byte)0) : (byte)0);
patientReminder.setBloodSugarTimes(JsonUtils.toJson(request.getBloodSugarTimes()));
patientReminder.setHeartRateEnabled(request.getHeartRateEnabled() != null ? (request.getHeartRateEnabled() ? (byte)1 : (byte)0) : (byte)1);
patientReminder.setHeartRateTimes(JsonUtils.toJson(request.getHeartRateTimes()));
patientReminder.setMedicationEnabled(request.getMedicationEnabled() != null ? (request.getMedicationEnabled() ? (byte)1 : (byte)0) : (byte)1);
patientReminderMapper.insert(patientReminder);
} else {
// 更新提醒设置
if (request.getNotificationEnabled() != null) {
existing.setNotificationEnabled(request.getNotificationEnabled() ? (byte)1 : (byte)0);
}
if (request.getSubscriptionAvailable() != null) {
existing.setSubscriptionAvailable(request.getSubscriptionAvailable() ? (byte)1 : (byte)0);
}
if (request.getBloodPressureEnabled() != null) {
existing.setBloodPressureEnabled(request.getBloodPressureEnabled() ? (byte)1 : (byte)0);
}
if (request.getBloodPressureTimes() != null) {
existing.setBloodPressureTimes(JsonUtils.toJson(request.getBloodPressureTimes()));
}
if (request.getBloodSugarEnabled() != null) {
existing.setBloodSugarEnabled(request.getBloodSugarEnabled() ? (byte)1 : (byte)0);
}
if (request.getBloodSugarTimes() != null) {
existing.setBloodSugarTimes(JsonUtils.toJson(request.getBloodSugarTimes()));
}
if (request.getHeartRateEnabled() != null) {
existing.setHeartRateEnabled(request.getHeartRateEnabled() ? (byte)1 : (byte)0);
}
if (request.getHeartRateTimes() != null) {
existing.setHeartRateTimes(JsonUtils.toJson(request.getHeartRateTimes()));
}
if (request.getMedicationEnabled() != null) {
existing.setMedicationEnabled(request.getMedicationEnabled() ? (byte)1 : (byte)0);
}
patientReminderMapper.updateById(existing);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deletePatientReminder(Long id) {
Long userId = SecurityUtils.getCurrentUserId();
// 检查权限
PatientReminder patientReminder = patientReminderMapper.selectById(id);
if (patientReminder == null) {
throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_NOT_FOUND.getCode(),
"患者提醒设置不存在");
}
if (!patientReminder.getPatientUserId().equals(userId)) {
throw new work.baiyun.chronicdiseaseapp.exception.CustomException(
work.baiyun.chronicdiseaseapp.enums.ErrorCode.DATA_ACCESS_DENIED.getCode(),
"无权操作该患者提醒设置");
}
// 删除提醒设置
patientReminderMapper.deleteById(id);
}
}
## 7. Controller层设计
java // PatientReminderController.java package work.baiyun.chronicdiseaseapp.controller;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import work.baiyun.chronicdiseaseapp.common.R; import work.baiyun.chronicdiseaseapp.model.vo.PatientReminderOverviewResponse; import work.baiyun.chronicdiseaseapp.model.vo.PatientReminderRequest; import work.baiyun.chronicdiseaseapp.service.PatientReminderService; import work.baiyun.chronicdiseaseapp.enums.ErrorCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
@RestController @RequestMapping("/patient-reminder") @Tag(name = "患者提醒管理", description = "患者健康提醒与用药提醒相关接口") public class PatientReminderController {
private static final Logger logger = LoggerFactory.getLogger(PatientReminderController.class);
@Autowired
private PatientReminderService patientReminderService;
@Operation(summary = "获取提醒概览", description = "获取患者的所有提醒设置概览")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "成功获取提醒概览",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = PatientReminderOverviewResponse.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@GetMapping(path = "/overview", produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> getOverview() {
try {
PatientReminderOverviewResponse response = patientReminderService.getReminderOverview();
return R.success(200, "ok", response);
} catch (Exception e) {
logger.error("get patient reminder overview failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
@Operation(summary = "保存患者提醒设置", description = "保存患者的所有提醒设置(创建或更新)")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "患者提醒设置保存成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@PostMapping(path = "/save", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> savePatientReminder(@RequestBody PatientReminderRequest req) {
try {
patientReminderService.savePatientReminder(req);
return R.success(200, "患者提醒设置保存成功");
} catch (Exception e) {
logger.error("save patient reminder failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
@Operation(summary = "删除患者提醒设置", description = "根据ID删除患者提醒设置")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "患者提醒设置删除成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode = "500", description = "服务器内部错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Void.class)))
})
@DeleteMapping(path = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public R<?> deletePatientReminder(@PathVariable Long id) {
try {
patientReminderService.deletePatientReminder(id);
return R.success(200, "患者提醒设置删除成功");
} catch (Exception e) {
logger.error("delete patient reminder failed", e);
return R.fail(ErrorCode.SYSTEM_ERROR.getCode(), ErrorCode.SYSTEM_ERROR.getMessage());
}
}
}
## 8. 错误码补充
当前实现使用 `ErrorCode.DATA_NOT_FOUND`(找不到记录)和 `ErrorCode.DATA_ACCESS_DENIED`(无权访问)来返回对应错误;如果你希望更精细的错误编码(便于前端识别或自动化监控),可以添加如下错误码:
java // 在 ErrorCode.java 中添加(可选) PATIENT_REMINDER_NOT_FOUND(7000, "患者提醒记录不存在"), PATIENT_REMINDER_ACCESS_DENIED(7001, "无权访问患者提醒记录");
## 9. 接口调用示例
### 9.1 获取提醒概览
GET /patient-reminder/overview
响应示例:
json { "code": 200, "message": "ok", "data": {
"reminder": {
"id": "1988502746686595073",
"patientUserId": "1988172854356631553",
"notificationEnabled": true,
"subscriptionAvailable": true,
"bloodPressureEnabled": true,
"bloodPressureTimes": ["07:00", "18:00"],
"bloodSugarEnabled": false,
"bloodSugarTimes": ["12:00"],
"heartRateEnabled": true,
"heartRateTimes": ["18:00"],
"medicationEnabled": true,
"createTime": "2025-11-20T10:00:00"
}
}, "timestamp": 1763570208009, "requestId": "xxx", "traceId": "xxx" }
### 9.2 保存患者提醒设置
POST /patient-reminder/save { "notificationEnabled": true, "subscriptionAvailable": true, "bloodPressureEnabled": true, "bloodPressureTimes": ["07:00", "18:00"], "bloodSugarEnabled": false, "bloodSugarTimes": ["12:00"], "heartRateEnabled": true, "heartRateTimes": ["18:00"], "medicationEnabled": true }
注意:当 notificationEnabled 为 false 时,服务端会把 subscriptionAvailable 自动设置为 false;如果调用方同时传入 subscriptionAvailable=true,保存后仍会被重置为 false(以保证一致性)。
### 9.3 删除患者提醒设置
DELETE /patient-reminder/1988502746686595073 ```
患者:
医生:
系统管理员:
本项目安全实现应遵循 docs/OLD/DevRule/08-安全规范.md 中的已有约定,以下为与提醒管理功能相关的要点(需在实现时落地):
认证与授权:
Authorization: Bearer <token>,兼容 X-Token / token)并通过 AuthInterceptor 校验,将 currentUserId/currentUserRole 注入请求上下文。输入校验:
异常与统一错误响应:
R.fail(code, message),并返回合适的 HTTP 状态码。日志与敏感信息:
SQL 注入与 ORM 使用:
最小权限原则:
审计与监控: