Files
gaokao-volunteer-system/SPRINT_3_S3_NEW_MODULES.md
Frontend Developer e8b8ad079e feat(sprint-2): V10 选项 B 完整切换 + G1 闸门通过
框架切换:Next.js 16 → Vite 5 + React 19 + React Router 7
状态管理:7 手写 hook → 4 Zustand slice
数据获取:直接 fetch → TanStack Query 5 (15 hooks)
表单:3-step 状态机 → RHF 7 + Zod
测试:0 → 25 单测 + 20 e2e (4 浏览器)
Bundle:192 KB gzip (目标 < 300 KB)
Lint:49 warning → 0 warning
Type:33 any → 0 any

S2-T-01 切 Vite 5 + React 19 框架
S2-T-02 Zustand 4 slice 替代 7 手写 hook
S2-T-03 TanStack Query 5 + 15 hooks
S2-T-04 Vitest + RTL + MSW + Chromatic 配置
S2-T-05 OpenAPI Codegen + Zod schema 复用
S2-T-06 RHF 7 + Zod 重写 FormCard
S2-T-07 Playwright 视觉基线 4 浏览器 5 spec
S2-T-08 Vite build + bundle < 300KB gzip

G1 闸门 (全部通过):
- typecheck: 0 error
- lint: 0 error 0 warning
- test (Vitest): 25/25 passed
- test:e2e (Playwright): 20/20 passed
- build (Vite): 326 modules 4.14s
- codegen:check: OpenAPI types 非占位

Sprint 2 收口报告:SPRINT_2_CLOSEOUT_2026-07-03.md
2026-07-03 15:51:03 +08:00

316 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sprint 3 任务拆解W4-5 · 12 人天 · 62 子任务)— V10 选项 B
> **技术栈**Vite 5 + React 19 + Zustand 4 + TanStack Query 5 + RHF 7 + Zod
> **目标**5 大 V2 新模块Share / Query / Review / LLM / Poster+ Share Link 统计 + Portal 集成
> **闸门**G2LLM fallback 4 模实测可切换 + 5 模块端到端跑通)
> **V10 变化**:从 22d 缩到 12d节省 10d = 8 hook 真实化用 TanStack Query 替代 + 5 模块统一模式)
> **PM 决策2026-07-03**整体重写为新实现Playwright 视觉回归 + Chromatic 验收
---
## ⚠️ V10 关键变化(与 V2 对比)
| 维度 | V2 | V10 选项 B |
|---|---|---|
| useChat 拆 6 子 hook | 1.5d | **0.5d**V10 已用 Zustand 替代S3 只需补 mock |
| 真实 sendMessage | 1.0d | **0.5d**TanStack Query mutation + Zod |
| useAudit + LLM 增强 | 1.5d | **0.75d**Zustand + mutation hook |
| 5 大新模块 | 14d | **8d**(统一 TanStack Query 模式) |
| 视觉回归 | 0d | **+1d**V10 Chromatic 验证) |
| 总估时 | 22d | **12d** |
---
## 0. Sprint 3 概览
| 任务 | 主任务 | 估时 | 子任务数 |
|---|---|---|---|
| T-B-12 | useChatOrchestratorV10 重写) | 0.5d | 3 |
| T-B-13 | 真实 sendMessage + typing | 0.5d | 3 |
| T-B-14 | 真实 getHistory + 滚动恢复 | 0.5d | 3 |
| T-B-15 | usePlan/useConsultation 真实化 | 0.75d | 4 |
| T-B-16 | useProfile Zustand 版 | 0.5d | 3 |
| T-B-17 | useAudit + LLM 增强4 模 fallback | 0.75d | 4 |
| T-B-22 | Share Link API4 端点) | 0.75d | 4 |
| T-B-23 | Data Query API3 端点) | 0.75d | 4 |
| T-B-24 | Review Flow API3 端点) | 0.75d | 4 |
| T-B-25 | Portal API2 端点) | 0.5d | 3 |
| T-B-26 | LLM Audit Enhance API | 0.75d | 4 |
| T-B-27 | Poster Generate API | 0.5d | 3 |
| T-B-28 | Share Link 统计 + 分享 UI | 1.0d | 5 |
| T-B-29 | Chromatic 视觉回归S3 增量) | 0.5d | 3 |
| T-B-30 | ESLint 0 warning 守门V10 重写) | 0.5d | 3 |
| **合计** | **15 任务** | **9.5d + 缓冲 2.5d = 12d** | **56 + 6 整合 = 62** |
---
## T-B-12 · useChatOrchestratorV10 重写 · 0.5d · 3 子任务)⚠️ V10 简化
> **V10 收益**Sprint 2 已建立 Zustand store + TanStack QueryS3 只需组装编排层
### ST-S3-B-12.1 写 chat-fixtures0.25d
- **产出**`apps/web/__mocks__/chat-fixtures.ts`
- **3 套 mock**:广东物理 620 / 广东历史 580 / 河北物理 600
### ST-S3-B-12.2 写 `useChatOrchestrator`0.125d
- **产出**`apps/web/src/hooks/useChatOrchestrator.ts`
- **职责**Zustand messages + TanStack Query mutation + Typing 状态机
- **V10 收益**:从 1.5dV2 拆 6 子 hook→ 0.125d
### ST-S3-B-12.3 接入 8 个 UI 组件0.125d
- **覆盖**MessageList / InputBar / TypingIndicator / PlanCard / ModeIndicator / 等
---
## T-B-13 · 真实 sendMessage + typing0.5d · 3 子任务)
### ST-S3-B-13.1 写 `useChatSendMutation`0.25d
- **封装**`POST /api/chat/send` + SSE 流式响应
- **类型**`z.infer<typeof ChatSendSchema>`
### ST-S3-B-13.2 写 `useTypingState` Zustand selector0.125d
- **三态**thinking旋转/ paused静止/ stopped隐藏
### ST-S3-B-13.3 接入 InputBar 真实点击发送0.125d
- **验收**
- [ ] 点击发送 → 流式输出 → typing 状态切换正确
- [ ] `any` 数量 0
---
## T-B-14 · 真实 getHistory + 滚动恢复0.5d · 3 子任务)
### ST-S3-B-14.1 写 `useChatHistoryQuery`0.125d
- **封装**`GET /api/chat/history?sessionId=xxx`
- **缓存策略**`staleTime: 5min` + `gcTime: 30min`
### ST-S3-B-14.2 写滚动恢复 hook0.25d
- **产出**`useScrollRecovery(ref, [messages.length])`
- **行为**sessionId 变化时滚到底部;用户上滑时不打断
- **V10 不变量**UI 1:1 复现V2 Plan §3.1 L1
### ST-S3-B-14.3 接入 ChatPanel 组件0.125d
---
## T-B-15 · usePlan/useConsultation 真实化0.75d · 4 子任务)
### ST-S3-B-15.1 写 `usePlanQuery`0.125d
### ST-S3-B-15.2 写 `usePlanCreateMutation`0.25d
### ST-S3-B-15.3 写 `useConsultationQuery` + `useConsultationMutation`0.25d
### ST-S3-B-15.4 接入 PlanCard 真实数据0.125d
- **V10 不变量 C1**PlanCard 3-Tab 切换不重渲染父组件
---
## T-B-16 · useProfile Zustand 版0.5d · 3 子任务)⚠️ V10 简化
### ST-S3-B-16.1 写 `useUserStore` Zustand0.25d
- **V10 收益**:从 1.0dV2 真实 useProfile→ 0.25d
### ST-S3-B-16.2 接入 Header 头像/菜单0.125d
### ST-S3-B-16.3 接入 Settings 页面0.125d
---
## T-B-17 · useAudit + LLM 增强4 模 fallback · 0.75d · 4 子任务)
> **G2 闸门核心**LLM 4 模claude / gpt / gemini / deepseek实测可切换
### ST-S3-B-17.1 写 LLM provider 适配器0.25d
- **产出**`apps/web/src/lib/llm/provider.ts`
- **4 provider 类型**`ClaudeProvider` / `GPTProvider` / `GeminiProvider` / `DeepseekProvider`
- **接口**`enhance(input: AuditInput): Promise<AuditEnhancement>`
### ST-S3-B-17.2 写 `useAuditEnhanceMutation`0.25d
- **封装**`POST /api/llm/audit-enhance` + provider 选择
- **降级链**claude → gpt → gemini → deepseek
### ST-S3-B-17.3 写 `useLLMConfig` Zustand store0.125d
- **状态**currentProvider / fallbackOrder / availableProviders
### ST-S3-B-17.4 4 模实测切换0.125d
- **验收G2**
- [ ] claude 失败 → 自动降级 gpt
- [ ] gpt 失败 → 自动降级 gemini
- [ ] 全部失败 → 走 deepseek
- [ ] Playwright e2e 覆盖
---
## T-B-22 · Share Link API4 端点 · 0.75d · 4 子任务)
### ST-S3-B-22.1 写 `useShareLinkCreate`0.25d
- **封装**`POST /api/share-link`
- **Schema**`{ planId, expiresIn: 7|30|forever }`
### ST-S3-B-22.2 写 `useShareLinkDelete`0.125d
- **封装**`DELETE /api/share-link/{id}`
### ST-S3-B-22.3 写 `useShareLinkLatestQuery`0.125d
- **封装**`GET /api/portal/share-link/latest`
### ST-S3-B-22.4 写 `useShareLinkStatsQuery`0.25d
- **封装**`GET /api/share-link/{code}/stats`
- **响应**`{ views, uniqueVisitors, lastAccessedAt }`
---
## T-B-23 · Data Query API3 端点 · 0.75d · 4 子任务)
### ST-S3-B-23.1 写 `useScoreLineQuery`0.25d
- **封装**`GET /api/data-query/score-line`
- **参数**`{ province, year, scoreType }`
### ST-S3-B-23.2 写 `useRankEstimatorQuery`0.25d
- **封装**`GET /api/data-query/rank-estimator`
- **V10 变化**TanStack Query `select` 直接转换位次→等效分数
### ST-S3-B-23.3 写 `useMajorsQuery` + `useSchoolsQuery`0.25d
- **封装**`GET /api/data-query/majors` + `GET /api/data-query/schools`
---
## T-B-24 · Review Flow API3 端点 · 0.75d · 4 子任务)
### ST-S3-B-24.1 写 `useReviewStartMutation`0.25d
- **封装**`POST /api/review/start`
### ST-S3-B-24.2 写 `useReviewStatusQuery`0.125d
- **封装**`GET /api/review/{id}/status`
- **轮询**`refetchInterval: 3000`(审核中)
### ST-S3-B-24.3 写 `useReviewActionMutation`0.25d
- **封装**`POST /api/review/action`approve / reject / request_changes
- **V10 变化**Zod discriminated union 处理 3 种 action
### ST-S3-B-24.4 接入 ReviewPanel UI0.125d
---
## T-B-25 · Portal API2 端点 · 0.5d · 3 子任务)
### ST-S3-B-25.1 写 `usePortalCWBQuery`0.25d
- **封装**`GET /api/portal/{token}/cwb`(位次查询)
### ST-S3-B-25.2 写 `usePortalFullPlanQuery`0.125d
- **封装**`GET /api/portal/{token}/full-plan`
### ST-S3-B-25.3 写 Portal 入口页面0.125d
- **UI/交互 1:1**:与原型招生门户入口一致
---
## T-B-26 · LLM Audit Enhance API0.75d · 4 子任务)
### ST-S3-B-26.1 写 `useAuditEnhanceMutation`0.25d
- **封装**`POST /api/llm/audit-enhance`
### ST-S3-B-26.2 写流式响应处理0.25d
- **V10 关键技术**SSE + ReadableStream + Zustand 增量更新
- **复用**`useChatSendMutation` 的流式逻辑
### ST-S3-B-26.3 写 LLM 结果展示组件0.125d
### ST-S3-B-26.4 接入 AuditPanel UI0.125d
---
## T-B-27 · Poster Generate API0.5d · 3 子任务)
### ST-S3-B-27.1 写 `usePosterGenerateMutation`0.25d
- **封装**`POST /api/poster/generate`
- **响应**`{ posterUrl, qrCode, expiresAt }`
### ST-S3-B-27.2 写海报预览组件0.125d
- **V10 视觉**:海报需与原型的视觉风格一致
### ST-S3-B-27.3 接入 ShareDialog UI0.125d
---
## T-B-28 · Share Link 统计 + 分享 UI1.0d · 5 子任务)⚠️ V10 加强
> **V10 加强**5 个子任务覆盖 UI + 统计图表 + 移动端适配
### ST-S3-B-28.1 写 ShareDialog 组件0.25d
- **UI/交互 1:1**:与原型分享弹窗一致
- **V10 不变量**:移动端 48px 触发区
### ST-S3-B-28.2 写 QR Code 展示组件0.125d
- **依赖**`qrcode.react`
### ST-S3-B-28.3 写 Stats 统计卡片0.25d
- **3 卡片**:访问数 / 独立访客 / 最近访问
- **图标**lucide-react
### ST-S3-B-28.4 写访问趋势 mini chart0.25d
- **依赖**`recharts`
- **V10 收益**Sprint 5 chart 任务的预演
### ST-S3-B-28.5 写 e2eshare-link.spec0.125d
---
## T-B-29 · Chromatic 视觉回归S3 增量 · 0.5d · 3 子任务)⚠️ V10 新增
### ST-S3-B-29.1 截图新增 5 模块页面0.25d
- **页面**ShareDialog / Stats / DataQuery / ReviewPanel / Poster Preview
### ST-S3-B-29.2 提交 Chromatic 增量基线0.125d
### ST-S3-B-29.3 Chromatic diff 阈值告警0.125d
- **CI**`pnpm chromatic --exit-zero-on-changes`
---
## T-B-30 · ESLint 0 warning 守门V10 重写 · 0.5d · 3 子任务)
> **V10 关键**S3 完成后 ESLint 必须 0 warningV2 是 49 warning 缓慢修)
### ST-S3-B-30.1 清理 S3 引入的 any0.25d
- **验收**`grep -r "any" apps/web/src/` 0 结果
### ST-S3-B-30.2 清理 S3 引入的 unused vars0.125d
### ST-S3-B-30.3 升级 Sprint 1 临时 warn 规则为 error0.125d
- **规则**`@typescript-eslint/no-explicit-any` / `react-hooks/set-state-in-effect`
- **目的**:阻止 Sprint 4-8 倒退
---
## V10 验收清单Sprint 3 G2 闸门)
```
G2 闸门(必须全部通过):
[ ] 5 大新模块端到端跑通Share / Query / Review / LLM / Poster
[ ] LLM 4 模实测可切换 + 自动降级
[ ] pnpm typecheck 0 error 0 any
[ ] pnpm lint 0 error 0 warning
[ ] pnpm test 100% 覆盖新增 56 个子任务相关组件
[ ] pnpm test:e2e 5 spec 全绿
[ ] pnpm build bundle < 320KB gzip+20KB for new modules
[ ] pnpm chromatic 视觉基线 13 页全提交
[ ] 原型 7 个手写 hook + 1 个 lib/ 全部删除
[ ] ESLint 0 warningV2 是 49 warning 修 1 整轮)
```
---
## V10 风险Sprint 3 特有)
| ID | 风险 | 等级 | 缓解 |
|---|---|---|---|
| V10-S3-R1 | LLM 4 模 provider 适配成本高 | 中 | 抽象统一接口;先实现 1 模,再快速复制 3 模 |
| V10-S3-R2 | SSE 流式响应与 TanStack Query 兼容 | 中 | 用 `onMutate` + Zustand 增量更新,绕过 React Query 的缓存机制 |
| V10-S3-R3 | 5 新模块并发开发协调 | 中 | 串行实施T-B-22 → T-B-23 → T-B-24 → T-B-26 → T-B-27 |
| V10-S3-R4 | Chromatic 商业服务配额 | 低 | Sprint 2 已用 1 次S3 再用 1 次,剩余 8 次给 S4-S8 |
---
**Sprint 3 文档状态**V10 选项 B 重写完成
**总工时**12 人天V2 是 22 人天,节省 10d
**总子任务**62 个V2 是 70 个)
**下一步**Sprint 2 完成后立即启动