/messages/send |
POST |
医生发送消息 |
医生 |
SendMessageRequest (receiverIds, content, etc.) |
R (消息ID)
|
/messages/system-daily-send |
POST |
系统发送日常消息 |
管理员 |
SystemDailySendRequest (receiverIds, title, content) |
R (消息ID)
|
/messages/system-anomaly-send |
POST |
系统发送异常通知 |
系统/管理员 |
SystemAnomalySendRequest (patientId, anomalyData) |
R (消息ID)
|
/messages/list |
GET |
获取消息列表(分页) |
患者/家属 |
查询参数: page, size, status |
R>
|
/messages/{id} |
GET |
获取消息详情 |
患者/家属 |
路径参数: id |
R
|
/messages/{id}/read |
PUT |
标记消息已读 |
患者/家属 |
路径参数: id |
R
|
/messages/{id} |
DELETE |
删除消息 |
患者/家属 |
路径参数: id |
R
|
/messages/unread-count |
GET |
获取未读消息数量 |
患者/家属 |
无 |
R
|
/messages/doctor/patient/{patientId} |
GET |
医生获取患者及其家属消息列表 |
医生 |
路径参数: patientId |
R
|
API设计示例
医生发送消息接口
@Operation(summary = "医生发送消息", description = "医生向患者/家属发送站内消息")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "发送成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class))),
@ApiResponse(responseCode = "400", description = "参数错误",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class))),
@ApiResponse(responseCode = "401", description = "未认证",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class)))
})
@PostMapping("/messages/send")
public R<String> sendMessage(@RequestBody SendMessageRequest request) {
// 校验参数、保存消息、触发推送、返回ID(String格式)
}
获取消息列表接口
@Operation(summary = "获取消息列表", description = "分页获取用户消息列表")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "获取成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class)))
})
@GetMapping("/messages/list")
public R<Page<MessageVO>> getMessageList(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) Integer status) {
// 返回分页列表,ID转为String
}
医生获取患者及其家属消息列表接口
@Operation(summary = "医生获取患者及其家属消息列表", description = "医生查看指定患者及其所有家属的消息记录")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "获取成功",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class))),
@ApiResponse(responseCode = "403", description = "权限不足",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class))),
@ApiResponse(responseCode = "404", description = "患者不存在",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = R.class)))
})
@GetMapping("/messages/doctor/patient/{patientId}")
public R<DoctorPatientMessagesVO> getPatientAndFamilyMessages(@PathVariable String patientId) {
// 校验医生权限、查询患者及其家属消息、返回结构化数据
}
配置示例(application.yml)
# MyBatis配置 - 注册MessageTypeHandler
mybatis:
type-handlers-package: work.baiyun.chronicdiseaseapp.handler
# 其他配置复用现有推送和数据库配置
数据库字段设计与兼容性
消息表 (t_messages)
- id: BIGINT PRIMARY KEY AUTO_INCREMENT(雪花算法生成,响应时转为String)
- sender_id: BIGINT NOT NULL, 发送者ID(系统消息为0)
- receiver_id: BIGINT NOT NULL, 接收者ID
- content: TEXT NOT NULL, 消息内容
- content_format: VARCHAR(20) DEFAULT 'PLAIN', 内容格式
- type: VARCHAR(50) NOT NULL, 消息类型(使用MessageType枚举:SYSTEM_DAILY-系统日常消息,DOCTOR-医生下发的消息,SYSTEM_ANOMALY-异常数据通知)
- notify_popup: TINYINT DEFAULT 0, 是否弹窗
- notify_subscribe: TINYINT DEFAULT 0, 是否订阅推送
- status: TINYINT DEFAULT 0, 状态(0未读1已读2已删除)
- read_time: DATETIME, 阅读时间
- is_deleted: TINYINT DEFAULT 0, 软删除标志
- create_time: DATETIME DEFAULT CURRENT_TIMESTAMP(BaseEntity字段)
- update_time: DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(BaseEntity字段)
- create_user: BIGINT(BaseEntity字段,创建者ID,转为String)
- update_user: BIGINT(BaseEntity字段,更新者ID,转为String)
- version: INT DEFAULT 0(BaseEntity字段,乐观锁版本号)
- remark: VARCHAR(255)(BaseEntity字段,可选备注)
注:实体类继承BaseEntity,自动包含上述公共字段。id、create_user、update_user在JSON响应中通过ToStringSerializer转为String。
消息类型枚举设计
MessageType枚举
package work.baiyun.chronicdiseaseapp.enums;
public enum MessageType {
SYSTEM_DAILY("系统日常消息"),
DOCTOR("医生下发的消息"),
SYSTEM_ANOMALY("异常数据通知");
private final String description;
MessageType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
MessageTypeHandler类型处理器
package work.baiyun.chronicdiseaseapp.handler;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import work.baiyun.chronicdiseaseapp.enums.MessageType;
@MappedTypes(MessageType.class)
public class MessageTypeHandler extends BaseTypeHandler<MessageType> {
// 处理MessageType枚举与数据库VARCHAR字段间的转换
// 存储时:enum.name() -> 数据库字符串
// 查询时:数据库字符串 -> MessageType.valueOf(name)
}
注:MessageTypeHandler继承自MyBatis的BaseTypeHandler,实现枚举与数据库字段的自动转换。通过@MappedTypes注解指定处理的枚举类型,确保数据库中的VARCHAR字段能正确映射为MessageType枚举值。
数据传输对象设计
请求VO
SendMessageRequest(医生发送消息请求)
@Schema(description = "医生发送消息请求")
public class SendMessageRequest {
@Schema(description = "接收者ID列表", required = true)
@NotEmpty(message = "接收者不能为空")
private List<Long> receiverIds;
@Schema(description = "消息内容", required = true)
@NotBlank(message = "内容不能为空")
private String content;
@Schema(description = "内容格式", example = "PLAIN")
private String contentFormat = "PLAIN";
@Schema(description = "消息类型", required = true, example = "DOCTOR")
@NotNull(message = "类型不能为空")
private MessageType type;
@Schema(description = "是否通知家属")
private Boolean notifyFamily = false;
@Schema(description = "是否弹窗")
private Boolean notifyPopup;
@Schema(description = "是否订阅推送")
private Boolean notifySubscribe;
}
SystemDailySendRequest(系统发送日常消息请求)
@Schema(description = "系统发送日常消息请求")
public class SystemDailySendRequest {
@Schema(description = "接收者ID列表", required = true)
@NotEmpty(message = "接收者不能为空")
private List<Long> receiverIds;
@Schema(description = "消息标题")
private String title;
@Schema(description = "消息内容", required = true)
@NotBlank(message = "内容不能为空")
private String content;
@Schema(description = "消息类型", required = true, example = "SYSTEM_DAILY")
@NotNull(message = "类型不能为空")
private MessageType type = MessageType.SYSTEM_DAILY;
}
SystemAnomalySendRequest(系统发送异常通知请求)
@Schema(description = "系统发送异常通知请求")
public class SystemAnomalySendRequest {
@Schema(description = "患者ID", required = true)
@NotNull(message = "患者ID不能为空")
private Long patientId;
@Schema(description = "异常数据详情")
private Object anomalyData;
@Schema(description = "消息类型", required = true, example = "SYSTEM_ANOMALY")
@NotNull(message = "类型不能为空")
private MessageType type = MessageType.SYSTEM_ANOMALY;
}
响应VO
MessageVO(消息信息响应)
@Schema(description = "消息信息")
public class MessageVO {
@Schema(description = "消息ID", example = "1234567890123456789")
private String id;
@Schema(description = "发送者ID", example = "1234567890123456789")
private String senderId;
@Schema(description = "发送者姓名")
private String senderName;
@Schema(description = "接收者ID", example = "1234567890123456789")
private String receiverId;
@Schema(description = "消息内容")
private String content;
@Schema(description = "内容格式", example = "PLAIN")
private String contentFormat;
@Schema(description = "消息类型", example = "DOCTOR")
private MessageType type;
@Schema(description = "是否弹窗")
private Boolean notifyPopup;
@Schema(description = "是否订阅推送")
private Boolean notifySubscribe;
@Schema(description = "状态", example = "0")
private Byte status;
@Schema(description = "阅读时间", example = "2023-12-01 10:00:00")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime readTime;
@Schema(description = "创建时间", example = "2023-12-01 10:00:00")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}
注:所有ID字段在VO中定义为String类型,避免前端精度丢失。时间字段使用@JsonFormat格式化。senderName通过注入UserInfoMapper查询UserInfo.nickname填充。type字段使用MessageType枚举类型,在JSON序列化时会转换为枚举名称字符串。
DoctorPatientMessagesVO(医生患者消息响应)
@Schema(description = "医生患者消息响应")
public class DoctorPatientMessagesVO {
@Schema(description = "患者消息列表")
private List<MessageVO> patientMessages;
@Schema(description = "家属消息列表")
private List<MessageVO> familyMessages;
}
注:该VO用于医生查看指定患者及其家属的消息记录,将患者和家属的消息分别组织在不同的列表中,便于前端展示和管理。
联动查询设计
消息模块涉及跨表查询用户信息,为填充MessageVO中的senderName字段:
- 注入依赖:MessageServiceImpl注入UserInfoMapper。
- 查询逻辑:查询消息列表或详情时,根据message.sender_id调用userInfoMapper.selectById(senderId),获取UserInfo对象。
- 字段映射:将userInfo.getNickname()赋值给messageVO.setSenderName()。
- 异常处理:若用户信息不存在,senderName设为空字符串或默认值,避免查询失败。
- 性能优化:对于列表查询,可批量查询用户信息,或使用缓存减少数据库访问。
权限与安全
- 发送接口仅医生/管理员角色可操作。
- 接收接口公开,但需认证。
- 医生专用接口(如获取患者及其家属消息列表)仅医生角色可访问,且需验证医生与患者的绑定关系。
日志与审计
- 操作日志:
[MessageOperation] userId={}, action={}, messageId={}
- 异常日志:
[MessageError] userId={}, error={}
- 使用SLF4J,日志级别INFO/ERROR。
- ID在日志中以字符串记录。
测试建议
- 单元测试:CRUD逻辑、状态管理。
- 集成测试:接口联调、推送触发。
- 性能测试:列表分页、状态更新。
兼容性与前端要求
- 前端需支持消息列表、详情、状态管理。
- 响应中ID字段为String,MessageType枚举字段在JSON中以字符串形式返回(如"DOCTOR")。
- 前端可根据消息类型字符串进行相应的UI展示和逻辑处理。
迁移/变更管理