470 lines
29 KiB
Markdown
470 lines
29 KiB
Markdown
# msg 模块 Next Steps(上下游依赖)
|
||
|
||
> 维护者:ai10(msg)
|
||
> 最后更新: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 RPC(5 Notification + 2 Preference + 6 Template)+ REST 双协议 |
|
||
| ARB-008 裁决落地 | ✅ 完成 | RPC 17→13,4 RPC 降级 REST only,topic 改为 `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/wechat)Promise.allSettled |
|
||
| /readyz 6 依赖检查 | ✅ 完成 | DB/ES/Redis/KafkaProducer/KafkaConsumer/PushGateway |
|
||
| 单元测试 | ✅ 完成 | 101 tests / 5 spec files,覆盖率 ≥ 80% |
|
||
| Announcements 公告 | ✅ 完成 | 新增 REST API(schema+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-bff(ai03)
|
||
|
||
**依赖内容**: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-bff(ai04)
|
||
|
||
**依赖内容**: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-bff(ai05)
|
||
|
||
**依赖内容**: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-gateway(ai09)
|
||
|
||
**依赖内容**: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-gateway(ai01)
|
||
|
||
**依赖内容**: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 前端 portal(ai13/ai14/ai15/ai16)
|
||
|
||
**间接依赖**:前端通过各自 BFF 聚合访问 msg,无直接 gRPC/REST 调用。
|
||
|
||
- teacher-portal(ai13):通过 teacher-bff GraphQL → msg gRPC/REST
|
||
- student-portal(ai14):通过 student-bff GraphQL → msg gRPC/REST
|
||
- parent-portal(ai15):通过 parent-bff GraphQL → msg gRPC
|
||
- admin-portal(ai16):通过 teacher-bff admin GraphQL → msg REST(公告管理)
|
||
|
||
---
|
||
|
||
## 3. 上游依赖(msg ← 上游模块需要提供什么)
|
||
|
||
### 3.1 iam(ai06)— 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-edu(ai07)— 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-ana(ai08)— 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-gateway(ai09)— 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 schema(msg_announcements + msg_announcement_reads 表)
|
||
- `src/announcements/announcements.repository.ts` — 数据访问层
|
||
- `src/announcements/announcements.service.ts` — 业务逻辑
|
||
- `src/announcements/announcements.controller.ts` — REST API(8 端点)
|
||
- `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 条 items(1 已存在 + 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 OK(pushGateway 软失败,预期) |
|
||
| 公告创建 | POST /announcements | ✅ 返回 id,status=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 | ✅ 自动生成 groupId(cuid2) |
|
||
| 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 个公告 mutation(createAnnouncement/publishAnnouncement/archiveAnnouncement/toggleAnnouncementPin),需 teacher-bff 接入 msg REST |
|
||
|
||
---
|
||
|
||
## 7. msg 模块工作完成总结
|
||
|
||
### 7.1 已完成的全部工作
|
||
|
||
1. **P5 通知中台核心**:13 gRPC RPC + 10 REST endpoints(notifications)+ 2 REST endpoints(preferences)+ 6 REST endpoints(templates)
|
||
2. **ARB-008 裁决落地**:RPC 17→13,4 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 endpoints(schema+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(端口 50056,13 RPC)**:
|
||
|
||
- NotificationService: SendNotification, ListNotifications, MarkAsRead, SearchNotifications, RecallNotification
|
||
- NotificationPreferenceService: GetPreferences, UpdatePreferences
|
||
- NotificationTemplateService: CreateTemplate, GetTemplate, ListTemplates, UpdateTemplate, DeleteTemplate, RenderTemplate
|
||
|
||
**REST(端口 3007)**:
|
||
|
||
- /notifications/*(10 endpoints)
|
||
- /preferences/user/:userId(2 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 中记录审计结果。
|