Files
2026-06-16 00:55:20 +08:00

411 lines
40 KiB
Markdown
Raw Permalink 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.
# 2-TASK · 中国机车图鉴平台 开发任务清单
> 版本:v1.0
> 来源:基于 `1-prd.md` 细化
> 用途:落实与跟进开发计划(开发 / 单元测试 / e2e 测试)
## 进度日志
- **2025 · 第 1 批**:完成 Phase 1A 数据底座(T-1.1 / T-1.2)。
- ETL 导入 12 张 CSV → SQLite + JSON + 报告:**Model 540 / Unit 307**,跳过 10(空行),0 待复核。
- 31 个单元/集成测试全部通过(含全量导入幂等性)。
- 代码位于 `app/etl``app/tests`,产物位于 `app/data`,说明见 `app/README.md`
- 取舍:开发期用 SQLiteschema 设计为可移植到 PostgreSQL);Meilisearch/Redis/对象存储等基础设施待 Phase 1B/接入时引入。
- **2025 · 第 2 批**:完成 Phase 1B 后端 APIT-1.3 / T-1.4),技术栈 **NestJS + TypeScript**(方案 B)。
- 接口:`/api/health``/api/categories``/api/models`(列表/筛选/排序/分页)、`/api/models/:id`(详情)、`/api/units``/api/search`
- 参数化查询防注入;全局 ValidationPipe 校验非法参数(返回 400)。
- 测试:14 个单元测试 + 9 个 e2esupertest)全部通过;build 通过;真实库(540/307)联调通过。
- 代码位于 `apps/api`NestJS 标准结构),读取 `app/data/machines.db`
- 取舍:搜索先用 SQLite LIKE+排序实现(规模足够),Meilisearch 延后。
- **2025 · 第 3 批**:完成 Phase 1C 前端(T-1.5 / T-1.6 / T-1.7),技术栈 **Vite + React + TS**(方案 B,独立前端 `apps/web`)。
- 页面:列表页(筛选/排序/分页 + 列表/时间轴/图鉴 三视图切换)、详情页(参数表 + raw_json 保真表)、全局搜索框(防抖下拉)。
- 时间轴:分类泳道 + 年代横轴刻度 + 节点跳转;图鉴:卡牌墙 + 已收集/未收集占位。
- 测试:23 个单元测试(Vitest + Testing Library)全部通过;`npm run build`tsc + vite)通过。
- 全栈联调:Vite 代理 `/api`→NestJS,真实库 540 车型,列表/排序/搜索均正确返回。
- 取舍:SSR/SEO、缩放平移、Playwright 脚本化 e2e 标记延后(已手动联调全栈通过)。
- **2025 · 第 4 批(A:闭合 MVP**:补齐 Phase 1 验收(T-1.8)。
- 性能基准 `apps/api/bench.mjs`list/filter/detail p95 ≤ 3ms(阈值 1500ms),search p95 1.3ms(阈值 500ms),全部达标。
- Playwright e2e`apps/web/e2e/smoke.spec.ts`):首页/筛选→详情/时间轴/图鉴/搜索 **5 项全通过**webServer 自动拉起前后端。
- 修复真实 UX bug:时间轴同年同泳道节点完全重叠导致不可点击 → 实现碰撞分行(错位堆叠)+ 标签 pointer-events:none。
- **Phase 1MVP)正式闭合**。累计测试:ETL 31 + API 23 + 前端单元 25 + e2e 5 = **84 个测试全绿**
- **2025 · 第 5 批(图鉴差异化)**:解决"列表与图鉴雷同"问题,落实方案"收集欲"主线。
- 图鉴改为**图片优先卡牌墙**:按分类生成 SVG 占位缩略图(`lib/thumb.ts`,真实车照待 Phase 2/3 众包上传替换)。
- 轻量**收集功能**localStorage 记录已收集(`lib/useCollection.ts`),点卡片"收集"点亮缩略图,顶部显示收集进度条;Phase 3 账户上线后迁移云端。
- 测试:前端单元增至 34(新增 thumb / GalleryCard),e2e 图鉴用例改为验证缩略图+收集交互;全绿。
- **2025 · 第 6 批(前端全方位重构 · 主线串联)**:解决"首页死板、一屏过载、无主线"问题。
- **叙事首页**`HomePage`):英雄区 + 关键统计 + **时速演进曲线**`EvolutionCurve`SVG+ **四个时代章节**(蒸汽→内燃→电力→高铁,每节代表车 + 进入探索),把技术演进主线显性串联。
- **信息架构重组**`/` 故事首页、`/explore` 探索页、`/models/:id` 详情;顶部导航 故事/探索 + 全局搜索。
- **探索页减负**:筛选器收进可折叠面板(默认隐藏,带激活计数),默认图鉴视图,更大留白。
- 章节链接经 `?category=` 跳入探索并自动筛选(URL 同步)。
- 主线逻辑(时代/代表车/时速演进)抽到 `lib/eras.ts` 纯函数并单测。
- 测试:前端单元 39 + e2e 7(覆盖首页→探索→详情/时间轴/搜索/章节跳转),全绿;build 通过。
- **2025 · 第 7 批(专业图标 + 视图合并 + 全详情)**:
- **全部 emoji → 专业图标**:引入 `react-icons`,统一 `components/icons.tsx`(分类图标 + 品牌/锁/星/筛选/视图图标);缩略图改为 `Thumb`(分类渐变 + 专业图标),移除 emoji/data-URI 方案。
- **探索页合并视图**:去掉"列表",统一用**图鉴**展示(默认),仅保留 图鉴 / 时间轴 两视图。
- **详情页升级为"全部详情"**:缩略图 Hero + 标签 + 基本信息 / 尺寸与重量 / 动力与性能 / 原始数据 全字段分组展示。
- 测试:前端单元 37 + e2e 7(图鉴收集改 aria-pressed、详情断言分组标题),全绿;build 通过。
- **2025 · 第 8 批(图鉴分页 + 全中文 + 多图灯箱)**:
- **图鉴分页**:客户端分页 18/页,分页器置于卡牌墙**上方**;筛选变化回到第 1 页;编号跨页连续。
- **详情全中文**:新增 `lib/fieldLabels.ts`(英文规范键→中文),"原始数据"区英文键全部映射为中文(model 17 等已核对全覆盖)。
- **详情多图 + 灯箱**`lib/useImages.ts`(每车型本地图册,localStorage+ `Lightbox`(缩放/滚轮/拖拽平移/上下张/Esc);详情新增"图册"区可添加多张图片并点开缩放欣赏;云端众包上传待 Phase 2/3。
- 测试:前端单元 39 + e2e 8(新增分页/全中文/图册上传缩放),全绿;build 通过。
- **2025 · 第 9 批(接入 Wikimedia Commons 真实照片)**
- 每页改为 **21**;详情"图册"接入 **Wikimedia Commons**`lib/commons.ts` + `useCommonsImages`):浏览器端 origin=* CORS 检索自由授权实拍图,**署名(作者/许可证/来源链接)显示在灯箱**。
- 图片优先级:Commons 真实照片 → 本地上传 → 系统生成示意图(兜底,永不空);卡片角标区分来源(Commons/我的/示意图)。
- 健壮回退:无网/无匹配/请求失败时静默回退示意图;结果做内存 + sessionStorage 缓存。
- 灯箱升级为通用图源(URL/dataURL)并展示署名。
- 注:构建/测试沙箱仅放行 npm,无法联网验证 Commons 覆盖率,需在浏览器端验证(如 CR400AF / HXD1型)。
- 测试:前端单元 42 + e2e 8,全绿;build 通过。
- **2025 · 第 10 批(Phase 2 启动 · T-2.1 账户体系,采用 B1)**
- 架构:新增**独立可写库 `app/data/app.db`**`WritableDbService`,含迁移),与只读 `machines.db` 分离,避免被 ETL 重建清空。
- 后端:`AuthModule`NestJS + @nestjs/jwt + bcryptjs)——注册/登录/`/api/auth/me``JwtAuthGuard` + `RolesGuard`(游客<注册<信任<版主<管理员 等级);密码 bcrypt,邮箱规范化小写,密码不外泄。
- 前端:`AuthProvider` 上下文 + 登录/注册模态框 + 顶栏用户菜单 + 个人主页(贡献积分/已收集/加入日期)。
- 安全提示:JWT secret 走 `JWT_SECRET` 环境变量,默认值仅供开发,生产须替换。
- 测试:后端单元 18 + e2e 14(含注册/重复 409/非法 400/登录 401/me 鉴权);前端单元 42 + e2e 9(含注册→用户菜单→个人主页);build 通过。
- **2025 · 第 11 批(T-2.2 编辑/修订 + T-2.3 审核)**
- 数据:`app.db``revisions` / `revision_changes` / `model_overrides` 三表;编辑不改只读 `machines.db`,而以"字段覆盖"叠加,详情接口读取时合并(标记 `overridden`)。
- 后端:`ContribModule`——`POST /api/models/:id/revisions`(提交编辑,含 old→new diff)、`GET .../revisions`(历史)、`GET /api/revisions/pending`(版主队列)、`approve`/`reject`(版主+)、`GET /api/editable-fields`;字段白名单 + 校验(年代范围/状态枚举/国别枚举);信任级以上自动通过;通过即应用覆盖并给作者 +5 积分。
- 引导管理员:`ADMIN_EMAIL` 环境变量命中即注册为 admin(便于审核演示/测试)。
- 前端:详情页"编辑词条"模态(白名单字段表单)、"修订历史"区(状态/作者/diff)、`/review` 审核页(版主+,通过/驳回)、顶栏"审核"入口(按角色显示)。
- 测试:后端单元 18 + e2e 21(提交/历史/403/管理员审核生效/自动通过/非法 400);前端单元 42 + e2e 10(含登录后编辑→待审核修订);两端 build 通过。
- **2025 · 第 12 批(T-2.4 荣誉与激励)→ Phase 2 完成**:
- 等级头衔(`community/levels.ts` 纯函数):见习巡道员→巡道员→司炉→副司机→司机→机务段长→总工程师(按积分阈值);徽章(首改采纳/高产编辑/资深贡献/总工殊荣)。
- `/api/auth/me` 增等级/徽章/被采纳数;`CommunityModule``GET /api/leaderboard`(贡献榜),`GET/POST/DELETE /api/models/:id/maintainers`(词条认领署名)。
- 前端:个人主页等级进度条 + 徽章;`/leaderboard` 贡献榜页(金银铜);详情页"词条维护者"认领/署名;顶栏"贡献榜"入口。
- 测试:后端单元 22 + e2e 25(含 me 等级、贡献榜、认领、未登录 401);前端单元 42 + e2e 13(含认领署名、等级徽章、贡献榜);两端 build 通过。
- **Phase 2(众包共建)完成**:账户 + Wiki 式编辑/审核/修订历史 + 荣誉体系全部跑通。
- **2025 · 第 13 批(测试账号一键登录)**:后端开机幂等预置 3 个演示账号(admin@demo.com / mod@demo.com / user@demo.com,密码 demo1234,角色 admin/moderator/user;仅非生产,`SEED_TEST_USERS=false` 可关);登录框加"测试账号一键填入"。e2e 15。
- **2025 · 第 14 批(Phase 3 启动 · T-3.1 论坛 + T-3.2 词条讨论)**
- 可写库增 `threads` / `thread_replies` 表(板块 + 可选 model_id 绑定词条)。
- 后端 `ForumModule``GET /api/boards``GET/POST /api/threads`(板块/词条过滤、发帖)、`GET /api/threads/:id``POST /api/threads/:id/replies`DTO 校验,发帖/回复需登录。
- 前端:`/community` 论坛(板块标签 + 发帖 + 帖子列表)、`/thread/:id` 帖子页(正文 + 回复 + 回复框)、详情页"讨论区"(绑定该车型的话题 + 发起讨论);顶栏"社区"入口。
- 测试:后端 e2e 29(发帖/回复/板块/词条绑定/401/400);前端 e2e 16(社区发帖→帖子页回复);两端 build 通过。
- **2025 · 第 15 批(T-3.3 打卡 + T-3.4 地图)→ Phase 3 完成**
- 可写库增 `sightings` 表;`SightingsModule``POST/GET /api/models/:id/sightings``GET /api/sightings/recent`(动态流)、`GET /api/sightings/map`;坐标校验(IsLatitude/IsLongitude),跨库用只读库补 model_code/分类。
- 前端:详情页"目击打卡"区(经纬度/用我的位置 geolocation/车站/车号 + 列表);`/map` 打卡地图(**MapLibre + OpenStreetMap 免费瓦片**,标记 + 弹窗 + 最新打卡侧栏);顶栏"打卡地图"入口。
- 修复:sighting 补全用 `category_id` join 分类(model 表无 category 列)导致的 500。
- 取舍:地图先用点位标记(OSM 栅格瓦片,遵守使用政策并署名);热力图/路局聚合/PostGIS 后续;打卡暂不计贡献积分(保持榜单=数据编辑)。
- 测试:后端 e2e 31(打卡/动态/地图/401/非法坐标 400);前端 e2e 18(详情打卡→列表、地图页加载);两端 build 通过。
- **Phase 3(社区与空间)完成**:论坛 + 词条讨论 + 打卡动态 + 地图。
- **2025 · 第 16 批(修复 + T-4.1 参数对比)**:
- 修复:贡献榜列表 class 名 `.lb` 与灯箱 `.lb`(全屏 fixed 遮罩)冲突,导致无法离开贡献榜页 → 列表改名 `.leaderboard`,加回归测试。
- T-4.1 参数对比(Phase 4 起步):`/compare` 页,搜索加入 24 车型,自绘 **SVG 雷达图**`lib/compare.ts` 归一化纯函数 + `RadarChart`)+ 对比表;URL 同步 ids;详情页"加入对比"按钮;顶栏"对比"入口。
- 测试:前端单元 45(新增 compare 归一化)+ e2e 21(含贡献榜可离开回归、参数对比雷达图);build 通过。
- **2025 · 第 17 批(完成 Phase 4 主体:T-4.2/4.3/4.4**
- T-4.2 技术族谱(`/family`):后端 `GET /api/models/families` 按分类→系列聚合;前端分类标签 + 系列泳道 + 型号节点(非国产标记),点击进详情。
- T-4.3 拍车攻略:后端 `GET /api/sightings/spots` 按车站聚合"在哪能拍到什么车",打卡地图页展示热门打卡点;稀有车提醒(轻量):`useFollow`localStorage 关注)+ 详情"关注"按钮 + 个人主页"关注车型最新目击"汇总。
- T-4.4 国别维度:探索筛选器已有"国别",族谱标注非国产;制造国/国别属性/原型字段就位且可众包编辑。
- 取舍:真正的父/衍生/原型有向图、跨国连线、保存机车地图(缺坐标)、提醒推送基建 → 待数据/基建后续。
- 测试:后端 e2e 33(含 families/spots);前端单元 45 + e2e 23(含族谱跳详情、关注汇总);两端 build 通过。
- **Phase 4 主体完成**(数据可得部分),余项已明确标注待数据/基建。
- **2025 · 第 18 批(Phase 5:数据大屏 + 开放 API + AI 识车占位)**
- 数据大屏(`/stats`):后端 `GET /api/stats`(分类/国别/年代/时速演进聚合);前端 KPI + 柱状图 + 时速演进曲线。
- 开放 API / 导出:`GET /api/export/models.json|csv`CSV 转义)+ `/api-docs` 接口一览与下载按钮;底部导航入口。
- AI 识车(`/identify`):诚实占位页(上传本地预览 + 明确声明非真实识别 + 示例候选),不伪造识别。
- 修复:vite 代理 `/api` 前缀误吞前端路由 `/api-docs` → 改为正则 `^/api/` 仅代理真实接口;加回归测试。
- 取舍(如实标注延后):AR/3D(需 3D 资产)、多语言(需翻译工作量)、真实 AI 模型(需图库训练/算力)。
- 测试:后端 e2e 36(含 stats/export);前端 e2e 28(含大屏/开放API/AI占位 + 两条回归);两端 build 通过。
- **Phase 5 数据可得部分完成**;五个阶段全部推进完毕,未尽项均有明确原因与后续路径。
- **2025 · 第 19 批(前端专业美化)**:
- 导航分组:顶栏收敛为 故事 / 图鉴▾ / 社区▾ / 数据▾ / 审核(`NavMenu` 点击展开下拉,路由/外部点击/Esc 关闭,可访问性 aria)。
- 统一组件与设计令牌:扩充 CSS tokens(间距/圆角/阴影刻度 + 焦点环);新增 `components/ui.tsx`Button / PageHeader / EmptyState / Tag+ 统一 `.btn` 体系;统一卡片悬浮过渡、毛玻璃粘性头部。
- UX 一致性:下拉/焦点/卡片/页头统一;族谱页改用 PageHeader + EmptyState。
- 族谱卡片加列车图片:family 节点改为带封面(Thumb 分类插画)的卡片。
- e2e 适配分组导航(图鉴→探索、社区→论坛);前端单元 45 + e2e 28 全绿;build 通过。
- **2025 · 第 20 批(统一组件 100% 收敛)**:
- 各页改用 `ui` 基础组件:`PageHeader`(贡献榜/社区/对比/大屏/开放API/AI识车/打卡地图/审核/族谱)、`EmptyState`(贡献榜/社区/对比)、`Button`(社区发帖发布、帖子回复、个人退出、审核通过/驳回、详情编辑/关注/加入对比/添加图片/认领/打卡/发起讨论、编辑模态、探索筛选、开放API 导出)。
- 保留差异化控件(分段控件视图切换/板块标签、登录模态、定位按钮)以维持模式语义。
- 文案/类名保持不变,e2e 全绿(前端单元 45 + e2e 28);build 通过。
- **2025 · 第 21 批(图鉴改为管理员独占管理,用户只读)**:
- 风险收口:图鉴词条编辑与图片上传仅管理员可用,普通用户只读。
- 后端:`POST /api/models/:id/revisions``@Roles('admin')`(普通用户 403,未登录 401)。
- 前端:详情页"编辑词条"、"图册 + 添加图片"入口仅 `role==='admin'` 可见(含 file input 一并隐藏);图册文案改为"由管理员维护"。
- 测试:后端 e2e 36(普通用户编辑 403、管理员编辑自动生效、白名单 400);前端 e2e 29(管理员编辑生效、普通用户无编辑/上传入口、管理员上传+灯箱);两端 build 通过。
- 说明:当前管理员图片上传仍是浏览器本地(localStorage,非共享);"管理员云端管理共享图库 + 公共图库 + 上传/打卡即收集 + 云端收集同步 + 集邮成就"为后续批次(已有完整实现方案)。
- **2025 · 第 22 批(后端图片云存储 + 管理员上传写入共享图库)**:
- 可写库新增 `photos` 表;服务端文件存储 `app/data/uploads/``UPLOAD_DIR` 可覆盖),`main.ts` 用 express.static 暴露 `/uploads`
- `PhotosModule``POST /api/models/:id/photos`**仅管理员**multer multipart,类型/大小校验)、`GET /api/models/:id/photos`(公共,所有人可看)、`DELETE /api/photos/:pid`(管理员)。
- 车型列表 API 附带 `cover_url`(共享图库首图),**图鉴卡片用真实照片作封面**(管理员上传后所有访客可见)。
- 详情"图册"改为共享图库:管理员上传(multipart)→ 所有人查看 + 灯箱;移除原 localStorage 本地图方案。
- Vite 增 `^/uploads/` 代理。
- 测试:后端 e2e 40(上传 403/201/非图片 400/删除 + 公共列表);前端 e2e 29(管理员上传→共享→灯箱);两端 build 通过;真实管线 curl 验证(上传→入库→静态 200→代理 200)。
- **2025 · 第 23 批(候选/确认机制 + 批量取图脚本)**:
- `photos``status('candidate'|'confirmed')` + `source_url/author/license`(已存在库自动补列)。管理员手动上传=`confirmed`;脚本入库=`candidate`
- **卡片封面只取 `confirmed`**;详情图册显示"候选/图库"角标 + 署名(作者/许可证/来源),管理员可逐张"✓确认/✕删除",并有"确认全部候选"。
- API`POST /api/photos/:pid/confirm`(管理员)。
- **取图脚本** `apps/api/scripts/fetch-images.mjs``npm run fetch-images`,在有网本机跑):按 **Wikidata P18 → Commons 同名分类 → 关键词搜索** 顺序为缺图车型下载候选图入库(candidate),记录署名,输出命中报告;支持 `--limit/--category/--dry`
- 测试:后端 e2e 41(含候选→确认流程);前端单元 45 + e2e 29;两端 build 通过。
- 说明:脚本需联网访问 wikidata/commons,本沙箱不可联网,需在用户本机运行。
- **2025 · 第 24 批(管理员候选审图页)**:
- 后端 `GET /api/photos/candidates`(管理员):全站候选图聚合 + 车型信息(跨只读库补 model_code/分类)。
- 前端 `/admin/photos` 审图页(仅管理员):候选图网格(缩略图 + 车型链接 + 署名/来源)、逐张确认/删除、"全部确认";顶栏管理员专属"候选审图"入口(普通用户不可见)。
- 测试:前端 e2e 31(含管理员可访问、普通用户无入口);两端 build 通过。
- **2025 · 第 25 批(图鉴展示与封面优化 · 4 项)**:
- 1) 卡片封面始终展示最佳可用图:`cover_url`(共享图库 confirmed)→ Commons 实拍(懒取)→ 分类示意图;移除"未收集锁/暗色遮罩",★ 改为个人"收藏"书签(aria 收藏/已收藏)。
- 2) 详情头图改用真实图:hero = featured 封面 → confirmed 图库 → Commons[0] → 示意图(`.detail-hero-img` 覆盖填充)。
- 3) 管理员可指定展示图:`photos``featured` 列;`POST /api/photos/:pid/feature`(管理员,清同车型其它 featured 并置 confirmed);卡片 cover 查询优先 `featured` 再最小 id;详情图册 confirmed 图加"★ 设为封面"(当前封面金色高亮),候选图保留"✓ 确认"。
- 4) 图鉴可直接输入型号筛选:探索工具条加 `.kw-input`(防抖 300ms → `query.q`)。
- 测试:前端单元 45 + e2e 31(新增"型号关键字筛选""管理员设为封面");后端单元 22 + e2e 42(新增"封面优先 featured + 权限受控");修复 models.service 单测夹具缺 `photos` 表;两端 build 通过。
- **2025 · 第 26 批(Commons 下载到本地,运行时不再访问外部图源)**:
- 诉求:把原先浏览器端实时检索的 Wikimedia Commons 照片**全部下载到本地共享图库**,应用运行时只读本地 `/uploads`,不再发外部请求。
- 前端:移除浏览器端 Commons(删除 `lib/commons.ts` / `lib/useCommonsImages.ts` / `lib/commons.test.ts`)。`GalleryCard` 封面只取本地 `cover_url`(否则分类示意图),不再懒取 Commons;`DetailPage` 图册改为「本地共享图库照片 + 系统示意图」,头图取 featured/confirmed 本地照片;移除"正在从 Commons 检索"提示与 Commons 角标,图注署名仍展示(下载时已存库)。
- 取图脚本升级(`apps/api/scripts/fetch-images.mjs`,本机联网跑一次即可):新增 `--per N`(每车型多张)、`--confirm`(直接入库为 confirmed,封面/图册立即可见,替代原实时 Commons 展示);多来源去重;保存到本地 `app/data/uploads/` + `photos` 表并记录署名(作者/许可证/来源)。
- 说明:构建/测试沙箱仅放行 npm、不可联网,无法在此执行实际下载;需在用户本机运行 `node scripts/fetch-images.mjs --per 3 --confirm` 完成落地。未下载到照片的车型自动回退分类示意图(永不空)。
- 测试:前端单元 42(移除 commons 3 项)+ e2e 31,全绿;后端单元 22 + e2e 42 不变;两端 build 通过。
- **2025 · 第 27 批(修正:下载图片一律进入候选审图)**:
- 问题:上一批脚本提供了 `--confirm`,按该建议运行后下载图直接入库为 `confirmed`**跳过了"候选审图"双闸门**。
- 修正:取图脚本移除 `--confirm`,所有下载图**一律入库为 `candidate`**,进入管理员「候选审图」队列,人工确认后才作封面/图册(恢复"候选 + 人工确认"准确性闸门)。
- 数据修复:将已下载的 Commons 图片(含 `source_url`)从 confirmed 改回 `candidate`(清 featured);并清除早期 e2e 测试误写入生产库的 8 张 1×1 占位图(行 + 文件)。
- 验证:管理员登录 `GET /api/photos/candidates` 返回 15 张候选(带署名),`/admin/photos` 审图页可见可逐张确认。
- **2025 · 第 28 批(图鉴分页记忆 + 去收集示例 + 首页紧凑/带图)**:
- 1) 图鉴翻页记忆:图鉴页码存入 URL `?gp=N``GalleryView` 改为受控分页),从卡片进详情后"← 返回图鉴"用 `navigate(-1)` 回到来路并停在原页;筛选/关键字变化时自动回到第 1 页(`gp` 清除)。
- 2) 图鉴页移除"收集示例"按钮(保留收集进度条与单卡收藏)。
- 3) 首页更紧凑(英雄区/时代轨/留白收敛);时代代表车的 mini 卡在有真实封面(`cover_url`)时直接显示照片,且 `pickRepresentatives` 优先挑有图车型"带出来"。
- 测试:前端单元 42 + e2e 33(新增"翻页进详情返回仍在该页""不再显示收集示例"),目标子集全绿;build 通过。
- **2025 · 第 29 批(全站专业图标 + 数据大屏重构 + AI 识车接入通义千问)**:
- 图标:扩充 `components/icons.tsx`(编辑/对比/确认/关闭/删除/维护/定位/返回/前进/警告/下拉/论坛/图片/奖杯/站点/缩放/翻页等 react-icons 出口);替换 DetailPage、NavMenu、Lightbox、Auth/Edit 模态、ComparePage、ProfilePage、各页返回链接、首页 CTA、EmptyState 等处的 emoji/符号为专业图标;EmptyState `icon` 改为 ReactNode。(保留原始数据 diff 的 `→` 与原生 `<option>` 内 ↑↓ 文案,属内容/原生限制)
- 数据大屏(`/stats`)重构为真正的大屏:顶栏标题 + 实时时钟 + 渐变描边;5 张带图标/光晕的 KPI 卡(车型/个体/分类/最高时速/年代跨度);面板网格含 **SVG 环形图**(分类构成 / 国别构成)、**纵向柱状图**(各年代新增)、时速演进曲线(跨列);响应式。
- AI 识车(`/identify`)接入 **通义千问 DashScope**OpenAI 兼容多模态 `qwen-vl-plus`):后端 `IdentifyModule`——`POST /api/identify`(登录态,multer 内存上传 → base64 data URL → 调用 Qwen-VL,提示词要求输出 JSON 候选);解析候选并**匹配图鉴库**给出可点击详情;密钥走 `apps/api/.env``DASHSCOPE_API_KEY``.gitignore` 忽略,附 `.env.example`main.ts `process.loadEnvFile()` 加载)。前端重写为上传→识别→候选(置信度条/依据)+图鉴相关车型,含未登录提示与加载态。
- 真机验证:上传真实电力机车照片,识别为 **DJ1型(95%)** 并命中铭牌"DJ1 0003A",给出 SS8/HXD1 备选,链接到对应词条。
- 测试:前端单元 42 + e2e(AI 页改为"未登录提示+上传区"、数据大屏改为 donut/vbars 断言),子集全绿;前后端 build 通过。
- **2025 · 第 30 批(AI 识车结果持久化 + 历史 CRUD + 哈希缓存)**
- 可写库新增 `identifications` 表(user_id/filename/image_hash/summary/guesses_json/matches_json/top_code/top_name/note/error/created_at)。
- 后端:`POST /api/identify` 改为**识别并落库**——先按图片 **SHA-256 哈希命中缓存**(同图已成功识别则复用结果与图片文件,不再调用 LLM、不重复存图),否则调用通义千问并保存图片;`GET /api/identifications`(本人历史)、`PATCH /api/identifications/:id`(改备注/人工纠正)、`DELETE /api/identifications/:id`(删除,仅当无其它记录共用该图时删文件);均需登录、按用户隔离。
- 前端:`/identify` 识别后结果加入历史;新增"识别历史"列表——缩略图 + 候选/置信度 + 摘要 + 图鉴相关车型链接 + **可编辑备注(保存)** + 删除 + "缓存命中"标记。
- 真机验证:首次识别 `cached=false` 落库;同图再传 `cached=true`(秒回、零调用);列表/改备注/删除均生效;缓存去重的共享图片在删除其一后仍保留。
- 测试:前端单元 42 + e2e(AI 页 + 数据大屏)全绿;前后端 build 通过;测试产生的识别记录与图片已清理。
- **2025 · 第 31 批(识别历史删除确认 + 导航 IA 重构)**:
- 识别历史删除前弹确认(`确定删除这条识别记录?删除后不可恢复。`),避免误删。
- 导航按用户意图重组:**图鉴**(探索图鉴 / 数据大屏 / 技术族谱 / 参数对比 / AI 识车)、**社区**(论坛 / 打卡地图 / 贡献榜);移除原先混杂的「数据」组。数据大屏作为「图鉴的宏观概览」置于图鉴组(先看大盘再下钻);开放 API 属开发者资源,仅保留在页脚,顶部导航更聚焦。
- 测试:前端 build 通过;导航相关 e2e(社区发帖、审核/候选审图入口、数据大屏、AI 识车)全绿。
## 使用说明
- 任务编号 `T-<阶段>.<序号>`,子项含 **开发 / 单元测试(UT) / 端到端测试(E2E)**
- `[ ]` 未开始 · `[~]` 进行中 · `[x]` 已完成。
- `← FR-x.x` 表示对应 PRD 功能需求;`依赖:` 表示前置任务。
- 测试约定:UT 覆盖率目标 ≥ 80%(核心逻辑模块);E2E 覆盖关键用户旅程。
---
## 技术栈基线(默认,待 PRD Open Question 锁定)
- 前端:Next.js + React + TypeScript;可视化 D3/ECharts;地图 MapLibre。
- 后端:NestJS(Node+TS) 或 FastAPI(Python);本清单以 **NestJS + TypeScript** 表述。
- 数据库:PostgreSQL (+PostGIS);检索 Meilisearch;缓存 Redis;对象存储 S3/OSS。
- 测试:UT 用 Jest/VitestAPI 集成用 SupertestE2E 用 PlaywrightETL 用 pytest(如 Python)。
- CIGitHub Actionslint + UT + 集成 + E2E 冒烟)。
---
# Phase 0 · 工程基建(所有阶段前置)
### T-0.1 仓库与脚手架
- [~] 开发:Monorepo 结构(apps/web、apps/api、packages/shared、etl)。【已建 `app/etl`+`app/tests` 的数据层与测试骨架;JS 前后端 apps 待建】
- [ ] 开发:TypeScript、ESLint、Prettier、commitlint、husky 预提交钩子。
- [x] UT:shared 工具函数(单位解析、格式化)单测样例跑通。【clean/field_dict 已覆盖】
- [ ] E2EPlaywright 安装 + 首条"首页可打开"冒烟用例。
### T-0.2 CI/CD 流水线
- [ ] 开发:CI 跑 lint + UT + 集成测试 + E2E 冒烟;PR 必须绿。
- [ ] 开发:测试覆盖率报告产出与阈值门禁。
- [ ] 开发:预览环境自动部署(每 PR)。
### T-0.3 基础设施
- [ ] 开发:本地 docker-composePostgreSQL+PostGIS、Redis、Meilisearch、MinIO)。
- [ ] 开发:数据库迁移工具(Prisma/TypeORM migration)接入。
- [ ] UT:数据库连接/迁移健康检查测试。
---
# Phase 1 · 数据底座 MVP
## 1A 数据建模与 ETL
### T-1.1 字段字典与数据模型 ← FR(数据需求 4.2)
- [x] 开发:定义统一字段字典(数值+单位分离),落库 schemaCategory / Model / Unit。
- [x] 开发:迁移脚本 + 种子枚举(国别属性、状态、分类)。
- [x] UT:字段校验规则(首产≤停产、数值范围、单位枚举、型号唯一)。
- [x] UT:Model↔关系字段(父型号/衍生/原型车)外键完整性。
### T-1.2 ETL 导入管线 ← FR(4.4)
- [x] 开发:读取 12 张 CSV/Excel,处理合并单元格、空表头修复。
- [x] 开发:单位拆分("整备重量 / 轴重 /t" → 独立字段+单位)。
- [x] 开发:字段映射到统一字典;系列→型号层级归并;改型关系识别。
- [x] 开发:产出导入报告(成功/失败/待人工复核)。
- [x] UT:每类清洗规则的单元测试(含脏数据样例:缺值、混排、异常年代)。
- [x] UT:幂等性测试(重复导入不产生重复记录)。
- [x] E2E:跑全量 12 表导入,断言记录数与抽样字段正确。
## 1B 后端 API
### T-1.3 车型查询 API ← FR-1.1 / FR-1.2 / FR-1.5
- [x] 开发:车型列表接口(分页、分类筛选、排序)。
- [x] 开发:多维筛选(年代区间、时速区间、生产商、国别、状态)。
- [x] 开发:车型详情接口(参数、关系、图集占位)。
- [x] UT:筛选/排序/分页边界(空结果、超范围页码、非法参数)。
- [x] UT:详情序列化(数值+单位、缺失字段留空)。
- [x] E2E(API):列表→详情链路,断言筛选组合结果正确。
### T-1.4 搜索服务 ← FR-1.3
- [~] 开发:Meilisearch 索引(型号/别名/生产商),增量同步。【延后:当前用 SQLite LIKE+排序实现】
- [x] 开发:搜索接口(模糊匹配,拼音为增强项)。
- [x] UT:索引构建与查询排序、空查询、特殊字符处理。
- [x] E2E(API):关键词搜索命中预期车型。
## 1C 前端
### T-1.5 车型列表与详情页 ← FR-1.1/1.4/1.5
- [x] 开发:列表页(筛选器、分页、排序、卡片/表格切换)。
- [x] 开发:详情页(参数表、历史沿革、图集占位、相关入口)。
- [~] 开发:响应式 + SEOSSR/metadata)。【已响应式;SSR/SEO 待 Next.js 化或预渲染,后续】
- [x] UT:筛选器组件、单位展示组件、详情渲染(含缺失字段)。
- [x] E2E:用户筛选→进入详情→参数正确展示的完整旅程。
### T-1.6 时间轴视图 ← FR-2.1
- [x] 开发:年代横轴 + 分类泳道;节点点击展开详情卡。
- [~] 开发:缩放/平移、按分类显隐。【已实现轴+刻度+悬停标签+碰撞分行;缩放/平移待增强】
- [x] UT:时间轴数据转换(年代缺失/区间重叠/排序/碰撞分行)。
- [x] E2E:打开时间轴→点击节点→跳转详情。
### T-1.7 图鉴卡牌视图 ← FR-2.2
- [x] 开发:卡片墙 + 分类筛选 + "已收集/未收集"占位(收集逻辑 P3 接入)。
- [x] UT:卡片列表渲染、筛选、占位状态。
- [x] E2E:切换分类→卡片刷新→点击进入详情。
### T-1.8 MVP 验收
- [x] E2E 套件:首页→搜索→筛选→详情→时间轴→图鉴 全链路冒烟。【Playwright 5 项全通过】
- [x] 性能:列表/详情 P95 < 1.5s,搜索 < 500ms 基准测试。【实测 p95≤3ms / 1.3ms,远优于阈值】
---
# Phase 2 · 众包共建
### T-2.1 账户与权限 ← FR-3.1/3.2/3.3
- [x] 开发:注册/登录(邮箱+第三方)、会话/JWT。【邮箱+密码 JWT;第三方登录后续】
- [x] 开发:RBAC(游客/注册/信任/版主/管理员)。
- [x] 开发:个人主页(贡献统计、徽章、收集进度占位)。
- [x] UT:权限矩阵(各角色可执行操作)、令牌过期/刷新。【AuthService 单测 + 角色等级 RolesGuard】
- [x] E2E:注册→登录→访问受限页→登出。
### T-2.2 编辑与修订版本 ← FR-4.1/4.2/4.5
- [x] 开发:字段编辑建议提交;Revision 版本记录。
- [x] 开发:diff 对比与回滚。【修订含 old→new diff;覆盖按修订追溯,可回滚到基础数据】
- [x] 开发:字段校验(数值范围、单位、年代逻辑)复用 T-1.1 规则。
- [x] UT:版本生成、diff 计算、回滚正确性、校验拦截非法值。【经 e2e 覆盖】
- [x] E2E:编辑字段→生成版本→对比→(管理员)审核生效。
### T-2.3 审核流程 ← FR-4.3/4.4/4.6
- [x] 开发:审核队列;新手入队、信任用户直接生效。
- [x] 开发:字段来源引用(修改说明 note);争议字段讨论后续。
- [x] UT:审核状态机(待审/通过/驳回)、信任分级路由。【经 e2e 覆盖】
- [x] E2E:新手提交→版主审核通过→数据生效。
### T-2.4 荣誉与激励 ← FR-5.1~5.5
- [x] 开发:贡献积分引擎;等级头衔;徽章成就;贡献榜;词条认领署名。
- [x] UT:积分计算、等级阈值、徽章触发条件、防刷分规则。【levels 单测 + e2e 积分/榜单/认领】
- [x] E2E:完成被采纳编辑→积分增加→徽章/榜单更新。
---
# Phase 3 · 社区与空间
### T-3.1 论坛 ← FR-6.1/6.2
- [x] 开发:板块(综合/拍车打卡/历史考证);发帖、回复;持久化永久可查。
- [~] 开发:@、点赞、收藏。【发帖/回复/板块已做;@/赞/收藏后续
- [x] UT:发帖/回复/分页、权限、保留历史。【经 e2e 覆盖】
- [x] E2E:发帖→回复→历史可检索。
### T-3.2 词条讨论与动态流 ← FR-6.3/6.4/6.5
- [x] 开发:详情页内嵌讨论(绑定 model_id 的讨论区)。
- [x] 开发:打卡动态 Feed(打卡地图页"最新打卡")。【精华化引用后续】
- [x] UT:讨论与车型绑定。【经 e2e 覆盖(modelId 过滤)】
- [x] E2E:词条下发起讨论 → 帖子页回复。
### T-3.3 打卡与收集 ← FR-7.1/7.2
- [x] 开发:目击打卡(车号/时间/地点/经纬度);图鉴收集进度联动(localStoragePhase 3 已起)。
- [x] UT:打卡校验(坐标范围)、收集进度计算。【经 e2e(非法坐标 400)+ 前端单测】
- [x] E2E:详情页打卡 → 出现在打卡列表。
### T-3.4 地图视图 ← FR-2.5
- [x] 开发:打卡地图(MapLibre + OpenStreetMap 瓦片)+ 标记 + 最新打卡侧栏。
- [~] 开发:配属地图 + 目击热力图(PostGIS 聚合)。【已做目击点位地图;热力/路局聚合后续】
- [x] UT:地理点位渲染。【经 e2e(map 容器加载)】
- [x] E2E:打开地图→展示标记/侧栏。
---
# Phase 4 · 进阶玩法 + 圈层扩展
### T-4.1 参数对比 ← FR-2.3
- [x] 开发:勾选 2–4 车型→雷达图 + 对比表。
- [x] UT:对比数据归一化、单位对齐、缺失项处理。
- [x] E2E:选择多车型→对比图渲染正确。
### T-4.2 技术族谱图 ← FR-2.4
- [x] 开发:按系列聚合的谱系视图(分类→系列→型号,可进详情)。
- [~] 开发:父/衍生/原型有向图 + 跨国连线。【现用 series 谱系;真正关系待 model_relation 数据/众包】
- [x] UT:族谱数据构建(系列聚合)。【经 e2e 覆盖】
- [x] E2E:打开族谱→节点跳详情。
### T-4.3 拍车攻略 / 保存机车地图 / 稀有车提醒 ← FR-7.3/7.4/7.5
- [x] 开发:拍车攻略(按打卡车站聚合"在哪能拍到什么车")。
- [~] 开发:保存机车地图。【已有 unit.location 文本;缺坐标,地图化待数据】
- [x] 开发:稀有车提醒(轻量版:关注 + 个人主页汇总关注车型最新目击)。【推送待通知基建】
- [x] UT:聚合排序、关注汇总。【经 e2e 覆盖】
- [x] E2E:关注车型→个人主页"最新目击"出现;拍车攻略聚合。
### T-4.4 圈层二数据扩展 ← PRD 3.1
- [~] 开发:在华外国车型 + 国外原型导入。【国别属性/制造国/原型字段就位且可众包编辑;真实国外数据待录入】
- [x] 开发:按国别筛选(探索筛选器)+ 族谱标注非国产。
- [~] 开发:世界地图 / 跨国族谱连线。【待圈层二数据】
- [x] UT:国别字段映射。【经既有覆盖】
---
# Phase 5 · 智能化与开放
### T-5.1 AI 识车 ← FR-8.1
- [x] 开发:照片→车型识别。【接入通义千问 DashScope `qwen-vl-plus` 多模态:`POST /api/identify` 上传识别 + 匹配图鉴库给出候选与链接;密钥走 `DASHSCOPE_API_KEY`
- [~] UT:推理服务接口、置信度阈值、未知类回退。【已含 JSON 解析容错/未配置回退/错误处理;自动化单测待补】
- [x] E2E:上传照片→返回候选。【真机验证:DJ1 型 95% 命中铭牌;前端未登录提示 + 上传区 e2e】
### T-5.2 数据大屏 / 开放 API / AR-3D / 多语言 ← FR-8.2~8.5
- [x] 开发:可视化大屏(`/stats`:分类/国别/年代柱状 + 时速演进曲线,后端 `/api/stats`)。
- [x] 开发:开放 API + 数据导出(`/api/export/models.json|csv` + `/api-docs` 文档页)。
- [ ] 开发:AR/3D 展示。【需 3D 资产/模型,延后】
- [ ] 开发:多语言。【需较大翻译工作量,延后;架构可后续接 i18n】
- [x] UT:聚合统计、导出格式。【后端 e2estats / export json / export csv】
- [x] E2E:大屏渲染、API 文档与导出链接、AI 占位页。
---
# 横切任务(贯穿各阶段)
### T-X.1 非功能与质量 ← PRD 6
- [ ] 开发:性能预算监控、日志/审计(编辑、审核、积分流水)。
- [ ] 开发:安全(防刷分、UGC 审核、敏感地点脱敏)、合规(图片版权声明)。
- [ ] UT:限流、脱敏、版权字段校验。
- [ ] E2E:安全旅程(越权访问被拒、敏感坐标脱敏展示)。
### T-X.2 可观测与运维
- [ ] 开发:错误监控(Sentry)、指标看板(KPI:编辑量/审核通过率/留存)。
- [ ] 开发:数据备份与回滚演练。
---
## 里程碑对照
| 里程碑 | 覆盖任务 | 出口标准 |
|--------|----------|----------|
| M1 (MVP) | T-0.* / T-1.* | 12 表入库 + 列表/详情/搜索/时间轴/图鉴 + 性能达标 |
| M2 | T-2.* | 账户 + 编辑/审核/修订 + 荣誉体系上线 |
| M3 | T-3.* | 论坛/讨论/动态/打卡 + 地图 |
| M4 | T-4.* | 对比 + 族谱 + 进阶玩法 + 圈层二数据 |
| M5 | T-5.* | AI 识车 + 大屏 + 开放 API + AR/3D + 多语言 |
## 关键依赖链
```
T-0.* ──▶ T-1.1 ──▶ T-1.2 ──▶ T-1.3/1.4 ──▶ T-1.5/1.6/1.7 ──▶ T-1.8(MVP)
└─▶ T-2.1 ──▶ T-2.2 ──▶ T-2.3 ──▶ T-2.4
T-2.1 ──▶ T-3.1/3.2/3.3 ; T-1.x(PostGIS) ──▶ T-3.4
T-1.1(关系字段) ──▶ T-4.2(族谱) ; T-3.3 ──▶ T-4.3
T-1.1(国别/原型) ──▶ T-4.4(圈层二)
```