Files
Edu/services/msg/docs/nextstep.md

470 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 中记录审计结果。