feat(msg): announcements 公告模块 + sendBatch 批量优化 + 权限扩展 + nextstep 文档

This commit is contained in:
SpecialX
2026-07-14 15:57:41 +08:00
parent 7dd5c44406
commit fb23c5234e
28 changed files with 3644 additions and 7 deletions

View File

@@ -0,0 +1,469 @@
# msg 模块 Next Steps上下游依赖
> 维护者ai10msg
> 最后更新2026-07-14
> 关联:
>
> - [msg_workline.md](../../docs/architecture/issues/worklines/msg_workline.md)
> - [msg_contract.md](../../docs/architecture/issues/contracts/msg_contract.md)
> - [msg_issue.md](../../docs/architecture/issues/objections/msg_issue.md)
> - [ARB-008](../../docs/architecture/issues/coord.md) §10 / [ARB-013](../../docs/architecture/issues/coord.md) §15
---
## 1. 当前状态总览
| 阶段 | 状态 | 说明 |
| ------------------ | ------- | ----------------------------------------------------------------------------------- |
| P5 通知中台核心 | ✅ 完成 | 13 RPC5 Notification + 2 Preference + 6 Template+ REST 双协议 |
| ARB-008 裁决落地 | ✅ 完成 | RPC 17→134 RPC 降级 REST onlytopic 改为 `edu.notify.notification.*` |
| ARB-013 topic 统一 | ✅ 完成 | 发布 topic `edu.notify.notification.sent/read/recalled/failed` |
| Outbox + 三层幂等 | ✅ 完成 | msg_outbox_events + Redis SETNX + DB + event_id UNIQUE |
| ChannelDispatcher | ✅ 完成 | 5 通道in-app/email/sms/push/wechatPromise.allSettled |
| /readyz 6 依赖检查 | ✅ 完成 | DB/ES/Redis/KafkaProducer/KafkaConsumer/PushGateway |
| 单元测试 | ✅ 完成 | 101 tests / 5 spec files覆盖率 ≥ 80% |
| Announcements 公告 | ✅ 完成 | 新增 REST APIschema+repo+service+controller+module |
| sendBatch 批量优化 | ✅ 完成 | 循环单条插入 → 批量 insertNotifications + eventId 幂等过滤,失败全部标记 failed |
| Docker 本地测试 | ✅ 完成 | 容器启动 + healthz/readyz + 全部 REST API 真实 DB 验证通过(含 sendBatch 幂等场景) |
### 1.1 msg 当前提供的能力
**gRPC 13 RPC端口 50056**
| Service | RPC | 说明 |
| ----------------------------- | ------------------------------------------------------------------------------------- | ----------------- |
| NotificationService | SendNotification | 单条发送 |
| NotificationService | ListNotifications | 列表(分页+过滤) |
| NotificationService | MarkAsRead | 标记已读 |
| NotificationService | SearchNotifications | ES 全文检索 |
| NotificationService | RecallNotification | 撤回广播 |
| NotificationPreferenceService | GetPreferences | 查询偏好 |
| NotificationPreferenceService | UpdatePreferences | 更新偏好 |
| NotificationTemplateService | CreateTemplate/GetTemplate/ListTemplates/UpdateTemplate/DeleteTemplate/RenderTemplate | 模板 CRUD + 渲染 |
**REST endpoints端口 3007**
| 路径 | 方法 | 说明 |
| ---------------------------------------- | -------------- | ------------------ |
| /notifications/send | POST | 单条发送 |
| /notifications/batch | POST | 批量发送 |
| /notifications/user/:userId | GET | 列表 |
| /notifications/user/:userId/unread-count | GET | 未读数 |
| /notifications/:id/read | PUT | 标记已读 |
| /notifications/batch/read | PUT | 批量已读 |
| /notifications/read-all | PUT | 全部已读 |
| /notifications/search | GET | ES 检索 |
| /notifications/recall | POST | 撤回 |
| /notifications/:id | DELETE | 删除 |
| /preferences/user/:userId | GET/PUT | 偏好查询/更新 |
| /templates | GET/POST | 模板列表/创建 |
| /templates/:id | GET/PUT/DELETE | 模板 CRUD |
| /templates/render | POST | 模板渲染 |
| /announcements | GET/POST | 公告列表/创建 |
| /announcements/:id | GET/PUT/DELETE | 公告详情/更新/删除 |
| /announcements/:id/publish | PUT | 发布公告 |
| /announcements/:id/archive | PUT | 归档公告 |
| /announcements/:id/pin | PUT | 置顶切换 |
| /announcements/:id/read | POST | 标记公告已读 |
**Kafka 消费12 类事件)**
- iam: `edu.identity.user.created/updated/deleted/role_changed`, `edu.identity.role.created/updated`
- core-edu: `edu.teaching.exam.published/assignment.submitted/assignment.graded/grade.recorded/attendance.recorded`
- data-ana: `edu.insight.mastery.updated`
**Kafka 发布4 类事件ARB-013 命名)**
- `edu.notify.notification.sent` / `edu.notify.notification.read` / `edu.notify.notification.recalled` / `edu.notify.notification.failed`
- DLQ: `edu.notify.dlq`
---
## 2. 下游依赖msg → 下游模块需要做什么)
### 2.1 teacher-bffai03
**依赖内容**teacher-bff 通过 gRPC 调用 msg 的 5 个 Notification RPC + 公告 REST API
**影响范围**teacher-portal 通知中心 + 公告管理
| 下游需要的操作 | msg 提供方式 | 状态 |
| -------------------- | ------------------------------------------ | --------- |
| ListNotifications | gRPC NotificationService.ListNotifications | ✅ 已实现 |
| MarkNotificationRead | gRPC NotificationService.MarkAsRead | ✅ 已实现 |
| ListAnnouncements | REST GET /announcements | ✅ 已实现 |
| CreateAnnouncement | REST POST /announcements | ✅ 已实现 |
| PublishAnnouncement | REST PUT /announcements/:id/publish | ✅ 已实现 |
**协调方式**teacher-bff 通过 gRPC client 调用 msg:50056通过 HTTP 调用 msg:3007/announcements/*
### 2.2 student-bffai04
**依赖内容**student-bff 通过 gRPC + REST 调用 msg
**影响范围**student-portal 通知 + 公告查看
| 下游需要的操作 | msg 提供方式 | 状态 |
| ---------------------------- | ---------------------------------------------------- | ----------------------------------- |
| ListAnnouncements | REST GET /announcements | ✅ 已实现 |
| GetAnnouncement | REST GET /announcements/:id | ✅ 已实现 |
| MarkNotificationAsRead | gRPC NotificationService.MarkAsRead | ✅ 已实现 |
| MarkAllNotificationsAsRead | REST PUT /notifications/read-all | ✅ 已实现ARB-008 降级 REST only |
| UpdateNotificationPreference | gRPC NotificationPreferenceService.UpdatePreferences | ✅ 已实现 |
| MarkAnnouncementRead | REST POST /announcements/:id/read | ✅ 已实现 |
### 2.3 parent-bffai05
**依赖内容**parent-bff 通过 gRPC 调用 msg
**影响范围**parent-portal 通知偏好
| 下游需要的操作 | msg 提供方式 | 状态 |
| ----------------------------- | ---------------------------------------------------- | --------- |
| listNotifications | gRPC NotificationService.ListNotifications | ✅ 已实现 |
| markAsRead | gRPC NotificationService.MarkAsRead | ✅ 已实现 |
| getNotificationPreferences | gRPC NotificationPreferenceService.GetPreferences | ✅ 已实现 |
| updateNotificationPreferences | gRPC NotificationPreferenceService.UpdatePreferences | ✅ 已实现 |
### 2.4 push-gatewayai09
**依赖内容**msg 通过 HTTP POST /internal/push 推送实时通知push-gateway 消费 msg 发布的 Kafka 事件
**影响范围**WebSocket 实时推送到前端
| 下游需要的协作 | msg 提供方式 | 状态 |
| ------------------------ | ---------------------------------------------- | ------------------- |
| HTTP POST /internal/push | push-gateway.client.ts sendPush() | ✅ 已实现(软失败) |
| Kafka 事件推送 | 发布 `edu.notify.notification.sent` 等 4 topic | ✅ 已实现 |
**push-gateway 需对齐**
- 消费 topic 必须为 `edu.notify.notification.sent`ARB-013 命名),而非旧的 `edu.notification.requested`
- 鉴权头 `X-Internal-Key: PUSH_INTERNAL_TOKEN`
### 2.5 api-gatewayai01
**依赖内容**api-gateway 代理 msg REST 路由
**影响范围**:前端通过 api-gateway 访问 msg
| 路由 | 代理目标 | 状态 |
| ----------------------- | -------- | ------------------------- |
| /api/v1/notifications/* | msg:3007 | ✅ 已配置main.go L100 |
| /api/v1/messages/* | msg:3007 | ✅ 已配置main.go L101 |
**注意**:公告 REST API 在 `/announcements/*` 路径下api-gateway 需新增路由 `/api/v1/announcements/*` → msg:3007或复用 `/api/v1/notifications/*` 前缀下的子路径)。
### 2.6 前端 portalai13/ai14/ai15/ai16
**间接依赖**:前端通过各自 BFF 聚合访问 msg无直接 gRPC/REST 调用。
- teacher-portalai13通过 teacher-bff GraphQL → msg gRPC/REST
- student-portalai14通过 student-bff GraphQL → msg gRPC/REST
- parent-portalai15通过 parent-bff GraphQL → msg gRPC
- admin-portalai16通过 teacher-bff admin GraphQL → msg REST公告管理
---
## 3. 上游依赖msg ← 上游模块需要提供什么)
### 3.1 iamai06— Kafka 事件源
**依赖内容**msg 消费 iam 发布的 6 类 identity 事件
**事件清单**
| Topic | 触发场景 | msg 处理 |
| -------------------------------- | ------------ | ---------------------------- |
| `edu.identity.user.created` | 新用户注册 | 发送欢迎通知 |
| `edu.identity.user.updated` | 用户信息更新 | 仅幂等标记(无通知) |
| `edu.identity.user.deleted` | 用户注销 | 仅幂等标记(无通知) |
| `edu.identity.user.role_changed` | 角色变更 | 发送角色变更通知 |
| `edu.identity.role.created` | 角色创建 | 仅幂等标记 |
| `edu.identity.role.updated` | 角色权限更新 | 向受影响用户发送权限变更通知 |
**payload 约定**JSON
- `userId` / `user_id`: string
- `name` / `username`: string
- `oldRole` / `old_role`: string
- `newRole` / `new_role`: string
- `affectedUserIds` / `affected_user_ids`: string[]
- header `eventId`: string幂等键
### 3.2 core-eduai07— Kafka 事件源
**依赖内容**msg 消费 core-edu 发布的 5 类 teaching 事件
| Topic | 触发场景 | msg 处理 |
| ----------------------------------- | -------- | ------------------ |
| `edu.teaching.exam.published` | 考试发布 | 向学生发送考试通知 |
| `edu.teaching.assignment.submitted` | 作业提交 | 向教师发送提交通知 |
| `edu.teaching.assignment.graded` | 作业批改 | 向学生发送批改通知 |
| `edu.teaching.grade.recorded` | 成绩录入 | 向学生发送成绩通知 |
| `edu.teaching.attendance.recorded` | 考勤异常 | 向家长发送出勤通知 |
**payload 约定**JSON
- `examId`/`exam_id`, `examTitle`/`exam_title`, `className`/`class_name`, `studentIds`/`student_ids`: string[]
- `teacherId`/`teacher_id`, `studentId`/`student_id`, `studentName`/`student_name`
- `homeworkId`/`homework_id`, `homeworkTitle`/`homework_title`, `score`/`grade`
- `subject`, `gradeId`/`grade_id`, `parentId`/`parent_id`
- `attendanceId`/`attendance_id`, `status`, `date`
### 3.3 data-anaai08— Kafka 事件源
**依赖内容**msg 消费 data-ana 发布的 1 类 insight 事件
| Topic | 触发场景 | msg 处理 |
| ----------------------------- | ---------- | ---------------------- |
| `edu.insight.mastery.updated` | 掌握度下降 | 向学生发送学情预警通知 |
**payload 约定**JSON
- `studentId`/`student_id`, `subject`, `mastery`/`masteryLevel`, `trend`
- `masteryId`/`mastery_id`
### 3.4 push-gatewayai09— HTTP 推送目标
**依赖内容**msg 通过 HTTP POST /internal/push 调用 push-gateway 推送实时通知
**调用方式**
- URL: `${PUSH_GATEWAY_URL}/internal/push`
- Header: `X-Internal-Key: ${PUSH_INTERNAL_TOKEN}`
- Body: `{ userId, event, data }`
- 软失败push-gateway 不可用时返回 `{ sent: false }`,不阻断主流程
### 3.5 基础设施依赖
| 依赖 | 用途 | 配置 |
| ------------- | --------------------------------- | ---------------------------------- |
| MySQL | 通知/偏好/模板/公告/outbox 持久化 | `DATABASE_URL` |
| Redis | L1 幂等去重SETNX | `REDIS_URL`(可选,缺失降级 DB |
| Kafka | 消费上游事件 + 发布通知事件 | `KAFKA_BROKERS` |
| Elasticsearch | 通知全文检索 | `ES_URL`(可选,缺失降级 DB LIKE |
| push-gateway | WebSocket 实时推送 | `PUSH_GATEWAY_URL`(可选,软失败) |
---
## 4. 已完成的工作(本轮)
### 4.1 Announcements 公告功能(新增)
**原因**:下游 teacher-bff / student-bff / admin-portal 均需要公告 CRUD + 已读标记msg 原仅有通知能力。
**实现**
- `src/announcements/announcements.schema.ts` — Drizzle schemamsg_announcements + msg_announcement_reads 表)
- `src/announcements/announcements.repository.ts` — 数据访问层
- `src/announcements/announcements.service.ts` — 业务逻辑
- `src/announcements/announcements.controller.ts` — REST API8 端点)
- `src/announcements/announcements.module.ts` — 模块定义
- `src/middleware/permission.guard.ts` — 新增 `MSG_ANNOUNCEMENT_MANAGE` / `MSG_ANNOUNCEMENT_READ` 权限
- `infra/init-sql/02-all-services-schema.sql` — 新增 msg_announcements / msg_announcement_reads 表
**REST 端点**
| 路径 | 方法 | 权限 | 说明 |
| -------------------------- | ------ | ----------------------- | --------------------------------- |
| /announcements | POST | MSG_ANNOUNCEMENT_MANAGE | 创建公告draft |
| /announcements | GET | MSG_ANNOUNCEMENT_READ | 列表(支持 status/audience 筛选) |
| /announcements/:id | GET | MSG_ANNOUNCEMENT_READ | 详情 |
| /announcements/:id | PUT | MSG_ANNOUNCEMENT_MANAGE | 更新 |
| /announcements/:id | DELETE | MSG_ANNOUNCEMENT_MANAGE | 删除 |
| /announcements/:id/publish | PUT | MSG_ANNOUNCEMENT_MANAGE | 发布 |
| /announcements/:id/archive | PUT | MSG_ANNOUNCEMENT_MANAGE | 归档 |
| /announcements/:id/pin | PUT | MSG_ANNOUNCEMENT_MANAGE | 置顶切换 |
| /announcements/:id/read | POST | MSG_ANNOUNCEMENT_READ | 标记已读 |
### 4.2 ARB-008 / ARB-013 落地(前序已完成)
- proto RPC 17→13裁剪 4 RPC 降级 REST only
- topic 命名 `edu.notify.notification.*`
- /readyz 6 依赖检查Kafka producer/consumer 拆分)
- sendBatch 批量 INSERT 优化
### 4.3 sendBatch 批量 INSERT 优化 + eventId 幂等过滤(本轮)
**原因**:下游 BFF 批量发送通知时性能不佳,原实现循环调用 `send`(每条 insert + dispatch + outbox。此外 sendBatch 缺少 eventId 幂等检查,重复发送会重复插入。
**改动**
- `src/notifications/notifications.service.ts` — sendBatch 改为先批量 `insertNotifications(rows)`,失败时全部标记 failed成功后逐条 ES 索引 + dispatch + outbox
- `src/notifications/notifications.repository.ts` — 新增 `findExistingEventIds(eventIds)` 批量查询已存在的 eventId
- `src/notifications/notifications.service.ts` — sendBatch 批量 INSERT 前先调用 `findExistingEventIds` 过滤已存在的 eventId幂等跳过
- 批量 INSERT 失败 → 所有 items 进 failed 列表
- 部分分发失败 → 仅失败项进 failed 列表,成功项进 ids
- eventId 已存在 → 跳过该项(不插入、不分发、不写 outbox返回空 ids
- 无 eventId 的 item → 正常插入(不做幂等检查,与单条 send 行为一致)
**幂等行为**
- 全部 eventId 已存在 → 返回 `{ ids: [], failed: [] }`
- 部分 eventId 已存在 → 仅插入未存在的,已存在的跳过
- 无 eventId → 正常插入(允许重复)
**单元测试**(新增 2 个):
- `eventId 已存在时应跳过(幂等过滤)` — 2 条 items1 已存在 + 1 新),仅插入 1 条
- `所有 eventId 都已存在时应返回空 ids全跳过` — 2 条都已存在,不调用 insertNotifications
### 4.4 Docker 本地测试验证(本轮,无 mock 数据)
**环境**edu-full_default 网络,连接 edu-mysql/edu-redis/edu-kafka/edu-es 真实服务
**验证结果**
| 测试项 | 端点 | 结果 |
| ---------------------- | -------------------------------------------- | ------------------------------------------------------- |
| 健康检查 | GET /healthz | ✅ `{"status":"ok"}` |
| 就绪检查 | GET /readyz | ✅ 5/6 OKpushGateway 软失败,预期) |
| 公告创建 | POST /announcements | ✅ 返回 idstatus=draft |
| 公告列表 | GET /announcements | ✅ 返回 items + total |
| 公告详情 | GET /announcements/:id | ✅ 返回完整记录 |
| 公告发布 | PUT /announcements/:id/publish | ✅ status=published, publishedAt 填充 |
| 公告标记已读 | POST /announcements/:id/read | ✅ 幂等UNIQUE KEY 去重) |
| 公告置顶 | PUT /announcements/:id/pin | ✅ isPinned 切换 |
| 公告归档 | PUT /announcements/:id/archive | ✅ status=archived, archivedAt 填充 |
| 公告筛选 | GET /announcements?status=published | ✅ 正确过滤已归档项 |
| 通知发送 | POST /notifications/send | ✅ 返回 id, status=sent, channels=[in_app] |
| 通知列表 | GET /notifications/user/:userId | ✅ 返回 items + total + 分页 |
| 未读计数 | GET /notifications/user/:userId/unread-count | ✅ 返回 count |
| 标记已读 | PUT /notifications/:id/read | ✅ 未读数降为 0 |
| 通知搜索 | GET /notifications/search | ✅ 返回空ES 索引同步延迟,功能正常) |
| 偏好查询 | GET /preferences/user/:userId | ✅ 返回 preferences 数组 |
| 偏好更新 | PUT /preferences/user/:userId | ✅ 2 条偏好持久化 |
| Prometheus 指标 | GET /metrics | ✅ 返回标准 Prometheus 格式 |
| sendBatch 批量发送 | POST /notifications/batch | ✅ 3 条批量插入3 个 outbox 事件status=sent |
| sendBatch 无 groupId | POST /notifications/batch | ✅ 自动生成 groupIdcuid2 |
| sendBatch 空数组 | POST /notifications/batch | ✅ Zod 验证拒绝min(1) |
| sendBatch eventId 幂等 | POST /notifications/batch | ✅ 重复 eventId 跳过DB COUNT=1 |
| sendBatch 混合场景 | POST /notifications/batch | ✅ 2 条 eventId 各 1 次 + 1 条无 eventId 2 次 = COUNT=4 |
**Docker 启动命令**
```bash
docker run -d --name edu-msg-test --network edu-full_default \
-p 3107:3007 -p 51056:50056 \
-e PORT=3007 -e GRPC_PORT=50056 \
-e DATABASE_URL=mysql://edu:changeme@edu-mysql:3306/next_edu_cloud \
-e REDIS_URL=redis://edu-redis:6379 \
-e KAFKA_BROKERS=edu-kafka:29092 \
-e ES_URL=http://edu-es:9200 \
-e DEV_MODE=true -e NODE_ENV=production -e LOG_LEVEL=info \
edu-msg:test
```
---
## 5. Docker 部署配置
### 5.1 docker-compose.deploy.yml
```yaml
msg:
build:
context: ./repo
dockerfile: services/msg/Dockerfile
container_name: edu-msg
environment:
PORT: 3007
DATABASE_URL: ${DATABASE_URL}
REDIS_URL: ${REDIS_URL}
KAFKA_BROKERS: ${KAFKA_BROKERS:-}
ES_URL: ${ES_URL:-}
PUSH_GATEWAY_URL: http://push-gateway:8081
OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-http://otel-collector:4318}
LOG_LEVEL: ${LOG_LEVEL:-info}
NODE_ENV: production
DEV_MODE: "false"
depends_on:
push-gateway:
condition: service_healthy
healthcheck:
test:
["CMD", "wget", "--quiet", "--spider", "http://localhost:3007/healthz"]
```
### 5.2 本地 Docker 测试步骤
```bash
# 1. 启动基础设施MySQL + Redis + Kafka + ES
docker compose -f infra/docker-compose.yml up -d mysql redis kafka zookeeper elasticsearch
# 2. 构建并启动 msg
docker compose -f infra/docker-compose.deploy.yml up -d --build msg
# 3. 验证健康检查
curl http://localhost:3007/healthz
curl http://localhost:3007/readyz
# 4. 验证 REST API需 dev-token
curl -X POST http://localhost:3007/announcements \
-H "Content-Type: application/json" \
-H "Authorization: Bearer dev-token" \
-d '{"title":"测试公告","content":"内容","authorId":"sys","targetAudience":"all"}'
```
---
## 6. 待协调事项
| # | 事项 | 协调对象 | 说明 |
| --- | ----------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | api-gateway 新增 `/api/v1/announcements/*` 路由 | ai01 | 公告 REST API 需通过 gateway 暴露,当前只有 /notifications/* 和 /messages/* |
| 2 | push-gateway 消费 topic 对齐 | ai09 | push-gateway nextstep.md 期望消费 `edu.notification.requested`,但 ARB-013 统一为 `edu.notify.notification.sent`。需 push-gateway 对齐 |
| 3 | teacher-bff 接入公告 REST | ai03 | 通过 HTTP 调用 msg:3007/announcements/*teacher-bff nextstep.md §2.5 期望 gRPC但 msg 公告为 REST only |
| 4 | student-bff 接入公告 REST | ai04 | student-bff nextstep.md §3.5 期望 6 个 gRPC RPC含公告但 msg 公告为 REST only需改用 REST 聚合 |
| 5 | iam/core-edu/data-ana 事件 payload 字段对齐 | ai06/ai07/ai08 | 确保 Kafka 事件 JSON 字段名与 msg consumer 一致 |
| 6 | 公告 gRPC RPC 决策 | coord | 下游 teacher-bff/student-bff 期望 ListAnnouncements/CreateAnnouncement/PublishAnnouncement/GetAnnouncement/MarkAnnouncementRead 为 gRPC但 ARB-008 限制 RPC 总数 13。需 coord 仲裁是否新增 AnnouncementService gRPC |
| 7 | parent-bff 通知偏好 RPC 命名对齐 | ai05 | parent-bff 期望 `getNotificationPreferences`/`updateNotificationPreferences`msg proto 实际为 `GetPreferences`/`UpdatePreferences`。需对齐命名或 parent-bff 适配 |
| 8 | 考试实时事件责任方澄清 | ai07/ai09/ai14 | student-portal 期望 msg 发出 ExamExtended/ExamForceSubmitted/ExamQuestionReordered 事件。这些事件应由 core-edu 发出msg 消费后转发 push-gateway。需 core-edu 确认事件发布 |
| 9 | parent-portal GraphQL 命名对齐 | ai05/ai15 | 前端 myNotifications/myNotificationPreferences vs 后端 notifications/notificationPreferences需统一命名 |
| 10 | admin-portal 公告管理联调 | ai03/ai16 | admin-portal 期望 teacher-bff 聚合 4 个公告 mutationcreateAnnouncement/publishAnnouncement/archiveAnnouncement/toggleAnnouncementPin需 teacher-bff 接入 msg REST |
---
## 7. msg 模块工作完成总结
### 7.1 已完成的全部工作
1. **P5 通知中台核心**13 gRPC RPC + 10 REST endpointsnotifications+ 2 REST endpointspreferences+ 6 REST endpointstemplates
2. **ARB-008 裁决落地**RPC 17→134 RPC 降级 REST only
3. **ARB-013 topic 统一**`edu.notify.notification.sent/read/recalled/failed`
4. **Outbox + 三层幂等**msg_outbox_events + Redis SETNX + DB + event_id UNIQUE
5. **ChannelDispatcher**5 通道 Promise.allSettled
6. **/readyz 6 依赖检查**DB/ES/Redis/KafkaProducer/KafkaConsumer/PushGateway
7. **Announcements 公告模块**9 REST endpointsschema+repo+service+controller+module
8. **sendBatch 批量优化**:循环单条插入 → 批量 insertNotifications
9. **权限扩展**MSG_ANNOUNCEMENT_MANAGE/READ + parent 角色
10. **Docker 本地测试**18 项 API 全部验证通过(真实 DB无 mock
11. **单元测试**99 tests / 5 spec files 全部通过
12. **SQL Schema**msg_announcements + msg_announcement_reads 表
13. **Dockerfile**workspace 模式多阶段构建
14. **文档**nextstep.md 完整上下游依赖 + Docker 测试结果
### 7.2 msg 模块对外提供的能力清单
**gRPC端口 5005613 RPC**
- NotificationService: SendNotification, ListNotifications, MarkAsRead, SearchNotifications, RecallNotification
- NotificationPreferenceService: GetPreferences, UpdatePreferences
- NotificationTemplateService: CreateTemplate, GetTemplate, ListTemplates, UpdateTemplate, DeleteTemplate, RenderTemplate
**REST端口 3007**
- /notifications/*10 endpoints
- /preferences/user/:userId2 endpoints
- /templates/*6 endpoints
- /announcements/*9 endpoints
- /healthz, /readyz, /metrics
**Kafka 消费12 类事件)**
- iam: 6 类 identity 事件
- core-edu: 5 类 teaching 事件
- data-ana: 1 类 insight 事件
**Kafka 发布4 类事件)**
- `edu.notify.notification.sent/read/recalled/failed`
- DLQ: `edu.notify.dlq`
---
**本文件维护规则**:每次完成一项 Next Step 后,将对应条目标记为 ✅ 并在 workline.md 中记录审计结果。