docs(docs): coord 完成 15 模块 issue 仲裁与基础设施同步
coord.md 新增 ARB-019/020/021 三章仲裁章节,修正 ARB-001。 - coord.md: 新增 ARB-019/020/021(student/parent/admin-portal 24 项) - coord.md: 修正 ARB-001(admin P2 预留/schema 文件名/classes 数据源) - 004 §4: 依赖图加 PBFF→DataAna+Msg - 004 §7.2: push-gateway→Redis 软失败标注 - 004 §11.4: 错误码前缀矩阵(11 服务+i18n key) - 004 §11.5: ActionState 信封规范(降级模式方案 B) - matrix §1: 依赖矩阵加 PBFF 边 - matrix §2: 移除 api-gateway 为 iam gRPC 消费方 - matrix §4: admin-portal→teacher-bff - matrix §5: 移除 /sse+鉴权头统一 - matrix §6: 错误码表补 i18n key 列 - 15 个 issue.md: 仲裁结论回写 - push-gateway_contract: 移除 /sse+鉴权头改 X-Internal-Token - packages/contracts: 新建包 ADMIN_* 权限点常量 AI: coord
This commit is contained in:
@@ -17,29 +17,35 @@
|
||||
|
||||
### 1.2 HTTP 端点
|
||||
|
||||
| Method | Path | 用途 | 认证 | 请求体 | 响应 |
|
||||
| ------ | --------------------------- | -------------------------------------- | --------------------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- |
|
||||
| GET | /ws | WebSocket 升级端点(实时推送通知) | JWT RS256(query `?token=` 或 `Authorization`) | — | 升级为 WebSocket 长连接 |
|
||||
| POST | /internal/push | 内部单推接口(msg 服务触发) | `X-Internal-Token` 头 | `{user_id, event, data, ttl?}` | `{success, delivered, online}` |
|
||||
| POST | /internal/broadcast | 内部广播接口(msg 服务触发) | `X-Internal-Token` 头 | `{event, data, filter?}` | `{success, reached}` |
|
||||
| GET | /internal/online/\<userID\> | 查询用户在线状态 | `X-Internal-Token` 头 | — | `{online: bool, instances: []}` |
|
||||
| GET | /healthz | 健康检查(liveness) | 公开 | — | `{status:"ok", service, version, connections}` |
|
||||
| GET | /readyz | 就绪检查(readiness) | 公开 | — | `{status, degraded?}`(Redis/Kafka 软失败时 degraded:true) |
|
||||
| GET | /metrics | Prometheus 指标端点 | 公开(内网) | — | Prometheus 文本格式 |
|
||||
| Method | Path | 用途 | 认证 | 请求体 | 响应 |
|
||||
| ------ | --------------------------- | ---------------------------------- | ----------------------------------------------- | ------------------------------ | ----------------------------------------------------------- |
|
||||
| GET | /ws | WebSocket 升级端点(实时推送通知) | JWT RS256(query `?token=` 或 `Authorization`) | — | 升级为 WebSocket 长连接 |
|
||||
| POST | /internal/push | 内部单推接口(msg 服务触发) | `X-Internal-Token` 头 | `{user_id, event, data, ttl?}` | `{success, delivered, online}` |
|
||||
| POST | /internal/broadcast | 内部广播接口(msg 服务触发) | `X-Internal-Token` 头 | `{event, data, filter?}` | `{success, reached}` |
|
||||
| GET | /internal/online/\<userID\> | 查询用户在线状态 | `X-Internal-Token` 头 | — | `{online: bool, instances: []}` |
|
||||
| GET | /healthz | 健康检查(liveness) | 公开 | — | `{status:"ok", service, version, connections}` |
|
||||
| GET | /readyz | 就绪检查(readiness) | 公开 | — | `{status, degraded?}`(Redis/Kafka 软失败时 degraded:true) |
|
||||
| GET | /metrics | Prometheus 指标端点 | 公开(内网) | — | Prometheus 文本格式 |
|
||||
|
||||
**待 ISSUE-001 仲裁项**:
|
||||
- ~~`GET /sse`~~(02 文档 §10 建议不支持;contract v1 列出但与 02 冲突,待 coord 仲裁移除)
|
||||
**SSE 端点裁决**(ARB-015 §17.3,ISSUE-001 已裁决):
|
||||
|
||||
**待 ISSUE-002 仲裁项**:
|
||||
- 内部 API 鉴权头命名:本契约采用 `X-Internal-Token`(对齐总裁裁决 §7.2);现码为 `X-Internal-Key`,待仲裁确认后统一
|
||||
- ❌ 不支持 `GET /sse`(仅 WebSocket /ws;删除 contract v1 的 /sse 行,02 文档 §10 正确)
|
||||
- 理由:简化实现 + 统一推送通道 + WebSocket 双向通信能力更强
|
||||
|
||||
**鉴权头裁决**(ARB-015 §17.3,ISSUE-002 已裁决):
|
||||
|
||||
- 统一采用 `X-Internal-Token` 头(对齐总裁裁决 §7.2)
|
||||
- 现码 `X-Internal-Key` 需 ai02 统一为 `X-Internal-Token`
|
||||
|
||||
**`X-Internal-Token` 校验机制**:
|
||||
|
||||
- 启动时从 `INTERNAL_API_TOKEN` 环境变量加载
|
||||
- 与 msg 服务共享同一密钥(K8s Secret 注入)
|
||||
- DevMode(`DEV_MODE=true`)下跳过校验,便于本地联调
|
||||
- 缺失/不匹配 → 401 + `PUSH_UNAUTHORIZED`
|
||||
|
||||
**响应语义**(/internal/push):
|
||||
|
||||
- `delivered: true`:本实例或跨实例成功投递到至少一个连接
|
||||
- `online: false`:用户离线,msg 应走离线推送(SMS/邮件)
|
||||
- `delivered: false, online: true`:投递失败(连接满/异常),msg 应重试或落库
|
||||
@@ -81,10 +87,12 @@
|
||||
```
|
||||
|
||||
**客户端 → 服务端**:
|
||||
|
||||
- WebSocket Ping 控制帧(心跳,30s 间隔,非文本消息)
|
||||
- 重连时:`GET /ws?token=&session_id=<id>&last_seq=<n>`(P6 实现)
|
||||
|
||||
**心跳规则**(RFC 6455 控制帧):
|
||||
|
||||
- 客户端每 30s 发送 Ping
|
||||
- 服务端自动回 Pong(gorilla/websocket 默认)
|
||||
- 服务端 `SetReadDeadline(60s)`,60s 无消息则关闭连接
|
||||
@@ -101,43 +109,44 @@
|
||||
|
||||
### 2.2 Kafka 事件订阅(异步)
|
||||
|
||||
| Topic | Event | 发布方 | mock 策略 |
|
||||
| ------------------------------ | ------------------------ | ---------- | --------------------------------------------------------------------------- |
|
||||
| `edu.notification.requested` | NotificationRequested | msg (ai10) | msg 就绪前不订阅,使用本地定时器每 30 秒推送 1 条 mock 通知到所有在线客户端 |
|
||||
| Topic | Event | 发布方 | mock 策略 |
|
||||
| ---------------------------- | --------------------- | ---------- | --------------------------------------------------------------------------- |
|
||||
| `edu.notification.requested` | NotificationRequested | msg (ai10) | msg 就绪前不订阅,使用本地定时器每 30 秒推送 1 条 mock 通知到所有在线客户端 |
|
||||
|
||||
> **topic 命名对齐 ISSUE-053 裁决**([president-final-rulings.md](../president-final-rulings.md) §1.5):禁止抽象名 `edu.*.events`,统一 `edu.<domain>.<aggregate>.<action>` 格式。
|
||||
> 原 contract v1 写的 `edu.msg.notification.events` 已废弃。
|
||||
|
||||
**消费语义**:
|
||||
|
||||
- Consumer Group:`push-gateway`
|
||||
- 至少一次(at-least-once),消费失败重试 3 次后入死信队列
|
||||
- 幂等性:基于 `event_id` Redis SETNX 去重(TTL 24h)
|
||||
|
||||
### 2.3 HTTP 调用(如有)
|
||||
|
||||
| 调用方 | Method | Path | 用途 | 时机 |
|
||||
| -------------- | ------ | --------------------------------- | --------------------------------- | ---------- |
|
||||
| push-gateway | GET | `iam/.well-known/jwks.json` | 拉取 RS256 公钥校验 WebSocket JWT | iam 就绪后 |
|
||||
| 调用方 | Method | Path | 用途 | 时机 |
|
||||
| ------------ | ------ | --------------------------- | --------------------------------- | ---------- |
|
||||
| push-gateway | GET | `iam/.well-known/jwks.json` | 拉取 RS256 公钥校验 WebSocket JWT | iam 就绪后 |
|
||||
|
||||
### 2.4 Redis 协议
|
||||
|
||||
| 用途 | 数据结构 | Key 模式 | TTL |
|
||||
| -------------------- | --------------- | ------------------------------------ | --------------- |
|
||||
| 在线用户所在实例集合 | SET | `edu:push:online:<userID>` | 60s(心跳续期) |
|
||||
| 单连接元数据 | HASH | `edu:push:session:<userID>:<connID>` | 60s |
|
||||
| 用途 | 数据结构 | Key 模式 | TTL |
|
||||
| ---------------------- | --------------- | ------------------------------------ | --------------- |
|
||||
| 在线用户所在实例集合 | SET | `edu:push:online:<userID>` | 60s(心跳续期) |
|
||||
| 单连接元数据 | HASH | `edu:push:session:<userID>:<connID>` | 60s |
|
||||
| 跨实例定向推送 channel | Pub/Sub channel | `edu:push:channel:user:<userID>` | — |
|
||||
| 跨实例广播 channel | Pub/Sub channel | `edu:push:channel:broadcast` | — |
|
||||
| 幂等去重 | SETNX | `edu:push:idempotent:<event_id>` | 24h |
|
||||
| 跨实例广播 channel | Pub/Sub channel | `edu:push:channel:broadcast` | — |
|
||||
| 幂等去重 | SETNX | `edu:push:idempotent:<event_id>` | 24h |
|
||||
|
||||
> **Hub 启动重建机制**(对齐 ISSUE-058):实例启动时遍历内存连接 SADD + EXPIRE 60s;先清空 Redis 中本 instanceID 旧成员避免幽灵成员;实例崩溃 SET 自然过期(60s)。
|
||||
|
||||
### 2.5 内部接口(msg 调用 push-gateway)
|
||||
|
||||
| 被调用方 | Method.Path | 用途 | 说明 |
|
||||
| ------------ | ------------------------ | ------------------ | ---------------------------------------- |
|
||||
| push-gateway | POST /internal/push | msg 服务单用户推送 | msg 渲染模板后定向推送给目标用户 |
|
||||
| push-gateway | POST /internal/broadcast | msg 服务批量推送 | msg 收到业务事件后渲染模板,调此接口广播 |
|
||||
| push-gateway | GET /internal/online/<userID> | msg 查在线状态 | msg 决定走在线推送还是离线推送(SMS/邮件) |
|
||||
| 被调用方 | Method.Path | 用途 | 说明 |
|
||||
| ------------ | ----------------------------- | ------------------ | ------------------------------------------ |
|
||||
| push-gateway | POST /internal/push | msg 服务单用户推送 | msg 渲染模板后定向推送给目标用户 |
|
||||
| push-gateway | POST /internal/broadcast | msg 服务批量推送 | msg 收到业务事件后渲染模板,调此接口广播 |
|
||||
| push-gateway | GET /internal/online/<userID> | msg 查在线状态 | msg 决定走在线推送还是离线推送(SMS/邮件) |
|
||||
|
||||
---
|
||||
|
||||
@@ -187,25 +196,25 @@
|
||||
|
||||
## §5 与 02 文档、现码、总裁裁决的对齐说明
|
||||
|
||||
| 维度 | 现 code | 02 文档 | 总裁裁决 | 本 contract 采用 | 对齐任务 |
|
||||
| ---- | ------- | ------- | -------- | ---------------- | -------- |
|
||||
| 内部端点路径 | `/internal/push` | `/internal/push` | §4.2 as-is 采纳 02 §4.2 | `/internal/push` | ✅ 已对齐(v1 写 `/internal/send` 已修正) |
|
||||
| 鉴权头名 | `X-Internal-Key` | `X-Internal-Token` | §7.2 "X-Internal-Token 重命名" | `X-Internal-Token` | 待 ISSUE-002 仲裁后改代码 |
|
||||
| 鉴权环境变量 | `INTERNAL_API_KEY` | `INTERNAL_API_TOKEN` | §7.2 | `INTERNAL_API_TOKEN` | 待 ISSUE-002 仲裁后改代码 |
|
||||
| 鉴权机制 | 共享密钥 | 共享密钥 | — | 共享密钥(v1 写 mTLS 已废弃) | ✅ 已对齐 |
|
||||
| Kafka topic | —(未实现) | `edu.notification.events` | §1.5 ISSUE-053 → `edu.notification.requested` | `edu.notification.requested` | ✅ 已对齐 ISSUE-053 |
|
||||
| /readyz Redis 失败策略 | —(仅返连接数) | 返 503 硬失败 | §4.3 ISSUE-058 "仅告警不阻塞" | 软失败 200 + `degraded:true` | 待 ISSUE-006 仲裁最终策略 |
|
||||
| /readyz Kafka 失败策略 | — | 未描述 | §3.3 ISSUE-055 软失败 | 软失败 200 + `degraded:true` | ✅ 已对齐 ISSUE-055 |
|
||||
| /sse 端点 | 无 | §10 建议不支持 | — | 不提供(待 ISSUE-001 仲裁) | 待 ISSUE-001 仲裁 |
|
||||
| 容量目标 | — | 10w+ | — | 10w+ | 待 ISSUE-003 仲裁更新 modules/README |
|
||||
| 错误码前缀 | 无前缀 | `PUSH_*` | — | `PUSH_*` | 待 P5 实现时统一 |
|
||||
| 心跳协议 | 文本 ping/pong | RFC 6455 控制帧 | — | RFC 6455 控制帧 | 待 P5 任务 4.3 重构 |
|
||||
| 维度 | 现 code | 02 文档 | 总裁裁决 | 本 contract 采用 | 对齐任务 |
|
||||
| ---------------------- | ------------------ | ------------------------- | --------------------------------------------- | ----------------------------- | ------------------------------------------ |
|
||||
| 内部端点路径 | `/internal/push` | `/internal/push` | §4.2 as-is 采纳 02 §4.2 | `/internal/push` | ✅ 已对齐(v1 写 `/internal/send` 已修正) |
|
||||
| 鉴权头名 | `X-Internal-Key` | `X-Internal-Token` | §7.2 "X-Internal-Token 重命名" | `X-Internal-Token` | 待 ISSUE-002 仲裁后改代码 |
|
||||
| 鉴权环境变量 | `INTERNAL_API_KEY` | `INTERNAL_API_TOKEN` | §7.2 | `INTERNAL_API_TOKEN` | 待 ISSUE-002 仲裁后改代码 |
|
||||
| 鉴权机制 | 共享密钥 | 共享密钥 | — | 共享密钥(v1 写 mTLS 已废弃) | ✅ 已对齐 |
|
||||
| Kafka topic | —(未实现) | `edu.notification.events` | §1.5 ISSUE-053 → `edu.notification.requested` | `edu.notification.requested` | ✅ 已对齐 ISSUE-053 |
|
||||
| /readyz Redis 失败策略 | —(仅返连接数) | 返 503 硬失败 | §4.3 ISSUE-058 "仅告警不阻塞" | 软失败 200 + `degraded:true` | 待 ISSUE-006 仲裁最终策略 |
|
||||
| /readyz Kafka 失败策略 | — | 未描述 | §3.3 ISSUE-055 软失败 | 软失败 200 + `degraded:true` | ✅ 已对齐 ISSUE-055 |
|
||||
| /sse 端点 | 无 | §10 建议不支持 | — | 不提供(待 ISSUE-001 仲裁) | 待 ISSUE-001 仲裁 |
|
||||
| 容量目标 | — | 10w+ | — | 10w+ | 待 ISSUE-003 仲裁更新 modules/README |
|
||||
| 错误码前缀 | 无前缀 | `PUSH_*` | — | `PUSH_*` | 待 P5 实现时统一 |
|
||||
| 心跳协议 | 文本 ping/pong | RFC 6455 控制帧 | — | RFC 6455 控制帧 | 待 P5 任务 4.3 重构 |
|
||||
|
||||
---
|
||||
|
||||
## §6 变更历史
|
||||
|
||||
| 版本 | 日期 | 变更内容 | 变更依据 |
|
||||
| ---- | ---------- | ------------------------------------------------------------------------ | ------------------------------------- |
|
||||
| v1 | 2026-07-09 | 初始版本(coord 生成) | — |
|
||||
| 版本 | 日期 | 变更内容 | 变更依据 |
|
||||
| ---- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
||||
| v1 | 2026-07-09 | 初始版本(coord 生成) | — |
|
||||
| v2 | 2026-07-10 | topic 改 `edu.notification.requested`;/internal/send → /internal/push;鉴权 mTLS → X-Internal-Token;移除 /sse(待仲裁);新增 /internal/online;新增 §5 对齐表 | ISSUE-053/055/056/058 + 总裁 §4.2/§7.2 |
|
||||
|
||||
Reference in New Issue
Block a user