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

29 KiB
Raw Blame History

msg 模块 Next Steps上下游依赖

维护者ai10msg 最后更新2026-07-14 关联:


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.sentARB-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 启动命令

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

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 测试步骤

# 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/updateNotificationPreferencesmsg 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. ChannelDispatcher5 通道 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 Schemamsg_announcements + msg_announcement_reads 表
  13. Dockerfileworkspace 模式多阶段构建
  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 中记录审计结果。