refactor(docs): 移除 ai07 classes 合并到 core-edu,16 AI 降为 15 AI

This commit is contained in:
SpecialX
2026-07-10 14:04:49 +08:00
parent 6051f84a65
commit 9ba368477d
9 changed files with 457 additions and 2289 deletions

View File

@@ -164,6 +164,31 @@
| 全量测试 | `pnpm test` |
| 启动最小基础设施 | `docker compose -f infra/docker-compose.minimal.yml up -d` |
### 1.10 coord 交叉审查仲裁审查(跨模块)
> AI 在执行 coord 仲裁整改清单前,必须以 ai-allocation.md §3.2 为唯一源交叉验证责任方,并检查裁决内部自洽性。
| 场景 | 技术/规则 |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 仲裁责任方错配content→ai05 | content 模块由 ai09 负责ai-allocation §3.2 L80coord 整改清单若标 ai05 则为错配AI 必须以 ai-allocation §3.2 为准,提请 coord 修正而非自行执行 |
| 仲裁责任方错配msg→ai05 | msg 模块由 ai10 负责ai-allocation §3.2 L81coord 整改清单若标 ai05 则为错配;同上 |
| 仲裁责任方错配iam→ai02 | iam 模块由 ai06 负责ai-allocation §3.2 L77ai02=push-gatewaycoord 整改清单若标 ai02 则为错配;同上 |
| 仲裁责任方错配push-gateway→ai01 | push-gateway 由 ai02 负责ai-allocation §3.2 L73ai01=api-gatewaycoord §4.2/§5.6/整改第 21 项若标 ai01 则为错配;同上 |
| 仲裁责任方错配teacher-portal→ai07 | teacher-portal 由 ai13 负责ai-allocation §3.2 L84ai07=classes黄金模板coord §2.4/§4.1/§5.5/整改第 2 项若标 ai07 修正 teacher-portal 则为错配;同上 |
| 仲裁责任方错配parent-portal→ai07 | parent-portal 由 ai15 负责ai-allocation §3.2 L86ai07=classescoord §2.4 第 4 项若标 ai07 修正 parent-portal 端点则为错配;同上 |
| 仲裁责任方错配core-edu→ai03 | core-edu 由 ai08 负责ai-allocation §3.2 L79ai03=teacher-bffcoord §4.2/整改第 14 项/整改第 23 项若标 ai03 则为错配;同上 |
| 仲裁责任方错配parent-bff→ai07 | parent-bff 由 ai05 负责ai-allocation §3.2 L76ai07=classescoord §5.2 "ai03/ai04/ai07" 若指 BFF 前缀迁移则 ai07 应为 ai05同上 |
| 仲裁责任方错配系统性5 组认知错位) | coord 存在 5 组系统性 AI 编号映射错位ai07→teacher-portal/ai05→content-msg/ai02→iam/ai01→push-gateway/ai03→core-edu疑似沿用之前 session 错误认知;整改前必须以 ai-allocation §3.2 全量交叉验证 |
| 仲裁裁决内部冲突topic 保留 vs 废弃) | coord-cross-review.md §3.5 保留 `edu.notification.events`,但 parent-bff 02 §0.1 C5 废弃改为 `edu.notification.sent/read/recalled/failed`coord 自身前后不一致时以正式审查报告coord-cross-review.md为准或提请 coord 统一 |
| 仲裁裁决内部冲突GraphQL 引入时机) | coord-cross-review.md §2.5 裁决"P2-P3 用 REST",但 parent-bff 02 §0.1 U3 用户仲裁"GraphQL P2 立即引入"coord §2.5 与用户 U3 仲裁直接冲突,用户仲裁优先级高,应以 U3 为准,提请 coord 修正 §2.5 |
| 仲裁现状误判project_rules §5 | coord §2.1 裁决"修订 project_rules §5 包名规范为 next_edu_cloud.*",但 project_rules §5 L214 已是目标态;整改前应先验证现状,避免多余整改 |
| 仲裁审查三维度 | ① 责任方是否匹配 ai-allocation §3.2 ② 裁决是否自洽(无内部冲突)③ 现状是否误判;三维度全过方可执行 |
| 仲裁问题处理流程 | 发现问题记录到 known-issues 工作日志 + 提请 coord 修正,禁止自行修改仲裁文档或按错误责任分配执行 |
| coord 代笔文档仲裁 vs 正式审查报告 | coord 代笔文档(如 parent-bff 02中的仲裁决策如 C5可能与 coord 正式审查报告coord-cross-review.md不一致以正式审查报告为准代笔文档中的仲裁需 coord 在正式报告中确认 |
| 仲裁审查覆盖面要求 | 首次审查覆盖面不足(仅查 3 组错配遗漏 4 组),二次审查应严格逐条核对全部 24 项整改项,禁止抽样核对;系统性错配需按 ai-allocation §3.2 全量交叉验证 |
| 用户仲裁 vs coord 仲裁优先级 | 用户仲裁U1-U4优先级高于 coord 仲裁coord 裁决与用户仲裁冲突时(如 §2.5 GraphQL vs U3以用户仲裁为准提请 coord 修正其裁决以保持一致 |
| 整改清单整体可执行性判断 | 若整改清单责任方错配占比超阈值(本次 71%17/24 项),则整改清单整体不可直接执行,必须等 coord 重新发布修正版整改清单后方可推进 |
---
## 二、模块经验
@@ -264,31 +289,78 @@
### 2.6 data-anaPython/FastAPIP4
| 场景 | 技术/规则 |
| -------------------- | ------------------------------------------------------------------------------------------------------------------- |
| 学情诊断 | ClickHouse 宽表查询5s 内返回 |
| CDC 链路 | Debezium 监听 MySQL binlog → Kafka`edu-cdc.next_edu_cloud.<table>`)→ DataAna 消费写 ClickHouse |
| CDC 延迟监控 | Debezium 暴露 lag metrics超阈值告警 |
| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) |
| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) |
| CDC 消费者实现 | `cdc_consumer.py` 用 aiokafka AIOKafkaConsumerlifespan 启动 asyncio.create_task 后台运行 |
| ClickHouse 写入 | `clickhouse_client.upsert_student_dashboard()` 用 client.insert() 写宽表client 为 None 时降级返回 False |
| Debezium 事件解析 | `before/after/source/op/ts_ms` 五字段op=r(快照)/c(新增)/u(更新)/d(删除) |
| 多表关联缓存 | 内存 ExamCache 缓存 exam_id→class_id 映射(来自 core_edu_exams CDC 事件grades 事件触发时查缓存填充宽表 class_id |
| Consumer offset 重置 | `kafka-consumer-groups --reset-offsets --to-earliest --execute` 需先停消费者让 group 处于 Empty 状态 |
| structlog API | 24.x 用 `make_filtering_bound_logger(level)`,旧版 `make_filtering_logger` 已废弃 |
| 场景 | 技术/规则 |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 学情诊断 | ClickHouse 宽表查询5s 内返回 |
| CDC 链路 | Debezium 监听 MySQL binlog → Kafka`edu-cdc.next_edu_cloud.<table>`)→ DataAna 消费写 ClickHouse |
| CDC 延迟监控 | Debezium 暴露 lag metrics超阈值告警 |
| 双轨读策略 | 实时查 MySQL 主库(刚提交的成绩),聚合查 CH 宽表(延迟 1-5s 可接受) |
| 幂等消费 | 所有事件消费者必须幂等(基于 event_id 去重) |
| CDC 消费者实现 | `cdc_consumer.py` 用 aiokafka AIOKafkaConsumerlifespan 启动 asyncio.create_task 后台运行 |
| ClickHouse 写入 | `clickhouse_client.upsert_student_dashboard()` 用 client.insert() 写宽表client 为 None 时降级返回 False |
| Debezium 事件解析 | `before/after/source/op/ts_ms` 五字段op=r(快照)/c(新增)/u(更新)/d(删除) |
| 多表关联缓存 | 内存 ExamCache 缓存 exam_id→class_id 映射(来自 core_edu_exams CDC 事件grades 事件触发时查缓存填充宽表 class_id |
| Consumer offset 重置 | `kafka-consumer-groups --reset-offsets --to-earliest --execute` 需先停消费者让 group 处于 Empty 状态 |
| 5 张宽表对齐 | student_dashboard_view / student_errors / mastery_snapshot / attendance_logs / ai_usage_logai-allocation §5 强制) |
| ActionState 信封对齐 | Python 服务 `{success, data, degraded}` 必须改 `{success, data, error:{code, message, details?, traceId?}}`degraded 作 details 子字段004 §11.5 P0 |
| gRPC server 启用 | P4 启用 data-ana gRPC server 端口 50055004 §4.2 矩阵HTTP 3006 保留作 Gateway 直连降级 |
| DataScope 6 级过滤 | Repository 层注入 WHERESELF/CLASS/GRADE/SCHOOL/DISTRICT/ALLiam `GetEffectiveDataScope` gRPC P4 补全004 §15.3 #5 |
| Outbox 豁免 | 派生数据事件MasteryUpdated / WarningTriggered / AIUsageRecorded豁免 Outbox允许直接 Kafka producer004 §12.2 + §15.3 #6 |
| 掌握度算法 | WEIGHTED_MOVING_AVGw_i=0.6^i+ FORGETTING_CURVE半衰期 30 天)取 max近 5 次不足降级 SIMPLE_AVG |
| 预警阈值设计 | LOW_MASTERY(<0.4) / CRITICAL_LOW(<0.2) / SCORE_DROP(下降≥20%) / ABSENT_FREQUENT(≥3次/周) / TREND_DECLINE(连续3次降) |
| 预警去重 | 同一 (target_id, warning_type) 24h 内只触发一次Redis 位图 `data_ana:warning:{target_id}:{type}:{date}` TTL 25h |
| CDC 水平扩展 | P4 单实例 + 内存 ExamCache LRU → P6 多实例 + Redis ExamCache + partition 扩容 + HPA |
| CDC 手动 commit | v2 改进:`enable_auto_commit=False` 手动 commitat-least-oncecommit 前确保 ClickHouse 写入成功 |
| ClickHouse 容量规划 | 5 宽表年增量 ~2.3GB5000 学生×20 次/月单节点可承载P6 按 class_id hash 分片 + ReplicatedMergeTree |
| ClickHouse FINAL | ReplacingMergeTree 查询必须加 `FINAL` 或用 `argMax` 聚合否则读到重复版本当前实现未加P0 整改) |
| 缓存键命名 | `data_ana:datascope:{user_id}` 5min / `data_ana:exam:{exam_id}` 30day / `data_ana:dedup:{event_id}` 7day |
| 4 端 Dashboard | teacher/student/parent/admin 各自 Dashboard API + 权限点 ANALYTICS_*_DASHBOARDv2 新增 4 端权限) |
| SubscribeMasteryUpdate | gRPC server-streaming RPC 为 P5+ AI 个性化推荐预留实时推送通道analytics.proto v2 提案) |
| GDPR 数据删除 | ClickHouse 无 UPDATE/DELETE用 `ALTER TABLE ... DELETE WHERE student_id=?` 异步 mutationP6 实现) |
| 字段脱敏 | student_id 在日志中 hash 化structlog processorPII 不入日志 |
| 事件版本化 | 事件 payload 加 `schema_version: "v1"` 字段,破坏性变更发 v2consumer 双消费 6 个月过渡 |
| API 版本化 | HTTP `/analytics/v1/*` + gRPC `next_edu_cloud.analytics.v1`,破坏性变更新增 v2旧版保留 6 个月 |
| Projection 演进 | P4 CDC 直连 → P4 后期双消费CDC + 领域事件)→ P6 ClickHouse MaterializedView 自动聚合 |
| 测试策略 | pytest + TestcontainersClickHouse/Kafka 容器化)+ 单元 ≥80% + 集成 ≥60% + 契约 pact-python + E2E |
| Dockerfile 多阶段 | builderuv sync --frozen+ runtimepython:3.12-slim < 200MB+ HEALTHCHECK 集成 |
| 配置项完整清单 | pydantic-settings 30+ 配置项env_prefix="DATA_ANA_",含 ClickHouse/Kafka/iam gRPC/Redis/OTel/算法参数/预警阈值 |
| 降级模式 4 场景 | ClickHouse/Kafka/Redis/iam gRPC 不可用时返回骨架 + `error.details.degraded=true`4 错误码 DATA_ANA_*_UNAVAILABLE |
| Python gRPC 实现 | grpc.aio + betterproto + ServerInterceptor 透传 W3C trace context与 HTTP 共享同一 Application Service |
| structlog API | 24.x 用 `make_filtering_bound_logger(level)`,旧版 `make_filtering_logger` 已废弃 |
### 2.7 messagingTS/NestJSP5
| 场景 | 技术/规则 |
| ------------- | ------------------------------------------------------------------ |
| 消息 CRUD | 会话/消息 + 调 Push Gateway 推送 + 通知偏好 |
| 通知批量化 | `createBatch(items)` 单次 INSERT沿用旧项目 dispatcher 模式 |
| 多渠道 | 站内/SMS/邮件/微信in_app 批量 + 其他渠道并行 |
| fan-out 分页 | `listByUserWithPagination(userId, page, pageSize)` 分页查询 |
| ES 降级 | ES_URL 未设置时 esClient=nullsafeIndex/safeSearch 跳过返回空结果 |
| Push 推送降级 | PUSH_GATEWAY_URL 未设置或连接失败时 try/catch 跳过,不影响 DB 写入 |
| db 常量导出 | database.ts 导出 `db` 常量替代 `getDb()` 函数 |
| 场景 | 技术/规则 |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 消息 CRUD | 会话/消息 + 调 Push Gateway 推送 + 通知偏好 |
| 通知批量化 | `createBatch(items)` 单次 INSERT沿用旧项目 dispatcher 模式 |
| 多渠道策略 | ChannelStrategy 接口 + InApp/Email/Sms/Push 实现新增渠道不改核心ai10 设计) |
| fan-out 分页 | `listByUserWithPagination(userId, page, pageSize)` 分页查询 |
| ES 降级(单向) | ES_URL 未设置时 esClient=nullsafeIndex/safeSearch 跳过返回空结果 |
| DB→ES 降级读 | DB 故障时 fallback 到 ES 读模型,响应头标 `X-Read-Model: es-degraded`ai10 设计) |
| ES→DB 降级检索 | ES 故障时 search() fallback 到 DB LIKE降级计数打 metricsai10 设计) |
| Push 推送降级 | PUSH_GATEWAY_URL 未设置或连接失败时 try/catch 跳过,不影响 DB 写入PushChannel 失败降级 in_app |
| Kafka 消费幂等 | event_id → Redis SETNX `msg:idem:{event_id}` TTL 7dRedis 不可用降级 msg_idempotency 表唯一键ai10 设计) |
| 三层幂等防线 | L1 Redis SETNX / L2 msg_idempotency 表 / L3 notifications.source_event_id 唯一索引ai10 设计) |
| 已读/未读位图 | Redis Bitmap `msg:unread:{userId}`BITCOUNT 计数Redis 不可用降级 DB COUNTai10 设计) |
| Outbox 强制(待 ai10 确认) | 004 §7.2 列 msg 生产 `edu.notification.events`,按 004 §12.2 强制 OutboxP5 必须实现 msg_outbox_events 表 + Publisher worker |
| 12 类事件消费清单(待 ai10 确认) | 消费 iamuser.created/updated/deleted/role_changed + role.created/updated+ core-eduexam.published/assignment.submitted/assignment.graded/grade.recorded/attendance.recorded+ data-anamastery.updated共 12 类事件触发通知 |
| 4 类发布事件(待 ai10 确认) | 发布 NotificationSent / NotificationRead / NotificationRecalled / NotificationFailed 4 类事件,消费者 push-gateway + data-ana |
| 通知状态机(待 ai10 确认) | pending → sent → delivered → read / failed / recalled含 6 状态转换 + 撤回场景预留 |
| NotificationTemplate 模板渲染(待 ai10 确认) | titleTemplate/contentTemplate 含 `{{variable}}` 占位符RenderTemplate RPC 根据 variables 替换渲染,支持 locale 多语言 |
| NotificationPreference 用户偏好(待 ai10 确认) | 渠道开关 channels[] + 频率限制 frequency_limit + 静默时段 quiet_hours5 维度配置矩阵 |
| Push Gateway 调用方向澄清(待 ai10 确认) | 004 §4.1 表述"PushGW→Msg 推送通道建立"歧义,实际方向是"msg → push-gateway (gRPC PushService.Push)",建议 coord 修正 004 §4.1 表述 |
| 频率限流(待 ai10 确认) | Redis 计数 `msg:ratelimit:{userId}:{type}:{yyyyMMddHH}` TTL 1h按 NotificationPreference.frequency_limit 限制Redis 不可用降级跳过限流 |
| 优雅关闭顺序(待 ai10 确认) | HTTP server → Kafka consumer → Outbox publisher → ChannelDispatcher → Redis → ES → DB pool → Tracer8 步序列保证在途消息处理完成 |
| events.proto message 缺失阻塞(待 ai10 确认) | events.proto 缺 UserEvent/RoleEvent/NotificationEvent/MasteryEvent 4 类 messagemsg 消费 iam/data-ana 事件阻塞,提请 coord 补齐 proto |
| 死信队列 | 消费失败超 3 次投递 `edu.notification.dlq`告警人工介入ai10 设计) |
| Outbox 事件发布 | 事务内写 msg_outbox + 业务表,独立 publisher 投递 `edu.notification.events`(参照 core-edu |
| 模板渲染 | TemplateService + {{var}} 替换 + i18n locale渲染失败降级默认文案记 MSG_TEMPLATE_RENDER_ERROR |
| 静默时段 | QuietHours 过滤非紧急通知URGENT 优先级绕过静默ai10 设计) |
| 短信限流配额 | Redis 滑动窗口限流 + 配额计数,超限返 MSG_RATE_LIMITED/MSG_QUOTA_EXCEEDEDai10 设计) |
| 优雅关闭 | 统一到 LifecycleService顺序 HTTP→Kafka→Outbox→ES→Redis→DB→Tracerai10 修订) |
| db 常量导出 | database.ts 导出 `db` 常量替代 `getDb()` 函数 |
| db 常量导出 | database.ts 导出 `db` 常量替代 `getDb()` 函数 |
### 2.8 push-gatewayGoP5
@@ -334,21 +406,98 @@
### 2.12 teacher-portal微前端宿主P1 测试页)
| 场景 | 技术/规则 |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P1 测试页 | 单一 Next.js 应用,验证 classes CRUD 端到端链路 |
| API 调用 | `fetch(${API_BASE}/api/v1/classes)` + `Authorization: Bearer ${TEST_JWT}` |
| P1 测试 JWT | 开发工具生成 HS256 tokenP2 起由 IAM 签发 RS256 |
| P2 Module Federation | next.config.js 配置,按场景域分 4 个稳定 portal |
| P2 路由组 + AppShell | `app/(app)/layout.tsx` 用 AppShell 包裹受保护页;`/login` 与 `/` 不套壳 |
| P2 真实 JWT | 登录后 token 存 localStorage`authHeaders()` 读 `Bearer ${getToken()}` |
| P2 视口驱动侧边栏 | AppShell fetch `/teacher/viewports` 渲染左侧导航active 路由高亮 |
| P2 根路径重定向 | `app/page.tsx` 客户端组件 `router.replace(isAuthenticated() ? '/dashboard' : '/login')` |
| fetch headers 类型 | `authHeaders(): Record<string, string>` 显式标注,避免 `{}` 与 `HeadersInit` 不兼容 |
| P2 前端权限校验等价物 | `usePermission().hasPermission()` Hook + `<RequirePermission>` 组件,镜像后端 `@RequirePermission()` 装饰器,禁止 `role === "xxx"` 硬编码§3.1 |
| P2 MF Shell+Remote 架构 | teacher-portal:3000 为 Shellstudent:3001/parent:3002/admin:3003 为 Remote共享登录态/布局/组件库/权限体系 |
| P2 统一 API 请求层 | ApiClient 封装 401 自动刷新 token 轮转 + ActionState `{success,data,error}` 解析 + 错误码前缀路由 i18n替代页面级 `authHeaders()+fetch` 重复 |
| P2 设计令牌三层模型 | primitive原始色板→ semantic-light/dark语义→ tailwind-theme`@theme inline` 暴露 `bg-*`ESLint `no-restricted-syntax` 禁 `#hex` + `design-tokens/no-hardcoded-fonts` 禁字面量字体§3.10 |
| P2 5 层状态管理 | nuqsURL 同步)/ TanStack Query服务端缓存/ Zustand客户端业务状态/ Zustand UIUI 临时态)/ react-hook-form表单 |
| P2 4 端错误码前缀对齐 | TP_=teacher-bff / SP_=student-bff / PP_=parent-bff / AP_=admin-bff前端按前缀路由 i18n key |
| P2 ErrorBoundary | 路由级 `<ErrorBoundary>` 包裹避免白屏,搭配 `<Suspense>` 流式加载 |
| 场景 | 技术/规则 |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P1 测试页 | 单一 Next.js 应用,验证 classes CRUD 端到端链路 |
| API 调用 | `fetch(${API_BASE}/api/v1/classes)` + `Authorization: Bearer ${TEST_JWT}` |
| P1 测试 JWT | 开发工具生成 HS256 tokenP2 起由 IAM 签发 RS256 |
| P2 Module Federation | next.config.js 配置,按场景域分 4 个稳定 portal |
| P2 路由组 + AppShell | `app/(app)/layout.tsx` 用 AppShell 包裹受保护页;`/login` 与 `/` 不套壳 |
| P2 真实 JWT | 登录后 token 存 localStorage`authHeaders()` 读 `Bearer ${getToken()}` |
| P2 视口驱动侧边栏 | AppShell fetch `/teacher/viewports` 渲染左侧导航active 路由高亮 |
| P2 根路径重定向 | `app/page.tsx` 客户端组件 `router.replace(isAuthenticated() ? '/dashboard' : '/login')` |
| fetch headers 类型 | `authHeaders(): Record<string, string>` 显式标注,避免 `{}` 与 `HeadersInit` 不兼容 |
| P2 MF Shell+Remote 架构 | teacher-portal:**4000** 为 Shellstudent:**4001**/parent:**4002**/admin:**4003** 为 Remote004 §1.2 + coord final §F1MF 暴露 **AppShell 整体**coord final §F10各 Remote 自行决定内部布局 |
| P2 统一 API 请求层 | ApiClient 封装 401 自动刷新 token 轮转 + ActionState `{success,data,error:{code,message,details?,traceId}}` 解析 + 错误码前缀路由 i18n + trace_id 透传;**P2 起对接 GraphQL**urql/apollocoord final §F9/B1 |
| P2 统一 API 请求层 | ApiClient 封装 401 自动刷新 token 轮转 + ActionState `{success,data,error}` 解析 + 错误码前缀路由 i18n替代页面级 `authHeaders()+fetch` 重复 |
| P2 设计令牌三层模型 | primitive原始色板→ semantic-light/dark语义→ tailwind-theme`@theme inline` 暴露 `bg-*`ESLint `no-restricted-syntax` 禁 `#hex` + `design-tokens/no-hardcoded-fonts` 禁字面量字体§3.10 |
| P2 4 端错误码前缀对齐 | **BFF_TEACHER\_=teacher-bff / BFF_STUDENT\_=student-bff / BFF_PARENT\_=parent-bff**coord final §B5统一 `BFF_XXX_` 风格admin-bff 待 P6 定;前端按前缀路由 i18n key |
| P2 4 端错误码前缀对齐 | TP_=teacher-bff / SP_=student-bff / PP_=parent-bff / AP_=admin-bff前端按前缀路由 i18n key |
| P2 i18n key 命名 | **统一 `error.<service>.<code_snake>`**(如 error.iam.invalid_credentials、error.bffTeacher.validation_errorcoord final §F4翻译文件按域分 namespace |
| P2 权限点命名风格 | **统一 `<RESOURCE>_<ACTION>`**(如 DASHBOARD_VIEW、EXAM_READ、GRADE_READ数据范围用后缀 `_OWN`/`_CHILD`(如 GRADE_READ_CHILDcoord final §F7 |
| P2 Token 存储 | **P2 用 localStorage**accessToken + refreshTokencoord final §F12httpOnly Cookie + CSRF Token **P6 单独评估**非分阶段迁移P2 直接采用最终方案的当前阶段形态) |
| P2 packages 归属 | **ai13 维护** packages/ui-tokens、ui-components、hookscoord final §F8coord 仅维护 shared-ts、contractsai13 需建立这 3 个共享包 |
| P2 GraphQL 客户端 | **P2 起前端用 urql 或 apollo**coord final §F9对接 teacher-bff GraphQL Yoga + DataLoader废弃 REST fetch 模式ApiClient 适配 GraphQL 操作类型query/mutation/subscription |
| P2 视口端点对齐 | **GET /iam/permissions/effective**(非 /iam/effective-permissionscoord final §F6/I8+ GET /iam/viewports + GET /iam/rolesparent 子女切换 POST /parent/children/:childId/select |
### 2.13 parent-portal家长端微前端 RemoteP4
> parent-portal 复用 teacher-portal Shell 暴露的 AppShell / usePermission / ApiClient / ErrorBoundary / 设计令牌 / A11y 工具集,本节仅记录 parent-portal **特有**的"场景→技术"映射,通用前端映射见 §2.12。
| 场景 | 技术/规则 |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| P4 端口分配 | parent-portal = **4002**004 §1.2 强制 4 端 4000-4003ai07 初稿误用 3002 与 iam 冲突ai15 修订) |
| P4 MF Remote 角色 | next.config.js 配 `@module-federation/nextjs-mf` 暴露 `./ChildSwitcher` 等家长端特有组件,复用 Shell 提供的 `AppShell`/`usePermission`/`ApiClient`/`RequirePermission` |
| P4 DataScope CHILDREN | 家长仅能查看自己绑定子女的数据004 §5.4 L0 SELF 变体),前端通过 ApiClient 透传 userId + childId下游服务按 DataScope 过滤 |
| P4 多子女切换 ChildSwitcher | Zustand slice`selectedChildId`+ 切换后 `queryClient.invalidateQueries({ queryKey: ['child', prevId] })`;多子女 Tab≤3或下拉≥4空态/单子女/多子女三态分支 |
| P4 多子女数量边界 | 0 子女EmptyChildState 引导绑定)→ 1 子女(隐藏切换器)→ 2-3Tab Bar→ 4-10下拉→ >10搜索 + 列表);超出 10 触发性能预警 |
| P4 通知偏好三维矩阵 | `Record<childId, Record<eventType, Record<channel, boolean>>>`react-hook-form + Zod 校验;默认值矩阵(成绩发布=邮件+短信、作业批改=邮件、教师沟通=短信、学校通知=邮件);渠道可用性校验 |
| P4 跨标签同步 | BroadcastChannel API`parent-portal-sync`+ localStorage 持久化 + LWWLast-Write-Wins冲突解决同步 selectedChildId / 通知偏好 / 已读状态 |
| P4 隐私合规COPPA/FERPA/PIPL | 未成年人数据最小化采集;通知偏好默认 opt-in数据保留 7 年后自动归档;前端不缓存敏感字段到 localStorage仅 childId + 已读位图) |
| P4 i18n 5 语言含 RTL | next-intl 支持 zh-CN / en-US / zh-TW / ja-JP / ar-SAar-SA 用 `dir="rtl"` + Tailwind `rtl:` 变体;翻译文件按域分 namespaceparent/grades/notifications/preferences |
| P4 PWA + Service Worker | manifest.jsonname/short_name/icons/theme_color+ Service Worker 缓存策略App Shell precache + API stale-while-revalidate + 离线兜底页P5+ 支持离线查看子女成绩缓存 |
| P4 API 契约版本管理 | 主版本 URL `/api/v1/*` → `/api/v2/*` + 子版本响应头 `X-API-Version` + Deprecation/Sunset 头埋点 + 低带宽场景 `X-Fields` 字段裁剪GraphQL 风格BFF REST 透传) |
| P4 性能预算 | Bundle ≤ 80KB JS / 20KB CSS / 200KB 总recharts 懒加载MF shared 单例避免 React 重复打包dynamic import 非首屏组件;预加载子女数据 |
| P4 监控与降级 | 10 项前端指标MF 加载失败率/ApiClient 错误率/Web Vitals/切换延迟等)+ 9 项降级策略MF 失败回退独立页/BFF 5xx 降级缓存/WS 重试指数退避等) |
| P4 模块演化与解耦 | 拆分触发条件Bundle > 120KB / 团队 > 3 人 / 独立部署需求);拆分后 MF 配置从 Shell 暴露状态管理迁移路径Zustand → Server Component + Server ActionsMF 升级路径MF 2.0 → 3.0 |
| P4 iam 家长-学生关联 P0 阻塞 | iam 缺 `GetChildrenByParent` proto RPC + `GET /iam/children` REST + `iam_student_guardians` 表ai02 补全前 parent-portal 仅能假设单子女硬编码 childId 开发调试 |
| P4 BFF 错误码前缀 | **BFF_PARENT\_=parent-bff**coord final §B5统一 `BFF_XXX_` 风格);前端按 `BFF_PARENT_*` 前缀路由 i18n key与 BFF_TEACHER_/BFF_STUDENT_ 对齐 |
### 2.14 parent-bffTS/NestJSP4
> parent-bff 是 BFF 聚合层,无 DB无聚合根无 @RequirePermissionU4 仲裁豁免)。本节记录 parent-bff 特有的"场景→技术"映射。
| 场景 | 技术/规则 |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| BFF 聚合层定位 | 只聚合不持状态,无 DB无聚合根无 @RequirePermissionU4 仲裁豁免,权限由 Gateway JWT + 下游服务负责) |
| GraphQL Yoga 端点 | POST /graphqlGraphQL over HTTP对前端 GraphQL对下游 gRPCU3 仲裁 GraphQL P2 立即引入parent-bff P4 直接用 GraphQL |
| DataScope=CHILDREN 越权校验 | ChildGuard 在 Resolver 执行前校验 requestedChildId ∈ iam.GetChildrenByParent(parentId),不绑定抛 BFF_PARENT_CHILD_NOT_BOUND403 |
| 多子女切换方案 A | 前端管理 childIdZustand sliceBFF 无状态ParentSession 仅请求级值对象不持久化selectChild mutation 仅记录审计日志 |
| 并行 gRPC 编排 | Promise.allSettled + 降级策略iam 失败返回 user=nullcore-edu 失败返回 grades=null部分失败标记 partial=true |
| DataLoader 批量去重 | per-request 实例,按 parentId 批量取孩子列表、按 studentId 批量取成绩/作业/考试,防 N+1 |
| Redis 聚合缓存 | 30s TTLcoord 推断 60sai05 调整为 30s + 主动失效key=bff:parent:{userId}:{queryHash}singleflight 防击穿 |
| 错误码前缀 | BFF_PARENT__coord 仲裁 C1对齐 004 §11.4 BFF 在前风格,不是 PARENT_BFF__ |
| 端口分配 | HTTP 3010不暴露 gRPCcoord 仲裁 C2BFF 对下游走 gRPC对上游仅 HTTP/GraphQL |
| 下游依赖 | iam + core-edu + data-ana + msgcoord 仲裁 C6 扩展,与 student-bff 对齐) |
| P0 阻塞 | iam 缺 GetChildrenByParent RPC + iam_student_guardians 表ai02 补全前 parent-bff 无法启动 |
| push-gateway 调用 | 走 HTTP /internal/pushU2 仲裁豁免 gRPCmsg 调用方改用 HTTP |
| Kafka topic 命名 | edu.notification.sent/read/recalled/failedcoord 仲裁 C5废弃 edu.notification.events 抽象名) |
| /readyz 下游探针 | P4 添加并行 ping iam + core-edu + data-ana + msg 的 /healthzcoord 推断 BFF 直接 okai05 调整为 P4 加下游探针) |
| GraphQL complexity 防御 | depth limit≤7+ query cost analysis≤1000+ persisted queriesP6 预留) |
| 优雅关闭 | HTTP server → Redis → gRPC clients → TracerBFF 无 DB/Kafka关闭顺序简化 |
| ParentSession 值对象 | parentId/roles/dataScope:CHILDREN/requestedChildId/traceId每次请求重建不跨请求004 §12.1 无状态约束) |
| 通知偏好配置 | NotificationPreference渠道开关 channels[] + 频率限制 frequency_limit + 静默时段 quiet_hoursP4 通过 msg 服务管理 |
### 2.15 student-portal学生端微前端 RemoteP3
> student-portal 复用 teacher-portal Shell 暴露的 AppShell / usePermission / ApiClient / ErrorBoundary / 设计令牌 / A11y 工具集,本节仅记录 student-portal **特有**的"场景→技术"映射,通用前端映射见 §2.12。
| 场景 | 技术/规则 |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| P3 端口分配 | student-portal = **4001**004 §1.2 强制 4 端 4000-4003ai07 初稿误用 3001 与 iam 冲突ai14 修订) |
| P3 MF Remote 角色 | next.config.js 配 `@module-federation/nextjs-mf`name: `student_app`port 4001暴露 `./pages` + `./ExamTaking`,复用 Shell 的 `AppShell`/`usePermission`/`ApiClient` |
| P3 DataScope SELF | 学生仅看自己数据004 §5.4 L0前端**无班级/年级/学校切换器**(与 teacher-portal L1-L5 核心差异),路由参数不含 classId/gradeId/schoolId |
| P3 错误码前缀统一 | core-edu 子域统一 `CORE_EDU_`(移除 `EXAMS_`/`HOMEWORK_`/`GRADES_` 独立前缀BFF 前缀 `BFF_STUDENT_`coord §5.2 裁决,从 `STUDENT_BFF_` 迁移) |
| P3 考试防作弊 useAntiCheat | `visibilitychange` 切屏检测 + `blur` 失焦 + `copy`/`paste`/`contextmenu` 禁止 + 违规计数maxViolations=3+ 超限自动交卷;前端层仅辅助,最终权威由后端考试时长 + 服务端时间校准保证 |
| P3 服务器时间同步 | `useServerTimeSync` HookRTT 计算 `offset = (t1 + rtt/2) - serverTime` + 5min 重同步周期;`CountdownTimer` 用 `role="timer"` + `aria-live="polite"` 朗读剩余时间 |
| P3 考试作答状态机 | NotStarted→InProgress→Paused切屏警告→AutoSubmitting倒计时归零→Submitting→SubmittedZustand L3 + localStorage 持久化草稿,自动保存每 30s + blur 时保存 |
| P3 考试页面 CSP iframe 防嵌套 | 考试路由 `frame-ancestors 'none'` + `X-Frame-Options: DENY`;其他路由允许 Shell 同源嵌入 |
| P3 考试 chunk 性能预算 | 考试作答 chunk < 200KB动态 import 隔离);总 Bundle < 300KBLCP < 2.5s / CLS < 0.1 / INP < 200ms |
| P3 移动端一等公民 | 学生端高频移动场景Tailwind 响应式断点 + viewport meta + 触摸优化(按钮 ≥ 44px考试页面支持横屏锁定 |
| P3 A11y 考试专项 | WCAG 2.2 AA键盘导航作答Tab/Enter/方向键)+ `aria-live` 倒计时 + 焦点陷阱(考试中不可 Tab 到地址栏)+ 屏幕阅读器兼容 |
| P3 考试草稿多设备同步预留 | 当前 localStorage 单设备P5+ 预留草稿同步LWW 冲突解决 + WebSocket 推送 + version 字段) |
| P3 Feature Flag 阶段化 | P3-P5 用环境变量(`NEXT_PUBLIC_FEATURE_*`P6+ 预留 Consul 配置中心动态开关 |
| P3 PWA 离线壳预留 | P5+ Service Worker 缓存考试壳,断网时仍可作答,恢复后自动提交 |
| P3 MF 降级与版本协商 | Remote `remoteEntry.js` 加载失败 → fallback 独立页面 + 错误提示;共享依赖版本不匹配 → 运行时协商降级 |
| P3 AI 答疑 SSE 流式P5 | `POST /student/ai/stream-chat` SSE 流式响应 + `streamingText` 逐字渲染 + 打字机效果 + AbortController 取消 |
| P3 权限点命名 | 与 student-bff §3.2 对齐 `STUDENT_*_READ` 风格(`STUDENT_DASHBOARD_READ`/`STUDENT_EXAM_READ`/`STUDENT_HOMEWORK_SUBMIT`/`STUDENT_GRADE_READ` 等) |