Skip to content

系统消息中心 - 实施进展

状态: 阶段2(公告广播+事件接入+公告管理页)+ 阶段3(删 user 旧 Notify)+ 阶段4(channel/platform 公告发布面 + uniapp 消息 widget)代码完成,待部署验证 日期: 2026-07-19(阶段0: 2026-07-05,阶段2/3: 2026-07-18) 版本: Phase 4 - Full-Hierarchy Announcements & User-Facing Widgets


已完成

后端 (Java)

1. 模块创建 ✓

  • hiapi-system-basic-message-entity — 消息模型
  • hiapi-system-basic-message-service — 消息服务
  • hiapi-system-basic-message-api — API 控制层

2. 数据模型 ✓

  • MsgNotification.java — JPA 实体,含收件箱字段、分类、已读标记
    • 索引优化: (receiverType, receiverId, readFlag, created) 加速列表查询
    • 去重保障: (templateCode, bizId, receiverType, receiverId) 联合唯一防重复投递
  • MsgCategory.java — 枚举 (TRADE/DEVICE/AUDIT/SECURITY/SYSTEM/MARKETING)

3. 业务逻辑 ✓

  • MsgNotificationService.java — 核心方法
    • saveNotification() — 写入收件箱 + 去重 + Redis 未读计数增加
    • read(ids) — 批量标记已读 + Redis 计数减少
    • readAll() — 清空所有未读 + 删除 Redis 计数 key
    • unreadCount() — 读 Redis 优先,缓存未命中从 DB 自愈回源 + 7 天 TTL 过期

4. API 层 ✓

  • 四账户类型 Controller (merchant/channel/platform/user)

    • 各自直接 extends BasicQueryController<MsgNotification,...>,注入共享 NotificationControllerSupport bean 委托 setQuery/toListVo/toDataVo/read/readAll/unreadCount(详见下方"已修复问题")
    • 自动从 Token 提取身份信息
    • 统一路由: /merchant/message/notification/*, /channel/message/notification/*
  • 标准 HTTP 端点

    • GET /query — 分页列表,支持 category/readFlag/properties/direction
    • POST /read — 批量标记已读
    • POST /read-all — 全部已读
    • GET /unread-count — 返回 {notification: count, announcement: 0}
  • Query/VO 类型

    • MsgNotificationQuery — 查询包装器,支持多维过滤
    • MsgNotificationVo — 响应值对象

5. 事件监听 ✓

  • EventSystemNotifyReceipt.java — 监听 SYSTEM_NOTIFY 事件,存入收件箱
    • 其他事件监听(FINANCE_PAY_SUCCESS/USER_REGISTER)暂时省略,留作第 2 期实现

6. Maven 集成 ✓

  • 三个子模块加入 hiapi-system-basic-core <modules>
  • 依赖管理加入 hiapi-fast-frame pom.xml
  • hiapi-cloud-public-app 依赖引入 message-api
  • 编译通过 ✓ (clean install -DskipTests 成功)

前端 (Admin Dashboard)

1. 组件 ✓

  • MessageBell.vue — 顶部消息铃
    • 后台 60s 轮询未读数 (showError: false 静默失败)
    • 下拉显示最近 5 条未读消息
    • "全部已读"按钮
    • 自动路由导航到消息中心

2. 页面 ✓

  • views/message/index.vue — 消息管理页
    • TablePage 组件 (自带分页/搜索/排序)
    • 分类过滤 + 已读状态过滤
    • 详情弹窗 (打开时自动标记已读)
    • 全部已读批量操作

3. 国际化 ✓

  • 中英文翻译文件 (src/i18n/zh-cn/message.json, src/i18n/en/message.json)
  • 覆盖: bell 组件、分类标签、表格列、过滤器、按钮文案

4. API 路径更新 ✓

  • MessageBell: /api/cloud-api/{accountType}/message/notification/...
  • Message Index: 同上
  • 四账户类型路由: /message, /channel/message, /platform/message

文档与规划

1. 总体规划文档 ✓

  • 更新模块位置 (独立 → 集成到 hiapi-system-basic-core)
  • 更新网关前缀 (/cloud-message → /cloud-api)
  • 路由流向详细说明

2. 项目记忆 ✓

  • /Users/adinz/.claude/projects/*/memory/project_message_center.md 已更新

待完成 (后续阶段)

第 1 阶段: 公告系统

  • [ ] MsgAnnouncement 实体 (一对多,拉模式)
  • [ ] 公告查询、发布、过期管理 API
  • [ ] 前端公告弹窗组件

第 2 阶段: 多渠道派发

  • [ ] 模板两层架构 (业务模板 + 渠道模板)
  • [ ] ISenderService 集成 (SMS/EMAIL 复用现有体系)
  • [ ] MQ 事件分发 (NotifyEvent/FinanceEvent/UserEvent 监听)
  • [ ] Outbox 模式 + 重试机制

第 3 阶段: 社交渠道

  • [ ] 微信公众号/小程序集成
  • [ ] UniPush 2.0 推送

第 4 阶段: 统计与运营

  • [ ] 消息统计表 (发送数/成功数/失败率)
  • [ ] 后台运营面板

验证清单

本地编译

  • [x] mvn clean install -DskipTests 通过,生成 message-entity/service/api 三个 JAR

部署

  • [ ] docker-compose 启动 MySQL、Redis、后端应用
  • [ ] 后端日志确认无异常

功能测试

后端

  • [ ] 新建消息: POST /api/cloud-api/merchant/message/notification(需关联 NotifyEvent 发送)
  • [ ] 查询列表: GET /api/cloud-api/merchant/message/notification/query?page=1&size=10
  • [ ] 未读数: GET /api/cloud-api/merchant/message/notification/unread-count{notification: N, announcement: 0}
  • [ ] 标记已读: POST /api/cloud-api/merchant/message/notification/read 单条/批量
  • [ ] 全部已读: POST /api/cloud-api/merchant/message/notification/read-all
  • [ ] 多账户隔离: platform/channel/user 账户只能读自己的消息

前端

  • [ ] MessageBell 显示未读数 (红点 badge)
  • [ ] 60s 轮询自动刷新
  • [ ] 下拉菜单显示最近 5 条
  • [ ] 点击条目进入消息中心详情
  • [ ] 消息中心分类/已读过滤
  • [ ] 全部已读按钮功能

性能验证

  • [ ] Redis 未读计数命中率 > 90%
  • [ ] 单用户查询响应 < 100ms
  • [ ] 未读计数 API 响应 < 50ms (Redis hit 情况)

关键代码位置

模块关键文件
后端实体hiapi-system-basic-message-entity/src/main/java/cn/hiapi/system/basic/message/entity/MsgNotification.java
后端服务hiapi-system-basic-message-service/src/main/java/cn/hiapi/system/basic/message/service/MsgNotificationService.java
后端 API 共享逻辑hiapi-system-basic-message-api/src/main/java/cn/hiapi/system/basic/message/api/NotificationControllerSupport.java
后端控制器hiapi-system-basic-message-api/.../MerchantNotificationController.java (×4 账户类型,各自直接 extends BasicQueryController,委托给 NotificationControllerSupport)
前端组件hiapi-cloud-admin-ts/src/components/MessageBell.vue
前端页面hiapi-cloud-admin-ts/src/views/message/index.vue
前端 i18nhiapi-cloud-admin-ts/src/i18n/{zh-cn,en}/message.json
Maven 配置hiapi-system-basic-core/pom.xml + hiapi-cloud-public-app/pom.xml

已知限制与 TODO

  • [ ] 事件监听器 (EventFinancePayNotifyReceipt/EventUserRegisterNotifyReceipt) 暂未实现,等第 2 阶段多渠道派发时完善
  • [ ] 公告(Announcement)表结构已规划,代码未开发,留作第 1 阶段
  • [ ] 渠道配置管理 API 未开发 (第 2 阶段)
  • [ ] 订阅偏好表 (msg_user_preference) 未开发 (第 2 阶段)

已修复问题

启动期 BeanCreationException (2026-07-05)

现象: 应用启动时 4 个 NotificationController(Merchant/Channel/Platform/User)全部报 Constructor threw exceptionClassCastException

根因: BasicQueryController 构造函数用反射拿实体类型:

java
Type type = getClass().getGenericSuperclass();
this.tClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];

这只看直接父类的泛型参数。原设计加了一层 AbsNotificationController extends BasicQueryController<MsgNotification,...>(非泛型,硬编码具体类型),四个账户控制器再 extends AbsNotificationController——此时它们的直接父类是非泛型的 AbsNotificationController,getGenericSuperclass() 拿到的是普通 Class 而非 ParameterizedType,强转必然抛出 ClassCastException

(已用 Explore 子代理确认:整个代码库中没有"中间抽象类保持泛型透传"的先例,唯一同类模式即本次踩坑的实现;其余多账户控制器都是直接 extends BasicQueryController<Concrete,...>,没有中间层。)

修复: 去掉 AbsNotificationController 继承层,改为组合复用:

  • 新增 NotificationControllerSupport(普通 @Component,非 Controller),承载原来的 setQuery/toListVo/toDataVo/read/readAll/unreadCount 共享逻辑。
  • 四个账户控制器改为直接 extends BasicQueryController<MsgNotification, Long, MsgNotificationVo, MsgNotificationQuery>,构造函数多注入一个 NotificationControllerSupport,各端点/覆写方法都是一行委托。

验证: mvn clean install -DskipTestshiapi-cloud-public 目录下退出码 0,无编译错误(尚未做运行时 Spring 上下文启动验证,建议部署到测试环境后确认 4 个 Controller Bean 均能正常创建)。


商户手动发消息给用户 (2026-07-05)

背景: 部署验证时发现,原设计里写入消息的唯一入口是 NotifyEvent(业务事件自动转通知),但代码库里没有任何地方发布过这个事件,商户后台也没有任何"发消息"的功能——商户完全没有办法主动给用户发一条站内信。

新增:

  • 后端 NotificationControllerSupport.send(mid, receiverType, MsgSendRequest):批量对 receiverIds 逐个调用 saveNotification(不传 templateCode/bizId,不做幂等去重,每次都新增)。
  • MerchantNotificationController 新增 POST /merchant/message/notification/send,固定 receiverType=usermid=当前商户,请求体 {receiverIds:[], title, content, category?}(category 缺省 MARKETING)。
  • 前端 views/user/index.vue(商户用户管理页):
    • 新增 selection 多选列 + 头部"群发消息"按钮(对勾选用户批量发)
    • 行内新增"发消息"按钮(单个用户)
    • 发送对话框:接收人数 + 标题 + 内容,提交调 POST /cloud-api/merchant/message/notification/send

顺带修复的 pom 问题: 编译时发现 hiapi-cloud-public-app 依赖 hiapi-system-basic-message-api 缺版本号。根因不在 hiapi-system-basic-core/pom.xml(该聚合 pom 的 dependencyManagement 对 sibling 模块不生效),而是 hiapi-cloud-public 的实际 Maven parent 是已安装到本地仓库hiapi-fast-frame(<relativePath/> 从仓库查找),其 pom.xml 里虽然已经声明了这个依赖的版本,但本地 ~/.m2 缓存的是加了这条声明之前的旧版本 POM。执行 cd hiapi-fast-frame && mvn install -N -q 重新安装父 POM 后,hiapi-cloud-publicmvn clean install -DskipTests 才恢复正常(退出码 0)。

仍未做: channel/platform 账户是否也需要"发消息给自己下面的商户/渠道"能力(如平台运营手动通知某商户)——本次只做了商户→用户这一条路径,按需再加。


阶段2:公告广播 + 事件接入修复 + 公告管理页 (2026-07-18)

背景决策:user 服务的旧 Notify{Body,Target,Type} 体系确定废弃,全系统用户消息/公告统一由消息中心承载(删除动作另行执行,见 hiapi-docs/TODO.md 0 号待办)。

1. 公告广播(拉模式,补齐 announcement 恒 0 的缺口)

模型:一条公告一行,不逐用户 fan-out;接收端用"生效中公告 + 个人已读游标"合并计算未读。

  • MsgAnnouncement(表 hiapi_msg_announcement):mid + receiverType + category + title/content/extra + status(1上架/0下架) + startTime/endTime + sort。endTime 入参 <=0 表示永久,落库统一转哨兵值 FOREVER=4102415999000(2099-12-31),避免查询出现 OR endTime=0。索引 (mid, receiverType, status, created)
  • MsgAnnouncementCursor(表 hiapi_msg_announcement_cursor):每接收者一行的已读游标 lastReadTime,唯一键 (receiverType, mid, receiverId)。未读数 = 生效中公告里 created > lastReadTime 的条数;read-all 推进游标(upsert,并发插入冲突退化为 update),不做逐条已读
  • Service:MsgAnnouncementService(activeWrapper/unreadCount/readAll)+ MsgAnnouncementCursorService(getCursorTime/touch)。
  • 接口:
    • 商户管理 /merchant/message/announcement:GET /queryPOST /addPOST /edit(含 status 上下架)、DELETE|GET /delete(BasicCurdController,receiverType 固定 user,mid/receiverType/created 不可改)。
    • 用户端 /user/message/announcement:GET /query(只出生效中的,每条带 readFlag)、POST /read-allGET /unread-count
    • NotificationControllerSupport.unreadCountannouncement 从恒 0 改为真实值(对四类账户通用)。

2. 事件接入修复(发现并修复一个"永远收不到"的 bug)

根因:旧 EventSystemNotifyReceipt 用 Spring 本地 @EventListener,而全系统跨服务事件走 RabbitMQ(IEventMessageListener + RabbitConfig 在 boot 期 postProcessBeanDefinitionRegistry 注册队列/绑定)——本地监听器根本收不到 MQ 消息,这就是"业务事件自动通知未接入"的真正原因。

修复与新增(全部放在 hiapi-system-basic-message-serviceevent/ 包——该模块已依赖 hiapi-core-event-sdk,且为 boot 期依赖,监听器 bean 在队列注册阶段可见;参照同服务 OperationLogListener 先例;不注入字段依赖,统一在 onMessage 里 dispatchContext.getServiceOne(...),因为 RabbitConfig 在 BeanDefinitionRegistryPostProcessor 阶段就会实例化监听器):

监听器队列routingKey行为
EventSystemNotifyListener(替代旧 Receipt)hiapi-cloud-public-message-system-notifySYSTEM_NOTIFY通用通道:业务方发 NotifyEvent 即落站内信
EventFinancePaySuccessListenerhiapi-cloud-public-message-finance-payFINANCE_PAY_SUCCESS_ALL(通配)支付成功→用户 TRADE 通知,templateCode=PAY_SUCCESS,bizId=payCode(finance Outbox 重投不重复)
EventUserRegisterListenerhiapi-cloud-public-message-user-registerUSER_REGISTER注册→SYSTEM 欢迎信,bizId=uid

生产方无需改动:finance 已经在发 PaymentSuccessEvent(FinancePaymentLogic,经 Outbox),user 已经在发 UserRegisterEvent(UserAccountService)。

已知缺口:提现审核没有对应事件(event-shared 里无 WITHDRAW key),接入需 finance 先补事件发布,留待后续。

3. 商户后台公告管理页(admin-ts)

  • 新页 views/user/announcement/index.vue,双 Tab:
    • 公告管理:TablePage CRUD(标题/分类/状态筛选,发布/编辑弹窗:标题+分类+内容+生效/失效时间+排序+状态开关;失效时间留空=永久;行内 编辑/上下架/删除)。
    • 发送记录:GET /merchant/message/notification/sent(后端本次新增,mid+receiverType=user 倒序分页),前端按 time-ka 模式回填接收用户资料(/cloud-user/merchant/user/user-list?ids=,try/catch 失败只显示 ID)。
  • 菜单 pages.ts 50503000「公告消息」由死链 /user/notify 改指 /user/announcement,路由注册,删除空壳 views/user/notify/index.vue
  • i18n:message.json zh/en 新增 announcement.*/sent.*

4. 验证状态

  • hiapi-cloud-public mvn compile BUILD SUCCESS(产物核对过时间戳;首次编译曾因 NotificationControllerSupport 漏 import 失败,已修复)。
  • admin-ts vue-tsc --noEmit 通过。
  • 未做运行时验证:MQ 队列创建/绑定、三张新表 DDL(JPA 自动建表)、公告端到端读写,需部署到测试环境确认。

5. 阶段2未含(后续)

  1. 删除 user 服务旧 Notify 体系 → 见阶段3
  2. uniapp-design 用户端消息中心 widget(列表/未读红点/公告)
  3. 提现审核事件接入(依赖 finance 补事件)
  4. channel/platform 的公告发布面(实体已带 receiverType,扩展即可)

阶段3:删除 user 服务旧 Notify 体系 (2026-07-18)

阶段2 落地后,用户消息/公告已完全由消息中心承载,hiapi-cloud-user 里的旧 Notify{Body,Target,Type} 体系不再有新增使用场景,予以删除。

删前核查(两处均确认干净,才动手删):

  • uniapp-design 全仓库搜索 /user/notify:无引用。
  • admin-ts 用户列表页「发送消息」(views/user/index.vuesendMsg):已在阶段0 切到 POST /cloud-api/merchant/message/notification/send,核实确认无误。
  • 全仓库搜索 NotifyBody/NotifyTarget/NotifyType/NotifyBodyService/NotifyTargetService/NotifyTypeService 的跨服务引用:无。

删除范围(hiapi-cloud-user,共 19 个文件):

  • C端 NotifyController(/user/notify)+ NotifyQuery + NotifyVo/NotifyTypeVo(hiapi-user-api)
  • 商户后台 NotifyController/NotifyTypeController + NotifyQuery/NotifyTypeQuery + NotifyVo/NotifyTypeVo(hiapi-user-merchant-api)
  • 实体 NotifyBody/NotifyTarget/NotifyType(hiapi-user-entity)
  • NotifyBodyJpa/NotifyTargetJpa/NotifyTypeJpa + NotifyBodyService/NotifyTargetService/NotifyTypeService(hiapi-user-service)

数据库旧表(hiapi_core_notify_*)保留不动,只下线代码,不做迁移/清库。

验证:hiapi-cloud-user 目录 mvn compile 全 9 个子模块 BUILD SUCCESS(产物时间戳核对过)。


阶段4:channel/platform 公告发布面 + 用户端消息 widget(2026-07-19)

1. 公告扩展到全层级(platform ↔ merchant/channel)

阶段2 的公告只做了 merchant→user 一条方向。本阶段补齐平台向下广播:

  • 模型不变,新增两种 receiverType 用法:MsgAnnouncement.mid 在 merchant→user 场景是"发布方商户的 mid"(租户内广播);平台广播新增用法——mid 固定 0,表示"全局,不区分商户/渠道"。已读游标 MsgAnnouncementCursorreceiverId 在这种场景下 = 商户/渠道自己的 mid(用于区分"谁读过"),但游标的 mid 参数固定传 0(与公告落库的 mid 对齐,不是商户自己的 mid)——这一点容易搞混,踩过一次坑:一开始想复用"query 用 getMid() 注入" 的通用逻辑,发现如果 getMid() 返回商户自己的 mid,查询条件 eq(mid, 该值) 永远查不到 mid=0 的平台公告行,必须让接收平台公告的 controller 的 getMid() 硬编码返回 0。
  • 发布端:新增 PlatformAnnouncementController(/platform/message/announcement,BasicCurdController),receiverType 由管理员在创建表单选择 merchantchannel(校验合法值),mid 固定 0,编辑时 mid/receiverType/created 不可改(避免已读游标语义错乱)。
  • 接收端:抽取 AnnouncementControllerSupport(仿照阶段0 NotificationControllerSupport 的组合复用先例,见 [[project_basicquerycontroller_generic_reflection]]/decisions/0005),把阶段2 UserAnnouncementController 里内联的"查询注入+VO转换+已读游标"逻辑抽出来复用,同时重构 UserAnnouncementController 改为委托调用。新增 MerchantPlatformAnnouncementController(/merchant/message/platform-announcement)、ChannelPlatformAnnouncementController(/channel/message/platform-announcement),两者 getMid() 都硬编码返回 0,receiverId()TokenGet.getMid()(该账户类型自己的身份 id)做已读游标区分。
  • 未接入通用未读铃铛:MerchantNotificationController.unread-count 现有的 announcement 字段查询用的是"商户自己的 mid"(用于 merchant→user 场景的公告),不会匹配 mid=0 的平台公告,因此平台公告的未读数走各自独立的 /merchant\|channel/message/platform-announcement/unread-count,前端需要另外拉取并自行合并到红点数字里,没有动阶段0 已上线的铃铛聚合逻辑(降低回归风险)。
  • 不做的:channel→merchant(渠道只广播给自己名下商户)。仓库里确有 ChannelMerchant(channelId+mid)关系表,但要支持"仅本渠道商户可见"需要给 MsgAnnouncement 加 channelId 维度 + 查询侧关联,属于 schema 变更,超出本次"扩展即可"的量级,留待真有需求再做。
  • 前端 admin-ts:views/message/index.vue(merchant/channel 共用的消息中心页)加"平台公告"Tab(showAnnouncementTab 按 accountType 显示);新增 views/platform/announcement/index.vue(平台发布 CRUD,含接收对象 merchant/channel 选择器,发布后不可改),菜单 pages.ts 新增 100700000「公告管理」。

2. 用户端消息中心 widget(hiapi-cloud-public-web)

关键发现:目标不是 uniapp-design(顶层文件夹的 package.json name 实际是 hiapi-cloud-vem,是 VEM 项目的装修组件源,和 hiapi-cloud-vem/vem-web 内容一致);消息中心属于 hiapi-cloud-public 服务,正确的装修组件家目录是 hiapi-cloud-public/hiapi-cloud-public-web/src/components/hiapi-public/(appId: 'A-10000',已有 swiper/notice/balance 等组件的项目)。

顺带发现并修复一个阻断性 bug:该项目下 src/pages/user-center/message.vue 早已存在一个相当完整的消息列表页(分类 Tab、未读红点、全部已读、详情弹层),但请求前缀写的是 /cloud-message/user/message/notification——/cloud-message 这个网关前缀根本不存在(见 CLAUDE.md 网关路由表,只有 /cloud-api 才路由到 hiapi-cloud-public),这个页面此前完全打不通。已改为 /cloud-api/user/message/notification

在此基础上完成:

  • message.vue 加「消息 / 公告」顶部大 Tab(公告是阶段2/4 新增的独立资源,不能塞进 notification 的 category 枚举里)。公告没有逐条已读接口,只有 read-all,页面据此不对公告条目发起单条已读请求。两个 Tab 的未读数复用同一次 /user/message/notification/unread-count 调用(接口本就同时返回 {notification, announcement} 两个真实值,不需要多打一次请求)。支持 onLoad query 参数 ?mode=announcement 直接定位到公告页。
  • 新增 hiapi-public-message-entry widget:图标+标题+未读红点(notification+announcement 求和),点击跳 /pages/user-center/message,可放入个人中心装修页。
  • 新增 hiapi-public-announcement-bar widget:滚动展示生效中公告(仿 VEM alarm-bar 的轮播实现),点击跳 /pages/user-center/message?mode=announcement
  • 两个 widget 都遵循已验证的 vem widget 模式:HiapiUtils.getEnv()==='DESIGN' 时走 mock 数据(供装修编辑器预览),onShow 拉取真实数据,HiapiUtils.buildStyle 处理外层样式。已在 src/components/index.ts 注册(appId: 'A-10000', project: 'hiapi-cloud-public')。

验证状态:hiapi-cloud-public 后端 mvn compile BUILD SUCCESS。hiapi-cloud-public-web 前端未能跑通 vue-tsc——这个子项目本身缺 @uni-helper/uni-types/@mini-types/alipay 等类型包(node_modules/@hiapi/hiapi-cloud-web-basic 目录是空的,pnpm install 后依然缺失),是环境既有问题、与本次改动无关(用 git status 核对过,这些包从未被本次改动触碰)。已用 HiapiUtils 真实类型签名(从 hiapi-cloud-web-basic/src/index.ts 源码核对 get/post/href/buildStyle 签名)人工核对新增代码,未跑运行时联调。

3. 阶段4未含(后续)

  1. channel→merchant 定向公告 见阶段5,已完成
  2. 平台公告未读数并入通用消息铃铛聚合接口
  3. 提现审核事件接入(见 hiapi-cloud-finance/docs/completion-plan.md §P3 第11项)
  4. hiapi-cloud-public-web 子项目本身的类型依赖缺失(与本功能无关,建议单独排查)

阶段5:channel→merchant 定向公告(2026-07-21)

补齐阶段4 留下的缺口:渠道只想给自己名下商户发公告,而不是全平台商户都收到。

  • 模型:MsgAnnouncement 新增 channelId 字段(long,0=平台全局广播,>0=该渠道专属,仅 receiverType=merchant 时有意义),复合索引 idx_msg_ann_channel(mid,receiverType,channelId)
  • 发布端:新增 ChannelAnnouncementController(/channel/message/announcement,@Secured(AccountType.Channel))。公告落库 mid 依然固定 0(和平台公告共享同一个"面向商户广播"的 mid 空间),真正归属靠 channelId 区分——channelId = TokenGet.getFid()(渠道账号登录后 Token.mid 恒为 0,身份信息在 fid 里,这是 ChannelPlatformAnnouncementController 阶段4 就踩过的坑,见下)。关键点:因为归属字段是 channelId 不是框架默认用来做隔离的 mid,BasicCurdControlleredit/delete 默认实现完全不做 mid 校验(只按主键过滤),必须手动覆写 buildWhere()(edit 场景)和 deleteBeforeIntercept()(delete 场景)补上 channelId 归属校验,否则渠道 A 能改/删渠道 B 发布的公告。
  • 接收端:MerchantPlatformAnnouncementController 原来只查 mid=0 + receiverType=merchant 拉全部平台公告,现在改成查 channelId in (0, 该商户所属渠道id)——商户所属渠道靠 ChannelMerchantService.getByMid(商户mid) 查(ChannelMerchant 表阶段4 调研时就已发现,一直没用上)。MsgAnnouncementUserQuery 新增 channelIds: List<Long> 字段配合 QueryWrapper.in()hiapi-system-basic-message-api 模块因此新增了对 hiapi-merchant-service 的编译期依赖(之前两个子系统只在运行时同容器,没有 Java 代码级依赖)。
  • 顺手修复一个阶段4 遗留 bug:ChannelPlatformAnnouncementController.receiverId() 一直用的是 TokenGet.getMid(),但渠道账号登录后 Token.mid 恒为 0——导致所有渠道账号的"平台公告已读游标"其实共用同一行(渠道 A 读了会让渠道 B 也变成已读)。改成 TokenGet.getFid()
  • 前端:新增 views/channel/announcement/index.vue(照抄 views/user/announcement/index.vue 的公告管理 Tab,去掉发送记录 Tab,因为渠道端没有"定向消息"概念;不需要 receiverType 选择器,固定发给 merchant),菜单 pages.ts 新增 300500000「公告管理」(渠道端)。商户端 views/message/index.vue 的"平台公告" Tab 加一列"来源"标签(平台/所属渠道),MsgAnnouncementVo 相应暴露 channelId 字段。
  • 验证状态:hiapi-cloud-public 后端 mvn compile BUILD SUCCESS,hiapi-cloud-admin-ts 前端 vue-tsc --noEmit 通过。菜单已直接执行 SQL 插入 hiapi_core_system_menu(menu_id=300500000),未走完整的应用重装/reloadMenus 流程。待部署验证:建表(channelId 新列走 ddl-auto: update 自动加)、渠道发公告→商户能收到且其他渠道商户收不到的端到端联调。

后续工作建议

  1. 立即: 部署验证阶段2~5(建表/队列/公告闭环、user 服务删除后正常启动、channel/platform/merchant 三端公告发布与接收、message.vue 联调)
  2. 下周: 平台公告未读数并入通用消息铃铛聚合接口、hiapi-cloud-public-web 类型依赖修复
  3. 三周后: 多渠道派发(短信/邮件复用 ISenderService)与社交渠道集成