chore(parent-portal): merge parent-portal module into main
Merge feat/parent-portal-ai15 into main, conflicts resolved in favor of feature branch
This commit is contained in:
@@ -458,24 +458,36 @@
|
||||
|
||||
> parent-portal 复用 teacher-portal Shell 暴露的 AppShell / usePermission / ApiClient / ErrorBoundary / 设计令牌 / A11y 工具集,本节仅记录 parent-portal **特有**的"场景→技术"映射,通用前端映射见 §2.12。
|
||||
|
||||
| 场景 | 技术/规则 |
|
||||
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| P4 端口分配 | parent-portal = **4002**(004 §1.2 强制 4 端 4000-4003;ai07 初稿误用 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-3(Tab Bar)→ 4-10(下拉)→ >10(搜索 + 列表);超出 10 触发性能预警 |
|
||||
| P4 通知偏好三维矩阵 | `Record<childId, Record<eventType, Record<channel, boolean>>>`;react-hook-form + Zod 校验;默认值矩阵(成绩发布=邮件+短信、作业批改=邮件、教师沟通=短信、学校通知=邮件);渠道可用性校验 |
|
||||
| P4 跨标签同步 | BroadcastChannel API(`parent-portal-sync`)+ localStorage 持久化 + LWW(Last-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-SA;ar-SA 用 `dir="rtl"` + Tailwind `rtl:` 变体;翻译文件按域分 namespace(parent/grades/notifications/preferences) |
|
||||
| P4 PWA + Service Worker | manifest.json(name/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 Actions);MF 升级路径(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_ 对齐 |
|
||||
| 场景 | 技术/规则 |
|
||||
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| P4 端口分配 | parent-portal = **4002**(004 §1.2 强制 4 端 4000-4003;ai07 初稿误用 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-3(Tab Bar)→ 4-10(下拉)→ >10(搜索 + 列表);超出 10 触发性能预警 |
|
||||
| P4 通知偏好三维矩阵 | `Record<childId, Record<eventType, Record<channel, boolean>>>`;react-hook-form + Zod 校验;默认值矩阵(成绩发布=邮件+短信、作业批改=邮件、教师沟通=短信、学校通知=邮件);渠道可用性校验 |
|
||||
| P4 跨标签同步 | BroadcastChannel API(`parent-portal-sync`)+ localStorage 持久化 + LWW(Last-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-SA;ar-SA 用 `dir="rtl"` + Tailwind `rtl:` 变体;翻译文件按域分 namespace(parent/grades/notifications/preferences) |
|
||||
| P4 PWA + Service Worker | manifest.json(name/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 Actions);MF 升级路径(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_ 对齐 |
|
||||
| P4 认证 token 存储 | localStorage(F12 裁决,P4 用 localStorage,非 cookie);refreshAccessToken 竞态防护用 inflight Promise 复用,并发请求只触发一次 refresh fetch |
|
||||
| P4 switchChild 纯前端 | ISSUE-009 裁决:切换子女不调后端,仅 Zustand slice + localStorage 持久化 selectedChildId;切换后由前端 invalidateQueries 触发数据刷新 |
|
||||
| P4 通知偏好 localStorage 降级 | ISSUE-033 裁决:P4 localStorage 为准,更新先写本地再尝试 GraphQL mutation(失败不阻塞);P5+ 切换到后端为主存储 |
|
||||
| P4 上游 mock 策略 | MSW 拦截 parent-bff GraphQL + iam REST;iam GetChildrenByParent P0 阻塞用 mock fixtures(student-001 + student-002 双子女);MSW server 在 providers 启动 |
|
||||
| TS noUncheckedIndexedAccess | Record 索引访问返回 `T \| undefined`;内层 Record 改 `Partial<Record<...>>` 或空对象回退(`?? {}`)避免 undefined 传播 |
|
||||
| urql fetchOptions headers 类型 | 返回类型须用 `Record<string, string>` 累积再展开,不可用条件对象(`{...(token?{Authorization}:{})}`)否则 TS 推断不兼容 |
|
||||
| Vitest Promise 身份比较 | `expect(p1).toBe(p2)` 对 Promise 不可靠(async 包装产生新 Promise);改为验证行为副作用(如 fetch 调用次数 === 1 证明竞态防护生效) |
|
||||
| jsdom window.location.href | 设置触发 "Not implemented: navigation" stderr 警告(非错误,测试仍通过);mock 用 `Object.defineProperty(window, "location", {...})` 替代 `vi.spyOn` |
|
||||
| MSW 响应过快观察加载态 | MSW 默认同步响应,加载态测试看不到 "加载中";用 `server.use()` + `delay(ms)` 延迟响应再断言按钮文本 |
|
||||
| Testing Library getByRole 误匹配 | `getByRole("button", { name: /未读/ })` 会匹配通知项 aria-label="未读";改用 `getAllByRole("button")` + find 文本内容过滤精确选中筛选按钮 |
|
||||
| ESLint flat config 测试文件 | 放宽规则需含 `*.test.tsx`/`*.spec.tsx`/`__tests__/**` 模式,否则 tsx 测试文件按严格规则报错 |
|
||||
| Dockerfile 多阶段构建 | G1 裁决首次即多阶段:builder(node:20-alpine 跑 build)+ runner(node:20-alpine 跑 server),端口 4002,HEALTHCHECK 指向 `/api/health`,非 root 用户 nextjs |
|
||||
|
||||
### 2.14 parent-bff(TS/NestJS,P4)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user