Appearance
系统消息中心 - 使用指南
面向:业务后端开发(怎么发消息)、前端开发(怎么收消息) 代码位置:
hiapi-cloud-public/hiapi-system-basic-core/hiapi-system-basic-message-{entity,service,api}网关前缀:/cloud-api/**→ hiapi-cloud-public 更新:2026-07-18(阶段2:公告广播 + 事件接入)
一、两种模型,别用错
| 通知 Notification(推) | 公告 Announcement(拉) | |
|---|---|---|
| 模型 | 每接收者一行(hiapi_msg_notification) | 一条公告一行,全体可见(hiapi_msg_announcement) |
| 适用 | 定向:支付成功、审核结果、单发/群发运营消息 | 广播:版本公告、活动通知、维护公告 |
| 已读 | 逐条 readFlag + Redis 未读计数 | 个人已读游标(hiapi_msg_announcement_cursor),read-all 推进,不逐条 |
| 禁忌 | 别拿它做全员广播(会 fan-out 爆表) | 别拿它做定向消息(没有接收者维度) |
分类枚举(两者共用):TRADE / DEVICE / AUDIT / SECURITY / SYSTEM / MARKETING。
二、后端怎么发一条定向消息(三种方式)
方式1(推荐):发 NotifyEvent 事件,解耦
任何服务注入 EventPublisher,发到 SYSTEM_NOTIFY,消息中心 EventSystemNotifyListener 落库:
java
eventPublisher.publish(MsgRoutingKey.SYSTEM_NOTIFY, BaseEvent.builder()
.eventType(MsgRoutingKey.SYSTEM_NOTIFY)
.payload(NotifyEvent.builder()
.mid(mid)
.receiverType("user") // user / merchant / channel / platform
.receiverId(uid)
.category(MsgCategory.AUDIT.name())
.templateCode("WITHDRAW_PASS") // 与 bizId 联合幂等,可空
.bizId(withdrawNo) // 可空;两者都传才去重
.title("提现审核通过")
.content("你的提现申请已通过,金额将在24小时内到账。")
.extra(...) // 跳转上下文 {route,bizType,bizId},透传不解析
.build())
.build());幂等:templateCode + bizId + receiverType + receiverId 存在即跳过,MQ 重投/Outbox 重发都安全。
方式2:已接入的自动事件(业务方零改动)
| 业务事件 | 生产方(已在发) | 落信 |
|---|---|---|
PaymentSuccessEvent(finance.pay.success.#) | finance FinancePaymentLogic(Outbox) | 用户 TRADE「支付成功」,bizId=payCode |
UserRegisterEvent(user.register) | user UserAccountService | 用户 SYSTEM「欢迎加入」,bizId=uid |
方式3:商户后台手动发
POST /cloud-api/merchant/message/notification/send,体 {receiverIds:[uid], title, content, category?}(缺省 MARKETING)。不做幂等,每次调用都新增。入口:后台会员列表页勾选群发/行内单发。
三、公告(广播)怎么用
商户后台管理(页面:会员管理 → 公告消息 /user/announcement)
/cloud-api/merchant/message/announcement:
| 端点 | 说明 |
|---|---|
GET /query?page=&size=&title=&category=&status= | 分页(BasicQuery 规范) |
POST /add | 发布。字段:title(必填)/category(缺省 SYSTEM)/content/extra/status(缺省1)/startTime(缺省立即)/endTime(<=0 或不传=永久,落库转哨兵 4102415999000)/sort |
POST /edit | 编辑,{id, ...};status 1/0 即上/下架;mid/receiverType/created 不可改 |
DELETE|GET /delete?id= | 删除 |
用户端读取
/cloud-api/user/message/announcement:
| 端点 | 说明 |
|---|---|
GET /query?page=&size=&category= | 只返回生效中(上架+在生效期)的公告,每条带 readFlag(created<=个人游标) |
POST /read-all | 全部已读(推进游标;进入公告列表页时调用) |
GET /unread-count | {announcement: n} |
GET /cloud-api/user/message/notification/unread-count 现在返回 {notification: n, announcement: n} 两个真实值,铃铛角标建议取两者之和。
平台向下广播(platform → merchant / channel,全局,mid 固定 0)
发布(平台后台「公告管理」/platform/announcement):/cloud-api/platform/message/announcement,POST /add 时 receiverType 必填选 merchant 或 channel,mid 固定 0,发布后 receiverType 不可改。CRUD 端点同商户侧。
接收:
- 商户读:
/cloud-api/merchant/message/platform-announcement(GET /query、POST /read-all、GET /unread-count) - 渠道读:
/cloud-api/channel/message/platform-announcement(同上)
注意:这两个接收端点与 /merchant\|channel/message/notification/unread-count 里的 announcement 字段互不相通——后者查询固定用"自己的 mid"(对应 merchant→user 场景的公告),平台公告 mid=0,不会被那个字段捕获到。要展示平台公告未读数,前端需单独调 platform-announcement/unread-count 再和铃铛数字相加。
不支持 channel→merchant(渠道只广播给自己名下商户)——ChannelMerchant(channelId+mid)关系表虽存在,但要做到"仅本渠道商户可见"需要给 MsgAnnouncement 加 channelId 维度,目前未做。
四、收件箱接口(四账户通用,阶段0 已有)
/cloud-api/{merchant|channel|platform|user}/message/notification: GET /query(category/readFlag 过滤)、POST /read {ids:[]}、POST /read-all、GET /unread-count。
商户侧另有 GET /sent?page=&size=&title=&category=:本商户发给用户的定向消息记录(手动 send + 自动事件),后台「公告消息 → 发送记录」Tab 在用。
五、接入新事件监听(消息中心侧开发规范)
- 监听器放
hiapi-system-basic-message-service的event/包(该模块已依赖hiapi-core-event-sdk且是 boot 期依赖)。 - 实现
IEventMessageListener<XxxEvent>,队列名hiapi-cloud-public-message-<语义>。 - 禁止构造器/字段注入业务 Service——RabbitConfig 在 BeanDefinitionRegistryPostProcessor 阶段就实例化监听器,统一在
onMessage里dispatchContext.getServiceOne(...)。 - 禁止用 Spring
@EventListener收跨服务事件(本地事件收不到 MQ,阶段2 修过这个 bug)。 - 落库统一走
MsgNotificationService.saveNotification,尽量带 templateCode+bizId 拿幂等。
六、用户端 widget(hiapi-cloud-public-web,装修组件体系)
用错项目会白做:消息中心属于 hiapi-cloud-public 服务,组件家目录是 hiapi-cloud-public/hiapi-cloud-public-web/src/components/hiapi-public/(appId: 'A-10000')。 不是 顶层 uniapp-design/ 文件夹——那个文件夹的 package.json name 实际是 hiapi-cloud-vem,是 VEM 项目的装修组件源。
已提供两个 widget(src/components/index.ts 已注册):
| Widget | 数据源 | 说明 |
|---|---|---|
hiapi-public-message-entry | /user/message/notification/unread-count | 图标+标题+未读红点(notification+announcement 求和),点击跳 /pages/user-center/message |
hiapi-public-announcement-bar | /user/message/announcement/query | 滚动展示生效中公告,点击跳 /pages/user-center/message?mode=announcement |
配套页面 src/pages/user-center/message.vue:「消息/公告」双 Tab,?mode=announcement 可直达公告 Tab。该页此前请求前缀写的是不存在的 /cloud-message,已修复为 /cloud-api——如果你在其它项目复制过这个页面的旧版本,记得同步修这个 bug。
七、已知边界
- 提现审核等 finance 事件未发布,接入先补事件(见
hiapi-cloud-finance/docs/completion-plan.md§P3 第11项)。 - channel→merchant(渠道只广播给自己名下商户)未做,需要
MsgAnnouncement加 channelId 维度。 - 平台公告(mid=0)的未读数不在通用铃铛聚合接口里,需单独拉取,见 §三"平台向下广播"。
- user 服务旧
Notify{Body,Target,Type}已删除(2026-07-18),新功能一律走本消息中心。 hiapi-cloud-public-web本地缺@uni-helper/uni-types等类型包,vue-tsc跑不通(环境问题,与消息中心功能无关)。