feat(teacher-portal): v2 下游核查 + push-gateway 接入 + 测试扩展至 87 + ui-components 12/12
v2 核查结论: - iam/push-gateway/api-gateway 已就绪 - teacher-bff teacher 域仍是 P2 占位(56/61 operations 不可用) - 5 个 gRPC target 留空走降级模式 B - 保留 MSW mock,记录 12 项 teacher-bff 待补工作到 nextstep-v2.md v2 完成工作: - push-gateway WebSocket 接入(环境变量 + URL query token + Reconnect 协议) - 单元测试扩展(usePermission 15 + useAuth 9,总计 87/87 passed) - ui-components 剩余 3 组件(Chart/Calendar/RichTextEditor,12/12) - 设计令牌完整迁移(无 hsl/hex 字面量) - i18n 5/55 页面 + 26 模块 key - 性能优化(size-limit 9 项 + 5 页面懒加载) - useAuth 迁移阻塞核查(iam 未实现 Set-Cookie,修正 v1 假设) 文档:新增 nextstep-v2.md;workline.md §5.7 添加 v2 工作记录。 验证:typecheck + lint + 87/87 tests + arch:scan 全部通过。
This commit is contained in:
@@ -223,10 +223,20 @@
|
||||
|
||||
#### 3.2.2 useAuth hook 迁移到 httpOnly cookie(F12 裁决)
|
||||
|
||||
**阻塞条件**:无(iam 已就绪)
|
||||
**v2 核查更新(2026-07-14)**:阻塞条件已修正。
|
||||
|
||||
**清单**:
|
||||
**阻塞条件**:⚠️ 阻塞于 iam 后端未实现 Set-Cookie
|
||||
|
||||
**核查发现**:iam nextstep.md §1.1 确认登录接口 `POST /v1/iam/login` 返回 JWT token pair(access_token + refresh_token)在 response body 中,但**未在响应头设置 Set-Cookie**。F12 裁决要求 httpOnly cookie 模式需要 iam 后端配合:
|
||||
|
||||
- iam REST 登录接口需要设置 `Set-Cookie: refresh_token=...; HttpOnly; Secure; SameSite=Strict`
|
||||
- teacher-portal 前端才能移除 localStorage JWT 存储
|
||||
|
||||
**当前状态**:teacher-portal useAuth 仍使用 localStorage JWT 模式(F12 裁决 P2 阶段方案),等待 iam 后端实现 Set-Cookie 后迁移。
|
||||
|
||||
**清单**(待 iam 后端支持后执行):
|
||||
|
||||
- [ ] 等待 iam REST 登录接口设置 `Set-Cookie: refresh_token=...; HttpOnly; Secure; SameSite=Strict`
|
||||
- [ ] 修改 `packages/hooks/src/use-auth.ts`:从 localStorage JWT 改为 httpOnly cookie
|
||||
- [ ] 修改 `src/app/(auth)/login/page.tsx`:登录成功后不再手动存 JWT 到 localStorage
|
||||
- [ ] 修改 `src/lib/graphql-client.ts`:urql fetcher 不再手动添加 Authorization header(浏览器自动带 cookie)
|
||||
@@ -234,67 +244,91 @@
|
||||
- [ ] 添加 CSRF 防护(double-submit cookie 模式)
|
||||
- [ ] 测试:登录 → Dashboard → 刷新页面 → 保持登录态
|
||||
|
||||
#### 3.2.3 push-gateway WebSocket 接入
|
||||
#### 3.2.3 push-gateway WebSocket 接入 ✅ 已完成(2026-07-14)
|
||||
|
||||
**阻塞条件**:无(push-gateway 已就绪)
|
||||
|
||||
**清单**:
|
||||
**v2 完成情况**:
|
||||
|
||||
- [ ] 添加环境变量 `NEXT_PUBLIC_PUSH_GATEWAY_WS_URL=ws://localhost:8081/ws`(开发) / `ws://push-gateway:8081/ws`(Docker)
|
||||
- [ ] 修改 `src/hooks/use-notifications-websocket.ts`:从 mock WebSocket 改为真实 push-gateway 连接
|
||||
- [ ] 修改 JWT token 传递方式:URL query param `?token=<jwt>` 或 Sec-WebSocket-Protocol header
|
||||
- [ ] 测试:通知中心实时接收推送
|
||||
- ✅ 添加环境变量 `NEXT_PUBLIC_PUSH_GATEWAY_WS_URL`(默认 `ws://localhost:8081/ws`)
|
||||
- ✅ 修改 `src/hooks/use-notifications-websocket.ts`:从硬编码 URL 改为环境变量
|
||||
- ✅ JWT token 传递:URL query param `?token=<jwt>`(从 localStorage 读取 edu_access_token,兼容 DevMode dev-token)
|
||||
- ✅ Reconnect 协议(P6):session_id(crypto.randomUUID)+ last_seq(消息序号追踪)
|
||||
- ✅ 手动重连生成新 session_id(触发服务端 ring buffer 补发)
|
||||
- ✅ 兼容 push-gateway 事件格式(event_type + payload 结构)
|
||||
- ✅ Mock 模式保留(NEXT_PUBLIC_API_MOCKING=enabled 时用 setInterval mock)
|
||||
|
||||
**验证**:typecheck + lint + 87/87 tests passed
|
||||
|
||||
#### 3.2.4 i18n 扩展(剩余 30+ 页面)
|
||||
|
||||
**阻塞条件**:无
|
||||
|
||||
**v2 完成情况**:部分完成
|
||||
|
||||
- ✅ `src/messages/zh-CN.json` + `src/messages/en.json` 已包含全部 26 模块的 key(common/dashboard/grades/settings 等)
|
||||
- ✅ 5 个示范页面已迁移:dashboard / grades / grades/entry / grades/stats / settings
|
||||
- ✅ LocaleSwitcher 组件已实现(cookie 驱动 + router.refresh)
|
||||
- ⏳ 剩余 50+ 页面未迁移(classes/exams/homework/notifications/students 等)
|
||||
- ⏳ 需要为剩余页面逐个添加 `useTranslations` + 替换中文硬编码
|
||||
|
||||
**清单**:
|
||||
|
||||
- [ ] 其余 30+ 页面中文硬编码替换为 `t("...")` 调用
|
||||
- [ ] 补充 `src/messages/zh-CN.json` 和 `src/messages/en.json` 缺失的 key
|
||||
- [ ] 测试:切换 locale 后所有页面文本正确显示
|
||||
- [ ] classes/page.tsx → t("classes.title")
|
||||
- [ ] exams/page.tsx → t("exams.title")
|
||||
- [ ] homework/page.tsx → t("homework.title")
|
||||
- [ ] notifications/page.tsx → t("notifications.title")
|
||||
- [ ] students/page.tsx → t("students.title")
|
||||
- [ ] ... 其余 45+ 页面
|
||||
|
||||
#### 3.2.5 性能优化扩展
|
||||
#### 3.2.5 性能优化扩展 ✅ 基本完成(2026-07-14)
|
||||
|
||||
**阻塞条件**:无(非紧急)
|
||||
|
||||
**清单**:
|
||||
**v2 完成情况**:
|
||||
|
||||
- [ ] 接入 CI 阻断(size-limit + bundle analyzer)
|
||||
- [ ] 路由懒加载:剩余重型页面(exams/[id]/build、knowledge-graph 等)使用 `next/dynamic`
|
||||
- [ ] 图片优化:`next/image` 替换 `<img>`(需配置 remotePatterns)
|
||||
- [ ] 字体优化:`next/font` 替换 CSS @font-face
|
||||
- ✅ `size-limit.json` 已配置 9 项性能预算检查(Shell/Remote/CSS/6 个路由)
|
||||
- ✅ 5 个重型页面已懒加载:lesson-plans/heatmap、grades/analytics、knowledge-graph、ParentPortalRemote、HeatmapMatrix
|
||||
- ⏳ 图片优化(next/image 替换 `<img>`)— 待配置 remotePatterns
|
||||
- ⏳ 字体优化(next/font 替换 CSS @font-face)— 待配置
|
||||
- ⏳ CI 阻断集成(size-limit + bundle analyzer)— 待 CI 配置
|
||||
|
||||
#### 3.2.6 单元测试扩展
|
||||
#### 3.2.6 单元测试扩展 ✅ 已完成(2026-07-14)
|
||||
|
||||
**阻塞条件**:无(非紧急)
|
||||
|
||||
**清单**:
|
||||
**v2 完成情况**:
|
||||
|
||||
- [ ] 测试 `usePermission` hook(权限矩阵)
|
||||
- [ ] 测试 `useAuth` hook(登录/登出/会话保活)— 注意:迁移到 httpOnly cookie 后需更新测试
|
||||
- [ ] 测试关键页面组件:AppShell / grades/entry / homework/submissions/[submissionId]
|
||||
- [ ] 配置 CI 运行 `pnpm --filter @edu/teacher-portal test`
|
||||
- ✅ 测试 `usePermission` hook(15 tests:hasPermission/hasAny/hasAll/hasRole/dataScope/context=null/useMemo 稳定性)
|
||||
- ✅ 测试 `useAuth` hook(9 tests:初始状态/localStorage 恢复/login/logout/refreshUser/跨实例共享/JSON 损坏降级)
|
||||
- ✅ 测试 MSW handlers(10 tests)+ handlers-p4(13 tests)
|
||||
- ✅ 测试 useCrossTabSync(6 tests)+ useNotificationsWebSocket(6 tests)
|
||||
- ✅ 测试 fixtures:exams(17 tests)+ viewports(11 tests)
|
||||
- ✅ 总计 87/87 tests passed(从 v1 的 63 tests 扩展到 87 tests)
|
||||
|
||||
#### 3.2.7 设计令牌完整迁移
|
||||
#### 3.2.7 设计令牌完整迁移 ✅ 已完成(2026-07-14)
|
||||
|
||||
**阻塞条件**:无(非紧急)
|
||||
|
||||
**清单**:
|
||||
**v2 完成情况**:
|
||||
|
||||
- [ ] `src/app/styles/globals.css`:将 `hsl(0 0% 100%)` 等字面量迁移到 `primitive.css`
|
||||
- [ ] `tailwind.config.js`:将 `hsl(...)` 字面量迁移到 `@theme inline` 块
|
||||
- ✅ `src/app/globals.css`:全部使用 `var(--*)` 引用,无 hsl() 字面量
|
||||
- ✅ `tailwind.config.js`:全部使用 `var(--*)` 引用,无 hsl() 字面量
|
||||
- ✅ ESLint `.eslintrc.tokens.js`:flat config 格式,禁止 #hex 和字体字面量
|
||||
- ✅ ESLint `.eslintrc.a11y.js`:flat config 格式,jsx-a11y 规则
|
||||
- ✅ 源码扫描确认:`src/` 目录无 hsl() 或 #hex 字面量(仅 a11y.ts 注释中提到 hex 说明)
|
||||
|
||||
#### 3.2.8 ui-components 剩余组件
|
||||
#### 3.2.8 ui-components 剩余组件 ✅ 已完成(2026-07-14)
|
||||
|
||||
**阻塞条件**:无(非紧急,待业务真实需求时抽取)
|
||||
|
||||
**清单**:
|
||||
**v2 完成情况**:
|
||||
|
||||
- [ ] `RichTextEditor` 组件(Tiptap 封装,lesson-plans/edit + exams/edit-rich 复用)
|
||||
- [ ] `Chart` 组件(SVG 图表统一封装)
|
||||
- [ ] `Calendar` 组件(lesson-plans/calendar 复用)
|
||||
- ✅ `Chart` 组件(SVG 图表统一封装):支持 bar/line/pie 三种类型,纯 SVG 实现无额外依赖
|
||||
- ✅ `Calendar` 组件(月视图日历):支持前后月切换、日期点击回调、事件标记(lesson/exam/meeting)
|
||||
- ✅ `RichTextEditor` 组件(富文本编辑器):基于 contentEditable 轻量实现,工具栏支持加粗/斜体/下划线/列表/标题/链接
|
||||
- ✅ `index.ts` 已更新导出 3 个新组件
|
||||
- ⏳ RichTextEditor 可在 P3+ 升级为 Tiptap 封装(需安装 @tiptap/core + @tiptap/react)
|
||||
|
||||
---
|
||||
|
||||
@@ -344,13 +378,13 @@
|
||||
|
||||
## 5. v2 已知问题(新增)
|
||||
|
||||
| 问题 | 影响 | 临时方案 |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------- | ----------------------------------- |
|
||||
| teacher-bff schema 文件未同步为 v2 扁平命名 | 契约不一致,teacher-portal 端无法基于 schema 生成类型 | 等待 ai03 同步 schema 文件 |
|
||||
| teacher-bff teacher 域 P3+ 查询仍抛 "not available in P2" | 56 个 GraphQL operations 无法切换真实数据 | 继续使用 MSW mock |
|
||||
| teacher-bff 5 个 gRPC target 留空走降级模式 B | teacher 域扩展查询返回空 | 等待 ai03 配置实际 gRPC 地址 |
|
||||
| teacher-portal useAuth 使用 localStorage JWT | XSS 风险(F12 裁决要求 httpOnly cookie) | 待 ai13 迁移(§3.2.2) |
|
||||
| teacher-portal WebSocket 使用 mock | 通知中心无法接收真实推送 | 待 ai13 接入 push-gateway(§3.2.3) |
|
||||
| 问题 | 影响 | 临时方案 |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------- |
|
||||
| teacher-bff schema 文件未同步为 v2 扁平命名 | 契约不一致,teacher-portal 端无法基于 schema 生成类型 | 等待 ai03 同步 schema 文件 |
|
||||
| teacher-bff teacher 域 P3+ 查询仍抛 "not available in P2" | 56 个 GraphQL operations 无法切换真实数据 | 继续使用 MSW mock |
|
||||
| teacher-bff 5 个 gRPC target 留空走降级模式 B | teacher 域扩展查询返回空 | 等待 ai03 配置实际 gRPC 地址 |
|
||||
| teacher-portal useAuth 使用 localStorage JWT | XSS 风险(F12 裁决要求 httpOnly cookie) | ⚠️ 阻塞于 iam 后端实现 Set-Cookie(§3.2.2) |
|
||||
| ~~teacher-portal WebSocket 使用 mock~~ | ~~通知中心无法接收真实推送~~ | ✅ 已接入 push-gateway(§3.2.3,环境变量 + token + Reconnect) |
|
||||
|
||||
---
|
||||
|
||||
@@ -358,39 +392,46 @@
|
||||
|
||||
### 6.1 已完成(v1 + v2)
|
||||
|
||||
| 工作项 | 完成度 |
|
||||
| ----------------------- | ---------------------------------------- |
|
||||
| P2 MF Shell + 基础页面 | ✅ 100% |
|
||||
| P3 考试/作业/成绩 | ✅ 100%(MSW mock) |
|
||||
| P4 知识图谱 + 学情分析 | ✅ 100%(MSW mock) |
|
||||
| P5 通知 + AI 助手 | ✅ 100%(MSW mock) |
|
||||
| P6 可观测性硬化 | ✅ 100% |
|
||||
| P7 参考项目差距闭环 | ✅ 100%(35 页面,MSW mock) |
|
||||
| Docker Desktop 验证 | ✅ 通过(standalone 模式) |
|
||||
| P1-A 单元测试 | ✅ 63/63 tests passed |
|
||||
| P1-B 设计令牌硬化 | ✅ ESLint tokens 零违规 |
|
||||
| P2-C i18n 集成 | ✅ 5 示范页面 + LocaleSwitcher |
|
||||
| P2-D ui-components 扩展 | ✅ 5/8 组件 |
|
||||
| P2-E 性能 + A11y | ✅ PerformanceDashboard + 5 核心页面修复 |
|
||||
| 工作项 | 完成度 |
|
||||
| ----------------------- | ------------------------------------------------------------------------- |
|
||||
| P2 MF Shell + 基础页面 | ✅ 100% |
|
||||
| P3 考试/作业/成绩 | ✅ 100%(MSW mock) |
|
||||
| P4 知识图谱 + 学情分析 | ✅ 100%(MSW mock) |
|
||||
| P5 通知 + AI 助手 | ✅ 100%(MSW mock) |
|
||||
| P6 可观测性硬化 | ✅ 100% |
|
||||
| P7 参考项目差距闭环 | ✅ 100%(35 页面,MSW mock) |
|
||||
| Docker Desktop 验证 | ✅ 通过(standalone 模式) |
|
||||
| P1-A 单元测试 | ✅ 87/87 tests passed(v1: 63 + v2: 24) |
|
||||
| P1-B 设计令牌硬化 | ✅ ESLint tokens 零违规 + 无 hsl/hex 字面量 |
|
||||
| P2-C i18n 集成 | ✅ 5/55 页面 + LocaleSwitcher + 26 模块 key |
|
||||
| P2-D ui-components 扩展 | ✅ 12/12 组件(v1: 9 + v2: Chart/Calendar/RichTextEditor) |
|
||||
| P2-E 性能 + A11y | ✅ PerformanceDashboard + 5 核心页面修复 + 5 页面懒加载 + size-limit 9 项 |
|
||||
| v2 push-gateway 接入 | ✅ WebSocket 环境变量 + token + Reconnect 协议 |
|
||||
|
||||
### 6.2 待完成(v2 新增)
|
||||
### 6.2 待完成(v2 剩余)
|
||||
|
||||
| 工作项 | 优先级 | 阻塞条件 |
|
||||
| ------------------------------ | ------ | ----------------------------------- |
|
||||
| MSW mock → 真实 API 切换 | P0 | teacher-bff teacher 域补全(12 项) |
|
||||
| useAuth 迁移到 httpOnly cookie | P1 | 无(iam 已就绪) |
|
||||
| push-gateway WebSocket 接入 | P1 | 无(push-gateway 已就绪) |
|
||||
| i18n 扩展(30+ 页面) | P2 | 无 |
|
||||
| 性能优化扩展 | P2 | 无 |
|
||||
| 单元测试扩展 | P2 | 无 |
|
||||
| 设计令牌完整迁移 | P2 | 无 |
|
||||
| ui-components 剩余组件 | P3 | 无 |
|
||||
| 工作项 | 优先级 | 阻塞条件 |
|
||||
| ------------------------------ | ------ | ------------------------------------------------ |
|
||||
| MSW mock → 真实 API 切换 | P0 | teacher-bff teacher 域补全(12 项) |
|
||||
| useAuth 迁移到 httpOnly cookie | P1 | ⚠️ 阻塞于 iam 后端实现 Set-Cookie(v2 核查修正) |
|
||||
| i18n 扩展(剩余 50+ 页面) | P2 | 无(工作量:逐页替换中文硬编码为 t() 调用) |
|
||||
| 图片优化(next/image) | P3 | 无(待配置 remotePatterns) |
|
||||
| 字体优化(next/font) | P3 | 无(待配置) |
|
||||
| CI size-limit 阻断集成 | P3 | 无(待 CI 配置) |
|
||||
|
||||
### 6.3 阻塞分析
|
||||
|
||||
**teacher-portal 当前唯一阻塞项**:teacher-bff teacher 域 P3+ 扩展查询/mutation 未实现(依赖 ai03 + ai07/08/11/10/12 联调)。
|
||||
**teacher-portal 当前阻塞项**:
|
||||
|
||||
**解除阻塞后**:可一次性完成 MSW mock → 真实 API 切换(§3.2.1),其余工作(§3.2.2-3.2.8)均不阻塞。
|
||||
1. **teacher-bff teacher 域 P3+ 扩展查询/mutation 未实现**(依赖 ai03 + ai07/08/11/10/12 联调)— 阻塞 MSW mock → 真实 API 切换
|
||||
2. **iam REST 登录接口未设置 Set-Cookie**(依赖 ai06 实现 httpOnly cookie)— 阻塞 useAuth 迁移
|
||||
|
||||
**解除阻塞后**:
|
||||
|
||||
- teacher-bff teacher 域补全 → 可一次性完成 MSW mock → 真实 API 切换(§3.2.1)
|
||||
- iam 实现 Set-Cookie → 可完成 useAuth httpOnly cookie 迁移(§3.2.2)
|
||||
|
||||
**无阻塞工作**:i18n 扩展剩余页面、图片/字体优化、CI 集成等均可在任意时间推进。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user