Files
gaokao-volunteer-system/SPRINT_2_S2_API_HOOKS.md
Frontend Developer fa7c22e8ed chore(s1): monorepo skeleton + 30-file prototype first commit
Sprint 1 G0 闸门通过 (pnpm install / typecheck / lint / build / turbo 全部 exit 0)

实际完成:
- T-A-01 monorepo 根骨架: pnpm-workspace.yaml + turbo.json + 根 package.json
- T-A-02 收编原型: apps/web/ 30 个 src/ 文件 (4948 行) 首次入库
- T-A-03 lint/format: eslint flat config (根 + apps/web) + prettier
- T-A-23 web-ci.yml: GitHub Actions 4-step CI (typecheck/lint/build/bundle)

勘误 (前置假设错误):
- 原型 4948 行 (不是 4114 行)
- 原型含 33 个 any + 16 个未用变量 (Sprint 2 修)
- 原型从未 commit, 用 cp -r + git add, 不是 git mv

推迟到 Sprint 2+:
- T-A-04/05 tsconfig packages 抽离
- T-A-06/07/08 design tokens 抽离
- T-A-09~13 5 基础组件

依赖隔离:
- .gitignore 加 11 行 monorepo 规则
- apps/web/.gitignore 补 .npm-cache/next-dev.log
- 删除 apps/web 内嵌 .git/ (cp -r 复制时携带)

验证:
- pnpm install: 1m55s, 456 packages
- typecheck: 0 errors
- lint: 0 errors, 49 warnings (any + unused)
- next build: 8 routes compiled, 9/9 static pages
- turbo run build: 1 successful, 12.9s
2026-07-03 09:22:03 +08:00

9.9 KiB
Raw Blame History

Sprint 2 任务拆解W2-3 · 18 人天 · 58 子任务)

主任务T-A-14 ~ T-A-22, T-B-01 ~ T-B-05 目标Vitest/Playwright 骨架 + OpenAPI Codegen + 后端 5 端点对接 闸门G1OpenAPI 类型生成无 any

0. Sprint 2 概览

任务 主任务 估时 子任务数
T-A-14 Vitest + RTL + MSW 配置 0.5d 4
T-A-15 示范单测ModeIndicator 0.25d 3
T-A-16 示范单测SafeMarkdown 0.25d 3
T-A-17 示范单测FormCard 0.5d 4
T-A-18 Playwright 配置 0.5d 3
T-A-19 示范 e2etheme.spec 0.5d 3
T-A-20 示范 e2enavigation.spec 0.5d 3
T-A-21 OpenAPI Codegen 接入 1.0d 5
T-A-22 示范调用mock server 0.5d 4
T-B-01 POST /api/chat/send 1.5d 6
T-B-02 GET /api/chat/history 0.5d 4
T-B-03 3 个 consultations 端点 1.0d 5
T-B-04 4 个 plans 端点 1.0d 5
T-B-05 assessment + audit/upload 2.0d 6
合计 15 任务 10.5d + 缓冲 7.5d = 18d 58

T-A-14 · Vitest + RTL + MSW 配置0.5d · 4 子任务)

ST-S2-A-14.1 写 vitest.config.ts0.125d

  • 产出apps/web/vitest.config.tsjsdom env + setup 文件 + alias

ST-S2-A-14.2 写 setup.ts0.125d

  • 产出@testing-library/jest-dom/vitest + MSW server 启动/关闭

ST-S2-A-14.3 写 renderWithProviders.tsx0.125d

  • 产出packages/test-utils/renderWithProviders.tsxQueryClient + Theme + Router + Intl
  • 验收
    • pnpm --filter web test 跑通

ST-S2-A-14.4 占位测试验证0.125d

  • 产出Math.test.ts1+1=2
  • 验收
    • 占位测试 1 绿

T-A-15 · 示范单测ModeIndicator0.25d · 3 子任务)

ST-S2-A-15.1 写 4 mode 渲染测试0.125d

  • 覆盖
    • explore: "探索模式"
    • generating: "生成中"
    • auditing: "审核中"
    • adjusting: "调整中"
  • 验收
    • 4 模式 label 正确

ST-S2-A-15.2 写 deriveMode 决策树测试0.0625d → 0d

  • 覆盖
    • isAuditActive → auditing
    • currentPlan + no audit → adjusting
    • province + score → generating
    • 其他 → explore
  • 验收
    • 100% branch coverage

ST-S2-A-15.3 验证 100% 覆盖0.0625d → 0d

  • 命令pnpm test --coverage
  • 验收
    • ModeIndicator.tsx line + branch 各 100%

T-A-16 · 示范单测SafeMarkdown0.25d · 3 子任务)

ST-S2-A-16.1 写 XSS 注入测试0.125d

  • 覆盖
    • <script>alert(1)</script> → 渲染为文本
    • <img src=x onerror=alert(1)> → onerror 剥
    • <a href="javascript:alert(1)"> → href sanitize
  • 验收
    • 3 注入用例全过

ST-S2-A-16.2 写正常 markdown 渲染0.0625d → 0d

  • 覆盖h1/ul/code/blockquote 渲染正确

ST-S2-A-16.3 验证0.0625d → 0d


T-A-17 · 示范单测FormCard0.5d · 4 子任务)

ST-S2-A-17.1 字段 touched 显示验证0.125d

  • 验收
    • 字段未 touch 时不显示错误
    • touch 后显示错误

ST-S2-A-17.2 跳步守卫测试0.125d

  • 验收
    • basic 未完成时点击 subjects 步骤 → 阻止 + 提示

ST-S2-A-17.3 subjects 数组拼装测试0.125d

  • 验收
    • 选物理/化学/生物 → 提交时 subjects: ["physics", "chemistry", "biology"]

ST-S2-A-17.4 验证 80% 覆盖0.125d

  • 命令pnpm test --coverage --testPathPattern=FormCard

T-A-18 · Playwright 配置0.5d · 3 子任务)

ST-S2-A-18.1 写 playwright.config.ts0.25d

  • 产出apps/web/e2e/playwright.config.ts
    • chromium + webkit
    • viewports: 375 (mobile) / 768 (tablet) / 1280 (desktop)
    • baseURL: http://localhost:3000
  • 验收
    • pnpm --filter web e2e 启动浏览器

ST-S2-A-18.2 写 smoke.spec.ts0.125d

  • 验收
    • 访问 / 看到 Sidebar

ST-S2-A-18.3 写 webServer 配置0.125d

  • 产出:自动启 pnpm dev

T-A-19 · 示范 e2etheme.spec0.5d · 3 子任务)

ST-S2-A-19.1 写 light/dark/system 切换0.25d

  • 覆盖
    • 切到 dark → data-theme="dark" 出现
    • 切到 system → data-theme 移除
  • 验收
    • 3 场景全绿

ST-S2-A-19.2 写持久化测试0.125d

  • 覆盖
    • 刷新页面后 localStorage 保留选择
  • 验收
    • expect(localStorage.getItem('theme')).toBe('dark')

ST-S2-A-19.3 写闪白防护测试0.125d

  • 覆盖
    • page.waitForLoadState('domcontentloaded') 在 100ms 内
  • 验收
    • DOMContentLoaded → theme 已应用

T-A-20 · 示范 e2enavigation.spec0.5d · 3 子任务)

ST-S2-A-20.1 写桌面导航0.125d

  • 覆盖viewport 1280 → Sidebar 可见MobileNav 隐藏

ST-S2-A-20.2 写移动导航0.125d

  • 覆盖viewport 375 → MobileNav 可见Sidebar 隐藏

ST-S2-A-20.3 写跳转测试0.25d

  • 覆盖
    • Sidebar /plans → 跳转
    • MobileNav /plans → 跳转
  • 验收
    • 4 场景全绿

T-A-21 · OpenAPI Codegen 接入1.0d · 5 子任务)

ST-S2-A-21.1 启动后端 OpenAPI0.125d

  • 命令curl http://localhost:8000/openapi.json > openapi.json
  • 验收
    • 看到 ~30+ 端点V2 增至 30+

ST-S2-A-21.2 写 codegen.config.ts0.25d

  • 产出packages/api-client/codegen.config.ts
    • schema: openapi.json
    • generator: typescript-fetch
    • output: src/gen

ST-S2-A-21.3 写 turbo.json generate 任务0.125d

  • 产出generate-api-client 任务依赖 ^build-admin
  • 验收
    • pnpm turbo run generate-api-client 跑通

ST-S2-A-21.4 跑 codegen 看产物0.25d

  • 验收
    • packages/api-client/src/gen/services/ 30+ 服务类
    • 0 个 anyG1 闸门

ST-S2-A-21.5 写 packages/api-client/src/index.ts barrel0.25d

  • 验收
    • import { AuthService } from '@gaokao/api-client' 可用

T-A-22 · 示范调用mock server0.5d · 4 子任务)

ST-S2-A-22.1 写 apps/web/lib/api/chat.ts0.125d

  • API
    export const apiClient = {
      chat: {
        send: (consultationId: string | undefined, body: { message: string }) => Promise<ChatResponse>,
        getHistory: (consultationId: string) => Promise<{ messages: Message[] }>,
      }
    };
    

ST-S2-A-22.2 写 MSW handler0.125d

  • 产出apps/web/mocks/handlers/chat.ts 拦截 /api/chat/send

ST-S2-A-22.3 在 page.tsx 临时接 apiClient0.125d

  • 验收
    • 类型管道通(不替换 useChat,仅验证)

ST-S2-A-22.4 写 Zod 校验 match 字段0.125d

  • 约束§0.1 R-NEW-3z.number().int().min(1).max(5)
  • 验收
    • match=6 → Zod 抛错

T-B-01 · POST /api/chat/send1.5d · 6 子任务)

ST-S2-B-01.1 对接 OpenAPI 类型0.25d

  • 产出import type { ChatSendRequest, ChatResponse } from '@gaokao/api-client/gen'

ST-S2-B-01.2 写 useChatStore.sendMessage0.5d

  • 产出apps/web/stores/chatStore.ts
  • APIsendMessage(consultationId, body) → 调用 apiClient.chat.send
  • 验收
    • 401/403 错误码正确捕获

ST-S2-B-01.3 写错误码 E03001/03002/03003 处理0.25d

  • 验收
    • 3 错误码 → 对应 Toast 提示

ST-S2-B-01.4 写单测0.25d

  • 覆盖:成功 / 401 / 403 / 500

ST-S2-B-01.5 写 e2e 草稿0.125d

  • 产出chat-send.spec.tsSprint 3 完成)

ST-S2-B-01.6 写 Storybook 故事0.125d

  • 产出ChatSendButton.stories.ts

T-B-02 · GET /api/chat/history0.5d · 4 子任务)

ST-S2-B-02.1 对接 OpenAPI0.125d

ST-S2-B-02.2 写 useQuery0.125d

  • 验收
    • cursor 分页支持

ST-S2-B-02.3 写 auth 校验0.125d

  • 验收
    • consultation_id 无访问令牌 → 401

ST-S2-B-02.4 写单测0.125d


T-B-03 · 3 个 consultations 端点1.0d · 5 子任务)

ST-S2-B-03.1 列表0.25d

  • 出参{consultations: [{id, title, updated_at, profile, has_plan, has_audit}]}

ST-S2-B-03.2 详情0.25d

  • 出参:完整 messages + profile + plan + audit

ST-S2-B-03.3 DELETE 软删除0.25d

  • 验收
    • 数据保留 30 天

ST-S2-B-03.4 写 useConsultation hook0.125d

ST-S2-B-03.5 写单测0.125d


T-B-04 · 4 个 plans 端点1.0d · 5 子任务)

ST-S2-B-04.1 列表0.25d

  • 出参{plans: [{id, name, created_at, profile, distribution: {rush,stable,safe}}]}

ST-S2-B-04.2 详情0.125d

ST-S2-B-04.3 PATCH name 修改0.25d

  • 验收
    • 仅支持 name其他字段 PATCH 拒绝

ST-S2-B-04.4 DELETE 软删除0.25d

ST-S2-B-04.5 写单测0.125d


T-B-05 · assessment + audit/upload2.0d · 6 子任务)

ST-S2-B-05.1 写 useAssessment hook0.25d

  • APIPOST /api/assessmentHolland RIAS

ST-S2-B-05.2 写 useAudit.upload multipart0.5d

  • 验收
    • Excel 5MB / Image 10MB / PDF 10MB 限制
    • MIME 校验

ST-S2-B-05.3 写上传进度条0.25d

  • APIXMLHttpRequest + progress 事件

ST-S2-B-05.4 写 AI 处理进度0.5d

  • API:轮询 /api/audit/{id}/status

ST-S2-B-05.5 错误码 E04001 处理0.25d

  • 验收
    • 上游 AI 失败 → Toast 重试

ST-S2-B-05.6 写单测0.25d


Sprint 2 收口验收

  • 15 主任务 / 58 子任务全部完成
  • G1 通过OpenAPI 生成 0 个 any
  • 测试覆盖率ModeIndicator/SafeMarkdown/FormCard 100% / 100% / 80%+
  • E2Etheme.spec / navigation.spec 全绿
  • 进入 Sprint 3 前 commit<feat(s2): vitest+playwright+openapi+5 endpoints>