Wujies 无界平台
平台功能(自研)消息中心

我的消息

「我的消息」是消息中心的用户侧。所有接口都挂在 /system/messageCenter 下,只操作当前登录用户自己的数据,不需要分配权限。

接口

方法路径说明
GET/system/messageCenter/myMessages我的消息分页
PUT/system/messageCenter/markRead/{logId}单条标记已读
PUT/system/messageCenter/markAllRead全部标记已读
DELETE/system/messageCenter/{logIds}删除我的消息
GET/system/messageCenter/unreadCount未读数量
GET/system/messageCenter/settings读取消息偏好
PUT/system/messageCenter/settings保存消息偏好

分页参数与平台其他列表一致(pageNum / pageSize),查询条件:

参数说明
isReadY 已读 / N 未读,不传为全部
messageType消息类型,按 sys_message.message_type 过滤
messageTitle标题模糊搜索

列表按 send_time 倒序,返回的是 消息日志(SysMessageLogVo),因此同一个公告不会因为多通道而重复展示——每条消息对每个用户只有一条主日志记录。

已读逻辑

markRead 的处理顺序:

  1. 按 logId 查询日志;
  2. 校验 receiver_id 是否等于当前用户,不是自己的消息直接返回失败;
  3. 已读则幂等返回成功;
  4. 更新 is_read=Y、read_time;
  5. 重新统计该消息的已读人数并回写 sys_message.read_count;
  6. 未读计数器减一,并清除该用户的消息摘要缓存。

markAllRead 会把当前用户所有 is_read=N 的日志批量置为已读,并按消息去重回写已读人数,随后把未读计数清零。重复调用不会产生副作用。

删除

DELETE /system/messageCenter/{logIds} 要求传入的每一条日志都属于当前用户。服务端会先按 logIds + receiver_id 查询,数量不一致就整体拒绝,避免越权删除。删除成功后按数据库重新计算未读数。

删除只影响用户自己的收件箱,不会删除管理端的消息主体;重新发布该消息时,用户会再次收到。

消息偏好

设置按用户维度保存在 Redis,键为 GLOBAL_REDIS_KEY + message:settings:{userId},有效期 7 天。首次读取时返回默认值并写入缓存。

字段默认说明
systemNotifytrue站内消息通知
emailNotifytrue邮件通知
smsNotifyfalse短信通知
systemMessageTypes["system","maintenance"]订阅的系统类消息
businessMessageTypes["order","payment","refund"]订阅的业务类消息
activityMessageTypes["promotion"]订阅的活动类消息
doNotDisturbfalse免打扰开关
doNotDisturbStart / doNotDisturbEnd—免打扰时间段
autoCleanReadfalse自动清理已读消息
readRetentionDays30已读消息保留天数

当前实现边界:这些字段目前只负责保存和回显,投递链路尚未读取它们来决定是否发送。实际是否发邮件、发短信由消息本身的 send_method 决定。接入前不要向用户承诺「关闭邮件通知就一定收不到邮件」。

设置没有数据库表,属于缓存型偏好:

  • 7 天不访问会过期并恢复默认值;
  • 清除 Redis 或切换租户缓存后同样会丢失;
  • 如果业务需要长期保存,应在业务侧另建表,而不是依赖这份缓存。

未读计数与缓存

数据存储有效期失效方式
未读数量Redis 原子值 message:unread:count:{userId}5 分钟标记已读、全部已读、删除后更新
消息摘要Redis message:summary:{userId}15 秒已读操作后主动删除
消息偏好Redis message:settings:{userId}7 天保存时覆盖

未读数走 Redis 原子计数,避免每次打开页面都 count(*)。键不存在时会从数据库重新统计并回填;计数出现偏差时,删除或全部已读操作会以数据库为准重新校准。

SSE 推送的消息体里已经带有最新的 unreadCount,前端收到事件后直接刷新角标即可;页面初始化时再调用一次 unreadCount 作为兜底。

前端接入建议

// 打开消息中心
const page = await get('/system/messageCenter/myMessages', {
  pageNum: 1,
  pageSize: 20,
  isRead: 'N',
});

// 点击消息
await put(`/system/messageCenter/markRead/${log.logId}`);

// 全部已读
await put('/system/messageCenter/markAllRead');

配合 SSE 时注意:

  • SSE 连接按用户维度建立,多标签页会各自收到同一事件,未读数应以事件里的值为准,不要本地累加;
  • 消息列表页收到新消息事件后,可只更新角标,等用户下拉刷新再拉列表,避免打断阅读;
  • 删除接口返回失败时,优先检查传入的 logIds 是否混入了其他用户或其他环境的数据。

目录