|
|
# 查价系统 工程任务拆解清单 v1.2
|
|
|
|
|
|
| 版本 | 日期 | 说明 |
|
|
|
|------|------|------|
|
|
|
| v1.0 | 2026-06-16 | 首版原子任务拆解 |
|
|
|
| v1.1 | 2026-06-16 | 对照 Traceability Matrix 补齐;口径对齐 PRD v0.4 |
|
|
|
| v1.1.1 | 2026-06-16 | **按推荐开发依赖顺序重排任务章节**(非 Task ID 数字序) |
|
|
|
| v1.2 | 2026-06-16 | 对齐 PRD v0.6 + 技术设计 v1.2;基线 001–118 已完成;增量 119–135 |
|
|
|
|
|
|
> **当前版本请使用本文件(v1.2)。** v1.1 文件保留作历史对照。
|
|
|
|
|
|
> 配套文档:[查价系统-PRD.md](./查价系统-PRD.md)(v0.6)· [查价系统-技术设计.md](./查价系统-技术设计.md)(v1.2)· [查价系统-UI设计.md](./查价系统-UI设计.md) · [查价系统-Traceability-Matrix-v1.0.md](./查价系统-Traceability-Matrix-v1.0.md)
|
|
|
> **v1.1 基线(task-001~118,除废止)= 已完成;v1.2 增量(task-119~135)= PRD v0.6 + 技术设计 v1.2 待交付。**
|
|
|
|
|
|
---
|
|
|
|
|
|
## 开发顺序总览(自上而下执行)
|
|
|
|
|
|
| Phase | 名称 | Task 序列 | 状态 |
|
|
|
|-------|------|-----------|------|
|
|
|
| 0 | 环境与底座 | 072→073→**119→120**→070→071→097→030 | 072–030 已完成;119–120 待执行 |
|
|
|
| 1 | 基础设施与缓存 | 096→031~036→035-ip | 已完成 |
|
|
|
| 2 | 鉴权 | 001~005→098 | 已完成 |
|
|
|
| 3 | 领域逻辑 | 040~044→010~014,092,095,109,111,019 | 已完成 |
|
|
|
| 4 | API 层 | 015~017→042~043→018 | 已完成 |
|
|
|
| 5.0 | RPA 录制前置 | **121→122** | 已完成 |
|
|
|
| 5 | RPA v1.2 重构 | **123→131**(基线 020–029,110,093,099,100 已完成) | 123–131 待执行 |
|
|
|
| 6 | 预警/审计 | 050~057→094→105 | 已完成 |
|
|
|
| 7 | 观测指标 | 101→102 | 已完成 |
|
|
|
| 8 | 前端 | 060~069→106→063,064,079,103,104→**132**→080 | 132 已完成 |
|
|
|
| 9 | 测试/CI | 081~118→**133→135**→091 | 133–135 待执行 |
|
|
|
| — | 已废止 | 075~078 | — |
|
|
|
|
|
|
**里程碑 M1(Phase 0~4)**:已完成 — `POST /quotes` L2 命中、幂等、校验 400。
|
|
|
**里程碑 M2(v1.2)**:`RPA_MOCK_MODE=false` 下 `scripts/probe-rpa.ts` **连续 3 次 exit 0**,四档 `source_type=rpa`(TC-601 + §12.7 Go/No-Go)。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 模块索引(按职能归类,非执行顺序)
|
|
|
|
|
|
| 模块 | Task ID | 数量 |
|
|
|
|------|---------|------|
|
|
|
| 数据库(DB) | 070–073 | 4 |
|
|
|
| 基础设施(Lib) | 096–098, **119–120** | 5 |
|
|
|
| 缓存系统 | 030–036, 035-ip | 8 |
|
|
|
| 用户系统(Auth) | 001–005 | 5 |
|
|
|
| 加价系统(Markup) | 040–044 | 5 |
|
|
|
| 查价核心(Quote Engine) | 010–019, 092, 095, 109, 111, **129** | 15 |
|
|
|
| API 路由 | 015–018, 042–043 | 6 |
|
|
|
| RPA 系统 | 020–029, 093, 099–100, 110, **121–131** | 23 |
|
|
|
| 预警系统(Alert) | 050–057, 094, 105 | 10 |
|
|
|
| 观测与指标 | 101–102 | 2 |
|
|
|
| 前端 UI | 060–080, 103–104, 106, **132** | 19 |
|
|
|
| 测试系统 | 081–091, 112–118, **133–135** | 21 |
|
|
|
| **合计(可执行)** | | **~119** |
|
|
|
| **已废止** | 075–078 | 4 |
|
|
|
|
|
|
## 技术栈约定
|
|
|
|
|
|
- Next.js 15 App Router + TypeScript
|
|
|
- MySQL 8.0 + Prisma ORM
|
|
|
- Redis 7 + BullMQ
|
|
|
- Playwright(RPA Worker)
|
|
|
- Tailwind CSS + shadcn/ui + phosphor-icons
|
|
|
|
|
|
## v1.1 口径冻结(对齐 PRD v0.4,基线已完成)
|
|
|
|
|
|
| 决策 | 规则 | 任务影响 |
|
|
|
|------|------|----------|
|
|
|
| GD-11 嵌入形态 | 查价 UI 为**宿主内嵌组件**,中台仅 admin 管理端 | task-106 替代 075–077;task-064 仅 admin |
|
|
|
| §2.3 加价配置 | 中台**不提供**运营加价 UI,由宿主调 API | task-078 **废止**;task-042/043 保留 |
|
|
|
| 四档报价 | GD-1/TC-102 为准(quotes=4) | task-088/111 按 4 档验收 |
|
|
|
| Fallback | 独立 FallbackOrchestrator 模块 | task-092 |
|
|
|
| 管理端 RPA | §3.1 预警 + RPA/开发进度 | task-093, 103, 104 |
|
|
|
| audit / metrics | §10.6 audit log + GD-4 三率 | task-094, 101, 102, 117 |
|
|
|
|
|
|
## v1.2 口径冻结(对齐 PRD v0.6 / 技术设计 v1.2)
|
|
|
|
|
|
| 决策 | 规则 | 任务影响 |
|
|
|
|------|------|----------|
|
|
|
| GD-15 入口 URL | `MOTHERSHIP_QUOTE_URLS` headed 录制;禁止 dashboard / `/quote` | task-119, 121, 122, 123 |
|
|
|
| 入口 vs 抓取错误 | 入口错误与抓取错误分开分类;地址未锁定单独处理 | task-125, 128, 129 |
|
|
|
| 新错误码 | `QUOTE_ENTRY_UNAVAILABLE`、`ADDRESS_SUGGESTION_NOT_FOUND` | task-128, 110(基线) |
|
|
|
| QuotePageAdapter | 多 URL 探测 + storageState + login 无凭据→STRUCT_CHANGE | task-123, 124 |
|
|
|
| Selector env 化 | 全部 `RPA_SELECTOR_*`,禁止手猜 selector | task-125–127 |
|
|
|
| probe 门禁 | 连续 3 次 exit 0 + 四档 | task-130, 133–135 |
|
|
|
| Bull Board | `/admin/queues` 队列监控(P1) | task-132 |
|
|
|
|
|
|
## v1.0 → v1.1 变更摘要
|
|
|
|
|
|
| 类型 | Task | 说明 |
|
|
|
|------|------|------|
|
|
|
| 新增 | 092–118(除重复编号) | 见各 Phase 章节 |
|
|
|
| 修订 | 005, 014, 016, 018, 029, 050, 064, 065, 066, 079, 085, 091 | 补 RBAC/置信度/Fallback 引用等 |
|
|
|
| 废止 | 075, 076, 077, 078 | PRD v0.4 口径下不交付 |
|
|
|
| 替代 | 106 | 宿主内嵌查价组件(替代 075) |
|
|
|
| 排序 | v1.1.1 | 正文按 Phase 0→9 开发顺序排列 |
|
|
|
|
|
|
## v1.1 → v1.2 变更摘要
|
|
|
|
|
|
| CHG ID | 类型 | Task | 说明 |
|
|
|
|--------|------|------|------|
|
|
|
| CHG-P02/T03 | 新增 | 119, 121, 122 | GD-15 env + 录制前置 |
|
|
|
| CHG-T04 | 新增 | 120 | docker rpa_state volume |
|
|
|
| CHG-P05/T05/T06 | 新增 | 123 | QuotePageAdapter |
|
|
|
| CHG-T09 | 新增 | 124 | storageState 生命周期 |
|
|
|
| CHG-T07 | 新增 | 125–127 | selector env 化 |
|
|
|
| CHG-P03/P04/T08 | 新增 | 128, 129 | 错误分类 v0.6 |
|
|
|
| CHG-P08/T02 | 新增 | 130 | probe 3/3 |
|
|
|
| CHG-T03 P1 | 新增 | 131 | Patchright 可选 |
|
|
|
| CHG-T10 | 新增 | 132 | /admin/queues Bull Board |
|
|
|
| CHG-P08/P09 | 新增 | 133–135 | TC-601 + Go-No-Go + CI |
|
|
|
| — | 状态 | 001–118 | 全部标「已完成」(废止除外) |
|
|
|
| — | 说明 | 022, 023, 028, 073, 110, 117, 088 | 基线已实现;行为变更由 task-12x 承接 |
|
|
|
|
|
|
---
|
|
|
|
|
|
# Phase 0 — 环境与底座
|
|
|
|
|
|
> 执行顺序:task-072 → task-073 → **task-119 → task-120** → task-070 → task-071 → task-097 → task-030
|
|
|
|
|
|
## task-072
|
|
|
|
|
|
- **ID**:task-072
|
|
|
- **状态**:已完成
|
|
|
- **模块**:数据库
|
|
|
- **目标**:创建 Docker Compose 配置(4容器)
|
|
|
- **输入**:技术设计 §6.1
|
|
|
- **输出**:`docker-compose.yml`,含 mysql/redis/next-app/rpa-worker,含 healthcheck 和 shm_size
|
|
|
- **实现范围**:仅基础设施配置,不含应用代码
|
|
|
- **验证**:`docker compose up -d` 四容器全部 healthy;`docker compose ps` 状态 running
|
|
|
---
|
|
|
|
|
|
## task-073
|
|
|
|
|
|
- **ID**:task-073
|
|
|
- **状态**:已完成
|
|
|
- **模块**:数据库
|
|
|
- **目标**:创建 `.env.example` + Dockerfile + Dockerfile.worker
|
|
|
- **输入**:技术设计 §6.2
|
|
|
- **输出**:`.env.example` / `Dockerfile` / `Dockerfile.worker`
|
|
|
- **实现范围**:环境变量模板 + 容器构建文件
|
|
|
- **验证**:`docker compose build` 无报错;`.env.example` 含全部必填变量
|
|
|
- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接
|
|
|
|
|
|
---
|
|
|
|
|
|
# 基础设施(Lib)
|
|
|
---
|
|
|
|
|
|
## task-119
|
|
|
|
|
|
- **ID**:task-119
|
|
|
- **状态**:待执行
|
|
|
- **模块**:Lib/Infra
|
|
|
- **目标**:v0.6 RPA 环境变量模板(GD-15)
|
|
|
- **输入**:PRD §8.2.1;技术设计 §6.2 CHG-T03
|
|
|
- **输出**:`.env.example` 增 `MOTHERSHIP_QUOTE_URLS`、`RPA_STORAGE_STATE_PATH`、`RPA_SELECTOR_*`(8 项)、`RPA_USE_PATCHRIGHT`
|
|
|
- **实现范围**:禁止内置 dashboard / `/quote` 默认 URL;缺 `MOTHERSHIP_QUOTE_URLS` 时 rpa-worker fail-fast
|
|
|
- **验证**:`.env.example` 含全部 RPA 必填项;未配置 URL 列表时 worker 启动报错退出
|
|
|
---
|
|
|
|
|
|
## task-120
|
|
|
|
|
|
- **ID**:task-120
|
|
|
- **状态**:待执行
|
|
|
- **模块**:Lib/Infra
|
|
|
- **目标**:Docker RPA 持久化(storageState volume)
|
|
|
- **输入**:技术设计 §6.1 §7.5 CHG-T04
|
|
|
- **输出**:`docker-compose.yml` rpa-worker 注入 RPA env + `rpa_state` volume → `/app/.rpa`
|
|
|
- **实现范围**:volume 挂载 + env 透传;不含业务逻辑
|
|
|
- **验证**:`docker compose up -d` 后 `.rpa/mothership-storage.json` 跨容器重启保留
|
|
|
---
|
|
|
|
|
|
## task-070
|
|
|
|
|
|
- **ID**:task-070
|
|
|
- **状态**:已完成
|
|
|
- **模块**:数据库
|
|
|
- **目标**:创建完整 Prisma schema(6张表)
|
|
|
- **输入**:PRD §9 + 技术设计 §3.5
|
|
|
- **输出**:`prisma/schema.prisma`,含 SysUser / QuoteRecord / IdempotencyRecord / QuoteCacheMeta / MarkupConfig / AlertLog / **AuditLog**
|
|
|
- **实现范围**:仅 schema 文件,不含 migrate 执行
|
|
|
- **验证**:`npx prisma validate` 零错误;所有字段类型、约束与 PRD DDL 一致
|
|
|
---
|
|
|
|
|
|
## task-071
|
|
|
|
|
|
- **ID**:task-071
|
|
|
- **状态**:已完成
|
|
|
- **模块**:数据库
|
|
|
- **目标**:执行 Prisma migrate + seed(初始化 admin_demo 账号)
|
|
|
- **输入**:task-070 的 schema
|
|
|
- **输出**:`prisma/migrations/` + `prisma/seed.ts`,创建 admin_demo/Demo@123
|
|
|
- **实现范围**:仅 migrate dev + seed;不含业务逻辑
|
|
|
- **验证**:`npx prisma migrate dev` 无报错;`SELECT * FROM sys_user` 返回 1 行 admin
|
|
|
---
|
|
|
|
|
|
## task-097
|
|
|
|
|
|
- **ID**:task-097
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Lib
|
|
|
- **目标**:实现 lib/prisma.ts Prisma 客户端单例
|
|
|
- **输入**:技术设计 §5
|
|
|
- **输出**:`lib/prisma.ts`;导出 `prisma` 单例;dev 热重载防多实例
|
|
|
- **实现范围**:仅 ORM 连接;不含业务查询
|
|
|
- **验证**:`prisma.$connect()` 成功;多模块 import 同一实例
|
|
|
---
|
|
|
|
|
|
## task-030
|
|
|
|
|
|
- **ID**:task-030
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现 Redis 客户端单例(lib/redis.ts)
|
|
|
- **输入**:ioredis ^5.x;技术设计 §3.6
|
|
|
- **输出**:`lib/redis.ts`;导出 `getRedis()` 单例;连接失败时抛错并 log
|
|
|
- **实现范围**:连接配置 + 错误处理;不含业务操作
|
|
|
- **验证**:`getRedis().ping()` 返回 PONG;Redis 不可达 → 抛异常并记录 error log
|
|
|
---
|
|
|
|
|
|
# Phase 1 — 基础设施与缓存(W1)
|
|
|
|
|
|
> 执行顺序:task-096 → task-031 → task-032 → task-033 → task-034 → task-035 → task-035-ip → task-036
|
|
|
|
|
|
## task-096
|
|
|
|
|
|
- **ID**:task-096
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Lib
|
|
|
- **目标**:实现 lib/response.ts 统一 API 响应包络
|
|
|
- **输入**:技术设计 §4.1;PRD §8.5
|
|
|
- **输出**:`lib/response.ts`;`ok(data)` / `fail(code, message, httpStatus)`;所有路由复用
|
|
|
- **实现范围**:仅响应格式化;自动附加 `Cache-Control: no-store` 辅助函数
|
|
|
- **验证**:Vitest:ok → `{ code:0, message:'ok', data }`;fail → `{ code, message, data:null }` + 正确 HTTP 状态码
|
|
|
---
|
|
|
|
|
|
## task-031
|
|
|
|
|
|
- **ID**:task-031
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现 L1 缓存(idem:{request_id},24h)
|
|
|
- **输入**:task-030;PRD §7.2;GD-2
|
|
|
- **输出**:`modules/cache/redis-cache.ts` 中 `getL1(request_id)` / `setL1(request_id, data)`
|
|
|
- **实现范围**:SETNX + GET;TTL=24h;value=完整响应快照 JSON
|
|
|
- **验证**:set → get 返回相同值;TTL 校验 ≈ 86400s;重复 SETNX → 不覆盖
|
|
|
---
|
|
|
|
|
|
## task-032
|
|
|
|
|
|
- **ID**:task-032
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现 L2 缓存(quote:{cargo_hash},3min+jitter)
|
|
|
- **输入**:task-030;PRD §7.2;GD-2
|
|
|
- **输出**:`modules/cache/redis-cache.ts` 中 `getL2(cargo_hash)` / `setL2(cargo_hash, data)`
|
|
|
- **实现范围**:SET + GET;TTL=180+random(0~30)s;value=四档原始报价 JSON(不含加价)
|
|
|
- **验证**:set → get 返回相同值;TTL 在 180~210s 之间(随机 jitter 验证分布)
|
|
|
---
|
|
|
|
|
|
## task-033
|
|
|
|
|
|
- **ID**:task-033
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现 L3 缓存(stale:{cargo_hash},30min,只读降级)
|
|
|
- **输入**:task-030;PRD §7.2;GD-2
|
|
|
- **输出**:`modules/cache/redis-cache.ts` 中 `getL3(cargo_hash)` / `setL3(cargo_hash, data)`(仅 RPA 成功时写入)
|
|
|
- **实现范围**:SET + GET;TTL=1800s;**正常路径禁止读取 L3**(只在 RPA 失败 fallback 时读)
|
|
|
- **验证**:RPA 成功后 L3 有值;L2 命中路径不访问 L3
|
|
|
---
|
|
|
|
|
|
## task-034
|
|
|
|
|
|
- **ID**:task-034
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现缓存击穿锁(lock:quote:{cargo_hash})
|
|
|
- **输入**:task-030;PRD §11.6
|
|
|
- **输出**:`modules/cache/redis-cache.ts` 中 `acquireLock(cargo_hash)` / `releaseLock(cargo_hash)`
|
|
|
- **实现范围**:SETNX lock key,TTL=5s;获取失败 → 等待2s后读L2;用完后 DEL
|
|
|
- **验证**:100并发同一 cargo_hash miss → 只触发1次 RPA;其余等待后命中 L2(TC-11.6)
|
|
|
---
|
|
|
|
|
|
## task-035
|
|
|
|
|
|
- **ID**:task-035
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现限流 Redis Key(令牌桶,60次/分钟/customer)
|
|
|
- **输入**:task-030;PRD §10.3 §11.7;GD-4
|
|
|
- **输出**:`modules/cache/rate-limiter.ts`;`checkRateLimit(customer_id)` → 超限返回 RATE_LIMITED
|
|
|
- **实现范围**:ratelimit:customer:{id} INCR + EXPIRE 60s;超60 → 429
|
|
|
- **验证**:60次 → 全部通过;第61次 → 429 RATE_LIMITED(TC-303 TC-503)
|
|
|
---
|
|
|
|
|
|
## task-035-ip
|
|
|
|
|
|
- **ID**:task-035-ip
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现 IP 级限流(1000次/分钟,超限封禁 10 分钟)
|
|
|
- **输入**:PRD §11.9
|
|
|
- **输出**:`modules/cache/ip-rate-limiter.ts`;`ratelimit:ip:{ip}` + `ban:ip:{ip}` TTL=600s
|
|
|
- **实现范围**:middleware 调用;>1000/min → 429 + 写 SECURITY 预警(task-105)
|
|
|
- **验证**:单 IP 1200 次/分钟 → 429;封禁 10 分钟内全部拒绝(TC-503 IP 维度)
|
|
|
|
|
|
---
|
|
|
|
|
|
# 加价系统(Markup)
|
|
|
---
|
|
|
|
|
|
## task-036
|
|
|
|
|
|
- **ID**:task-036
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Cache
|
|
|
- **目标**:实现熔断状态管理(circuit:rpa Redis key)
|
|
|
- **输入**:task-030;PRD §10.3 §11.5;GD-4
|
|
|
- **输出**:`modules/cache/circuit-breaker.ts`;`isCircuitOpen()` / `openCircuit()` / `closeCircuit()`
|
|
|
- **实现范围**:Redis key circuit:rpa;TTL=600s(10min);连续失败≥3次 → open;期间 Worker 不消费
|
|
|
- **验证**:连续3次失败 → circuit open;10分钟后自动关闭;open期间 job 走 L3/failed(TC-209)
|
|
|
---
|
|
|
|
|
|
# Phase 2 — 鉴权
|
|
|
|
|
|
> 执行顺序:task-001 → task-002 → task-003 → task-004 → task-005 → task-098
|
|
|
|
|
|
## task-001
|
|
|
|
|
|
- **ID**:task-001
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Auth
|
|
|
- **目标**:实现 JWT 工具函数(jose 库:签发/校验/解码)
|
|
|
- **输入**:技术设计 §4.2;jose ^5.x
|
|
|
- **输出**:`modules/auth/jwt.ts`,导出 `signToken(payload)` / `verifyToken(token)` / `decodeToken(token)`
|
|
|
- **实现范围**:JWT 操作只;不含数据库查询
|
|
|
- **验证**:Vitest 单元测试:签发 → 校验通过;篡改 → 抛异常;过期 → 抛异常
|
|
|
---
|
|
|
|
|
|
## task-002
|
|
|
|
|
|
- **ID**:task-002
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Auth
|
|
|
- **目标**:实现 POST /api/auth/login(仅 admin 角色)
|
|
|
- **输入**:task-001;Prisma SysUser
|
|
|
- **输出**:`app/api/auth/login/route.ts`;成功返回 `{ token, user: { user_id, role } }`
|
|
|
- **实现范围**:用户名密码校验(bcrypt compare)+ JWT 签发;非 admin 拒绝;不含 RBAC
|
|
|
- **验证**:curl POST 正确凭据 → 200 + token;错误密码 → 401;customer 账号 → 403
|
|
|
---
|
|
|
|
|
|
## task-003
|
|
|
|
|
|
- **ID**:task-003
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Auth
|
|
|
- **目标**:实现 middleware.ts(JWT 鉴权 + 路由保护)
|
|
|
- **输入**:task-001;Next.js middleware
|
|
|
- **输出**:`middleware.ts`;未鉴权路由返回 401;admin 路由校验 role=admin
|
|
|
- **实现范围**:路由匹配 + JWT 校验;不含限流(task-035 实现)
|
|
|
- **验证**:无 token 访问 `/api/quotes` → 401;有效 admin JWT 访问 `/api/alerts` → 通过
|
|
|
---
|
|
|
|
|
|
## task-004
|
|
|
|
|
|
- **ID**:task-004
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Auth
|
|
|
- **目标**:实现宿主 Service Token 验证(customer_id 租户绑定校验)
|
|
|
- **输入**:PRD §2.1;task-003
|
|
|
- **输出**:`modules/auth/service-token.ts`;校验 token + customer_id 绑定;越权 403
|
|
|
- **实现范围**:Service Token 解析 + customer_id 匹配;不含 JWT 签发(宿主自签)
|
|
|
- **验证**:正确 token + 匹配 customer_id → 通过;customer_id 不匹配 → 403
|
|
|
---
|
|
|
|
|
|
## task-005
|
|
|
|
|
|
- **ID**:task-005
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Auth
|
|
|
- **目标**:实现 RBAC 权限检查函数(含 rpa:operate)
|
|
|
- **输入**:PRD §10.6 §2.1;task-001
|
|
|
- **输出**:`modules/auth/rbac.ts`,导出 `requirePermission(token, permission)` 中间件
|
|
|
- **实现范围**:权限枚举:`pricing:markup:write` / `alert:read` / **`rpa:operate`**;无权限抛 FORBIDDEN
|
|
|
- **验证**:Vitest:admin token + alert:read → 通过;admin + rpa:operate → 通过;宿主 token + alert:read → 403
|
|
|
|
|
|
---
|
|
|
|
|
|
# 查价核心系统(Quote Engine)
|
|
|
---
|
|
|
|
|
|
## task-098
|
|
|
|
|
|
- **ID**:task-098
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Lib
|
|
|
- **目标**:实现 gateway 503 单次重试中间件
|
|
|
- **输入**:PRD §11.4
|
|
|
- **输出**:`middleware.ts` 或 `lib/gateway-retry.ts`;5xx 时 retry 1 次后仍失败 → 503 INTERNAL_ERROR
|
|
|
- **实现范围**:仅网关层重试;不含业务降级
|
|
|
- **验证**:mock 首次 503 第二次 200 → 客户端得 200;连续 503 → 503 + INTERNAL_ERROR
|
|
|
|
|
|
---
|
|
|
|
|
|
# 用户系统(Auth)
|
|
|
---
|
|
|
|
|
|
# Phase 3 — 加价基础
|
|
|
|
|
|
> 执行顺序:task-040 → task-041 → task-044
|
|
|
|
|
|
## task-040
|
|
|
|
|
|
- **ID**:task-040
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Pricing
|
|
|
- **目标**:实现 roundHalfUp 工具函数
|
|
|
- **输入**:PRD GD-10;技术设计 §4.5
|
|
|
- **输出**:`lib/math.ts`;`roundHalfUp(value, decimals)` 使用 Number.EPSILON 修正
|
|
|
- **实现范围**:仅数学函数
|
|
|
- **验证**:Vitest:0.105×2 → 0.21(不是0.20);32.005 → 32.01;边界值含负数
|
|
|
---
|
|
|
|
|
|
## task-041
|
|
|
|
|
|
- **ID**:task-041
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Pricing
|
|
|
- **目标**:实现 PricingEngine.applyMarkup()
|
|
|
- **输入**:task-040;PRD §6.3
|
|
|
- **输出**:`modules/pricing/engine.ts`;`applyMarkup(rawFreight, rawTotal, markupPercent)` → `{ markupAmount, finalTotal }`
|
|
|
- **实现范围**:markup_amount = ROUND_HALF_UP(raw_freight × markup_percent / 100, 2);final_total = ROUND_HALF_UP(raw_total + markup_amount, 2)
|
|
|
- **验证**:Vitest:raw_freight=320,surcharges=15,markup=10% → markup_amount=32.00,final_total=367.00(TC-103);raw_freight=380,markup=10% → 38.00/418.00
|
|
|
---
|
|
|
|
|
|
## task-044
|
|
|
|
|
|
- **ID**:task-044
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Pricing
|
|
|
- **目标**:实现未配置客户默认 0% 逻辑(MarkupConfig 查询)
|
|
|
- **输入**:Prisma MarkupConfig;PRD BR-6.3
|
|
|
- **输出**:`modules/pricing/engine.ts` 中 `getMarkupPercent(customer_id)` 函数;未配置返回 0.0
|
|
|
- **实现范围**:DB 查询 + 默认值兜底;不含写操作
|
|
|
- **验证**:Vitest:未配置 customer_id → 返回 0.0;已配置 10.0% → 返回 10.0
|
|
|
|
|
|
---
|
|
|
|
|
|
# 预警系统(Alert)
|
|
|
---
|
|
|
|
|
|
# Phase 3 — 查价领域逻辑
|
|
|
|
|
|
> 执行顺序:task-010 → task-011 → task-012 → task-095 → task-111 → task-013 → task-019 → task-092 → task-109 → task-014
|
|
|
|
|
|
## task-010
|
|
|
|
|
|
- **ID**:task-010
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 ValidationModule(输入校验 + Mothership 官方硬限)
|
|
|
- **输入**:PRD §4.2.8;zod;技术设计 §7.2.2
|
|
|
- **输出**:`modules/quote/validation.ts`;导出 `MOTHERSHIP_LIMITS` 常量 + `validateQuoteInput(body)` 函数
|
|
|
- **实现范围**:selected_from_suggestions=true 校验;pallet_count 1–25;dims L≤999/W≤99/H≤99 in;单托重≤9999 lb;总重 ≤249975 lb;cargo_type 9项枚举;request_id UUID v4;zip 正则;SQL注入检测
|
|
|
- **验证**:Vitest:TC-201~TC-204 + TC-E4.1~E4.7 全部场景;边界值测试
|
|
|
---
|
|
|
|
|
|
## task-011
|
|
|
|
|
|
- **ID**:task-011
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现单位换算函数(kg→lb,cm→in)
|
|
|
- **输入**:PRD §4.2.2 §4.2.3;GD-13
|
|
|
- **输出**:`modules/quote/unit-converter.ts`;`kgToLb(kg)` / `cmToIn(cm)`,ROUND_HALF_UP 2位
|
|
|
- **实现范围**:仅数学换算;不含校验逻辑
|
|
|
- **验证**:Vitest:227kg → 500.05lb(±0.01);122cm → 48.03in;精度 ROUND_HALF_UP
|
|
|
---
|
|
|
|
|
|
## task-012
|
|
|
|
|
|
- **ID**:task-012
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 cargo_hash 算法(MD5)
|
|
|
- **输入**:PRD BR-4.4;技术设计 §3.7
|
|
|
- **输出**:`modules/quote/cargo-hash.ts`;`buildCargoHash(input)` 返回 32位 hex
|
|
|
- **实现范围**:含 pickup(place_id) + delivery + weight_lb + dims_in + pallet_count + cargo_type;不含 service_level / customer_id
|
|
|
- **验证**:Vitest:相同入参 → 相同 hash;service_level 不同 → 相同 hash;place_id 不同 → 不同 hash
|
|
|
---
|
|
|
|
|
|
## task-095
|
|
|
|
|
|
- **ID**:task-095
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 confidence_score 赋值逻辑(F5.6)
|
|
|
- **输入**:PRD §5.3 F5.6
|
|
|
- **输出**:`modules/quote/confidence.ts`;`getConfidenceScore(sourceType)` → rpa/cache=0.95;stale=0.70
|
|
|
- **实现范围**:仅映射函数;在 GET /quotes 与落库时调用
|
|
|
- **验证**:Vitest:source=rpa → 0.95;source=stale → 0.70;source=cache → 0.95
|
|
|
---
|
|
|
|
|
|
## task-111
|
|
|
|
|
|
- **ID**:task-111
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 BR-5.5 四档齐全校验(done 前 gates)
|
|
|
- **输入**:PRD BR-5.5;GD-1
|
|
|
- **输出**:`modules/quote/quote-completeness.ts`;`assertFourTiers(quotes[])` → 不足 4 档抛 RPA_DATA_INVALID
|
|
|
- **实现范围**:校验 standard/guaranteed × lowest/fastest 组合齐全;orchestrator 与 RPA worker 共用
|
|
|
- **验证**:3 档 → 抛错走 Fallback;4 档 → 通过;TC-102
|
|
|
|
|
|
---
|
|
|
|
|
|
# RPA 系统
|
|
|
---
|
|
|
|
|
|
## task-013
|
|
|
|
|
|
- **ID**:task-013
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 IdempotencyModule(Redis L1 + DB 双保险)
|
|
|
- **输入**:task-031(L1 Redis);Prisma IdempotencyRecord;PRD §7.3 §10.4
|
|
|
- **输出**:`modules/quote/idempotency.ts`;`checkIdempotency(request_id)` + `saveIdempotency(request_id, quote_id)`
|
|
|
- **实现范围**:Redis SETNX L1 → 命中返回 quote_id;DB 唯一键冲突处理;24h TTL
|
|
|
- **验证**:Vitest:重复 request_id → 返回同一 quote_id;并发 10次 → 仅写入 1条 DB 记录(TC-207)
|
|
|
---
|
|
|
|
|
|
## task-019
|
|
|
|
|
|
- **ID**:task-019
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 quote_id 生成函数
|
|
|
- **输入**:PRD §5.2 示例格式(QTE_20260616_0001)
|
|
|
- **输出**:`modules/quote/quote-id.ts`;`generateQuoteId()` 含日期+序号;保证唯一性
|
|
|
- **实现范围**:仅 ID 生成;使用 DB 自增或 UUID+日期前缀
|
|
|
- **验证**:并发 100次调用 → 无重复(TC-402);DB 唯一键冲突 → INTERNAL_ERROR + 写 audit(task-109)
|
|
|
---
|
|
|
|
|
|
## task-092
|
|
|
|
|
|
- **ID**:task-092
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 FallbackOrchestrator(RPA 失败 → L3 降级完整编排)
|
|
|
- **输入**:task-033(L3);task-041(加价);task-050~053(预警);PRD §7.6 §8.4 §2.5
|
|
|
- **输出**:`modules/quote/fallback-orchestrator.ts`;`handleRpaFailure(quoteId, cargoHash, customerId, error)`
|
|
|
- **实现范围**:读 L3 → 有:按客户加价重算 → UPDATE done(source=stale,is_realtime=false) → STALE_FALLBACK 预警;无:failed + QUOTE_UNAVAILABLE → RPA_FAILED 预警;**不抛异常阻断主流程**
|
|
|
- **验证**:TC-205(stale done);TC-206(failed);TC-602;集成测试可单点 mock RPA 失败
|
|
|
---
|
|
|
|
|
|
## task-109
|
|
|
|
|
|
- **ID**:task-109
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 quote_id DB 唯一冲突 → INTERNAL_ERROR 处理
|
|
|
- **输入**:PRD §11.10
|
|
|
- **输出**:orchestrator 落库 catch P2002 → 写 audit + 返回 500 INTERNAL_ERROR
|
|
|
- **实现范围**:仅 quote_id 冲突;不含其他 DB 错误
|
|
|
- **验证**:mock 唯一键冲突 → 500 + audit_log 有记录;不写入脏数据
|
|
|
|
|
|
---
|
|
|
|
|
|
# 缓存系统
|
|
|
---
|
|
|
|
|
|
## task-014
|
|
|
|
|
|
- **ID**:task-014
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 QuoteOrchestrator 主流程(L1→L2→入队)
|
|
|
- **输入**:task-010~013;task-032(L2);BullMQ;Prisma QuoteRecord;技术设计 §2.3
|
|
|
- **输出**:`modules/quote/orchestrator.ts`;`submitQuote(body)` 完整主流程
|
|
|
- **实现范围**:换算 → 校验 → L1 → L2(命中则加价+落库+写L1+返回done)→ 未命中则 INSERT processing + 入队 → 返回 processing;**RPA 失败时委托 task-092 FallbackOrchestrator**
|
|
|
- **验证**:集成测试:L1 命中 → 不触发 RPA;L2 命中 → source=cache + done;首次 → status=processing;TC-104 TC-105;失败路径调用 092
|
|
|
---
|
|
|
|
|
|
# Phase 4 — API 层(里程碑:L1/L2 可通)
|
|
|
|
|
|
> 执行顺序:task-015 → task-016 → task-017 → task-042 → task-043 → task-018
|
|
|
|
|
|
## task-015
|
|
|
|
|
|
- **ID**:task-015
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 POST /api/quotes 路由(薄层)
|
|
|
- **输入**:task-003(鉴权);task-014(orchestrator);技术设计 §4.3
|
|
|
- **输出**:`app/api/quotes/route.ts`;调用 task-096 统一响应;Cache-Control: no-store
|
|
|
- **实现范围**:路由层:鉴权 → 调 orchestrator → 格式化响应;不含业务逻辑
|
|
|
- **验证**:curl POST 正常参数 → 200 + quote_id;缺字段 → 400;无 token → 401
|
|
|
---
|
|
|
|
|
|
## task-016
|
|
|
|
|
|
- **ID**:task-016
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 GET /api/quotes/{quoteId} 路由(轮询 + expired 检测)
|
|
|
- **输入**:task-003;Prisma QuoteRecord;PRD §5.2 §5.3
|
|
|
- **输出**:`app/api/quotes/[quoteId]/route.ts`;valid_until < now → status=expired;越权 → 403
|
|
|
- **实现范围**:DB 查询 + expired 计算 + customer_id 越权校验 + **task-095 confidence_score 赋值**;不含轮询逻辑(客户端实现)
|
|
|
- **验证**:status=done → 完整 QuoteResponse 含 4 档 + confidence_score;rpa/cache=0.95;stale=0.70;expired → status=expired;他人 quote_id → 403(TC-501)
|
|
|
---
|
|
|
|
|
|
## task-017
|
|
|
|
|
|
- **ID**:task-017
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现 GET /api/quotes/history 路由(强制分页)
|
|
|
- **输入**:task-003;Prisma QuoteRecord;PRD §8.5
|
|
|
- **输出**:`app/api/quotes/history/route.ts`;缺 page/size → 400;返回 `{ list, total, page, size }`
|
|
|
- **实现范围**:分页查询;按 customer_id 过滤;不含跨租户
|
|
|
- **验证**:不传 page → 400(TC-506);传 page=1&size=20 → 200 + list
|
|
|
---
|
|
|
|
|
|
## task-042
|
|
|
|
|
|
- **ID**:task-042
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Pricing
|
|
|
- **目标**:实现 GET /api/markup-configs 路由(分页列表)
|
|
|
- **输入**:task-003;Prisma MarkupConfig;技术设计 §4.3
|
|
|
- **输出**:`app/api/markup-configs/route.ts`;分页返回 MarkupConfig 列表
|
|
|
- **实现范围**:鉴权(宿主 token)+ 分页查询;不含写操作
|
|
|
- **验证**:有效 token → 200 + list;无 token → 401
|
|
|
---
|
|
|
|
|
|
## task-043
|
|
|
|
|
|
- **ID**:task-043
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Pricing
|
|
|
- **目标**:实现 PUT /api/markup-configs/{customerId} 路由
|
|
|
- **输入**:task-005(RBAC pricing:markup:write);Prisma MarkupConfig;PRD §6.4 §6.5
|
|
|
- **输出**:`app/api/markup-configs/[customerId]/route.ts`;UPSERT 操作;0~30.0 范围校验
|
|
|
- **实现范围**:权限校验 + 范围校验 + UPSERT + 审计字段(operator_id)
|
|
|
- **验证**:markup=30.0 → 200;markup=30.1 → 400(TC-505);无权限 → 403(TC-502);客户不存在 → 400
|
|
|
---
|
|
|
|
|
|
## task-018
|
|
|
|
|
|
- **ID**:task-018
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:实现超时扫描定时任务(processing >30s → failed)
|
|
|
- **输入**:Prisma QuoteRecord;技术设计 §2.2;PRD §11.4
|
|
|
- **输出**:`workers/scheduler/timeout-sweeper.ts`;每 10s 扫描 processing > 30s 的记录 → UPDATE status=failed + error_code=QUOTE_TIMEOUT
|
|
|
- **实现范围**:每 10s 扫描 processing > 30s → 先尝试 **task-092 L3 降级**;无 L3 则 UPDATE failed + QUOTE_TIMEOUT + 写预警
|
|
|
- **验证**:有 L3 时 31s → done stale;无 L3 → failed QUOTE_TIMEOUT(TC-208 TC-605)
|
|
|
---
|
|
|
|
|
|
# Phase 5.0 — RPA 录制前置(W2 前置,CHG-T11/T12)
|
|
|
|
|
|
> 执行顺序:task-121 → task-122
|
|
|
> **必须在 task-123 之前完成**:无 env 则 adapter/probe 无法验收。
|
|
|
|
|
|
## task-121
|
|
|
|
|
|
- **ID**:task-121
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:Playwright codegen 录制入口脚本
|
|
|
- **输入**:PRD §8.2.3 GD-15;技术设计 §9.2
|
|
|
- **输出**:`scripts/record-mothership.sh`(headed,`RPA_HEADLESS=false`)
|
|
|
- **实现范围**:启动 codegen;禁止 dashboard.mothership.com / www.mothership.com/quote
|
|
|
- **验证**:脚本可执行;录制产出 goto URL 与 selector 可提取
|
|
|
---
|
|
|
|
|
|
## task-122
|
|
|
|
|
|
- **ID**:task-122
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:录制 SOP 与 env 产出 checklist
|
|
|
- **输入**:task-121;PRD §8.2.3 LA→Dallas 标准样例
|
|
|
- **输出**:`docs/rpa-recording-sop.md`;URL→`MOTHERSHIP_QUOTE_URLS`、selector→`RPA_SELECTOR_*` 映射表
|
|
|
- **实现范围**:文档化人工录制流程;不含自动化提取工具
|
|
|
- **验证**:人工 checklist:env 填齐 8 个 selector 后可跑 probe
|
|
|
---
|
|
|
|
|
|
|
|
|
# Phase 5 — RPA 基线(W2,已完成)
|
|
|
|
|
|
> 执行顺序:task-020 → … → task-100(**全部已完成**;v1.2 增量见下方 Phase 5 v1.2 重构)
|
|
|
|
|
|
## task-020
|
|
|
|
|
|
- **ID**:task-020
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 QuoteProvider 接口定义
|
|
|
- **输入**:技术设计 §7.1;PRD 附录 A
|
|
|
- **输出**:`modules/providers/quote-provider.ts`;TypeScript interface QuoteProvider + QuoteItem + QuoteRequest
|
|
|
- **实现范围**:仅类型定义;items.length === 4 约束注释
|
|
|
- **验证**:TypeScript 编译无报错;MothershipRPAProvider 实现该接口不报错
|
|
|
---
|
|
|
|
|
|
## task-021
|
|
|
|
|
|
- **ID**:task-021
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 BullMQ 队列配置(quote-rpa 队列)
|
|
|
- **输入**:技术设计 §7.4;BullMQ ^5.x
|
|
|
- **输出**:`workers/rpa/index.ts` 消费入口;队列名=quote-rpa;并发=2;单job超时=25s;重试=1次
|
|
|
- **实现范围**:Worker 创建 + 连接 Redis;不含 RPA 业务逻辑
|
|
|
- **验证**:Worker 启动无报错;向队列推入 job → Worker 消费到
|
|
|
---
|
|
|
|
|
|
## task-022
|
|
|
|
|
|
- **ID**:task-022
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 session-manager.ts(Mothership 登录态维护)
|
|
|
- **输入**:技术设计 §7.2;Playwright
|
|
|
- **输出**:`workers/rpa/session-manager.ts`;`getSession()` 返回已登录 browser context;SESSION_EXPIRED 时重登1次
|
|
|
- **实现范围**:浏览器常驻 context + 登录状态检测 + 自动重登;不含报价抓取
|
|
|
- **验证**:冷启动 → 自动登录;session 过期 → 重登成功;重登失败 → 抛 SESSION_EXPIRED
|
|
|
- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接
|
|
|
|
|
|
---
|
|
|
|
|
|
## task-023
|
|
|
|
|
|
- **ID**:task-023
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 preCheck()(页面健康检查)
|
|
|
- **输入**:技术设计 §7.2;Playwright
|
|
|
- **输出**:`workers/rpa/mothership.ts` 中 `preCheck()` 方法;检测 URL 非 login/captcha;关键表单 selector 存在
|
|
|
- **实现范围**:仅检测,不填表单;失败抛对应错误分类
|
|
|
- **验证**:正常页面 → true;被重定向到 /login → SESSION_EXPIRED;验证码页 → RPA_CAPTCHA;selector 消失 → STRUCT_CHANGE
|
|
|
- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接
|
|
|
|
|
|
---
|
|
|
|
|
|
## task-024
|
|
|
|
|
|
- **ID**:task-024
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现地址联想点选逻辑(方案 A:place_id;方案 B:联想下拉)
|
|
|
- **输入**:PRD §4.2.1;技术设计 §7.2.1;GD-9
|
|
|
- **输出**:`workers/rpa/mothership.ts` 中 `fillAddress()` 方法;方案 A 优先;方案 B 备用
|
|
|
- **实现范围**:输入街道 → waitForSelector 联想 → click 匹配项 → 断言锁定(联想消失);锁定失败 → ADDRESS_SUGGESTION_NOT_FOUND
|
|
|
- **验证**:正常地址 → 表单锁定;联想不出现 → 抛 ADDRESS_SUGGESTION_NOT_FOUND;超5s → timeout
|
|
|
---
|
|
|
|
|
|
## task-025
|
|
|
|
|
|
- **ID**:task-025
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现表单填写(weight/dims/pallet_count/cargo_type)
|
|
|
- **输入**:PRD §4.2.2~4.2.4;Playwright;数据为 lb/in(已换算)
|
|
|
- **输出**:`workers/rpa/mothership.ts` 中 `fillCargo()` 方法
|
|
|
- **实现范围**:填 pallet_count(映射 Mothership Pallet quantity)+ weight_lb + dims_in + cargo_type;不含地址
|
|
|
- **验证**:录制回放验证字段填入正确;pallet_count=2 → Mothership quantity=2 Pallet
|
|
|
---
|
|
|
|
|
|
## task-026
|
|
|
|
|
|
- **ID**:task-026
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现四档报价抓取(standard/guaranteed × lowest/fastest)
|
|
|
- **输入**:技术设计 §7.2 步骤4~7;Playwright
|
|
|
- **输出**:`workers/rpa/mothership.ts` 中 `scrapeQuotes()` 方法;返回 QuoteItem[4]
|
|
|
- **实现范围**:提交一次 → 在结果页分别点 standard/guaranteed → 各切换 lowest/fastest → 抓 rawFreight + transitDays;严禁多次提交
|
|
|
- **验证**:实测返回 items.length===4;每档 rawFreight>0;transitDays 非空
|
|
|
---
|
|
|
|
|
|
## task-027
|
|
|
|
|
|
- **ID**:task-027
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 RPA 数据一致性校验
|
|
|
- **输入**:PRD §8.2;技术设计 §7.2
|
|
|
- **输出**:`workers/rpa/mothership.ts` 中 `validateResult()` 方法
|
|
|
- **实现范围**:4档均存在校验;每档 rawFreight>0;transitDays 非空;失败 → 抛 RPA_DATA_INVALID
|
|
|
- **验证**:Vitest:3档数据 → RPA_DATA_INVALID;price=0 → RPA_DATA_INVALID;price=-1 → RPA_DATA_INVALID
|
|
|
---
|
|
|
|
|
|
## task-028
|
|
|
|
|
|
- **ID**:task-028
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 RPA 错误分类处理与熔断机制
|
|
|
- **输入**:技术设计 §7.3;PRD §11.5;task-036(熔断 Redis key)
|
|
|
- **输出**:`workers/rpa/mothership.ts` 错误处理逻辑;连续失败≥3次 → 写 circuit:rpa=open 10min
|
|
|
- **实现范围**:错误分类(CAPTCHA→暂停Worker;STRUCT_CHANGE→不重试;SESSION_EXPIRED→重登1次)+ 熔断计数
|
|
|
- **验证**:连续3次失败 → Redis circuit:rpa=open;熔断期间 Worker 不消费新 job(TC-209)
|
|
|
- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接
|
|
|
|
|
|
---
|
|
|
|
|
|
## task-110
|
|
|
|
|
|
- **ID**:task-110
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 RPA 业务错误码映射(CARRIER_NO_CAPACITY / ADDRESS_NOT_SUPPORTED)
|
|
|
- **输入**:PRD §5.6
|
|
|
- **输出**:`modules/rpa/error-mapper.ts`;Mothership 页面文案 → 对应 error_code
|
|
|
- **实现范围**:RPA 抓取阶段识别无运力/邮编不支持;映射后走 092 或 failed
|
|
|
- **验证**:mock 无运力页 → error_code=CARRIER_NO_CAPACITY;不支持邮编 → ADDRESS_NOT_SUPPORTED
|
|
|
- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接
|
|
|
|
|
|
---
|
|
|
|
|
|
## task-029
|
|
|
|
|
|
- **ID**:task-029
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 RPA 成功后写缓存 + 落库 + 偏差检测编排
|
|
|
- **输入**:task-026;task-032(L2);task-033(L3);task-031(L1);Prisma QuoteRecord;task-051(偏差检测)
|
|
|
- **输出**:`workers/rpa/index.ts` 中 job 完成处理逻辑
|
|
|
- **实现范围**:RPA成功 → 写L2(3min+jitter)+L3(30min) → UPDATE QuoteRecord(done) → 更新 quote_cache_meta → 偏差检测 → 写L1;**RPA失败 → 调用 task-092**
|
|
|
- **验证**:集成测试:RPA 成功后 Redis L2/L3 有值;DB source_type=rpa;偏差≥5%时 alert_log 有 PRICE_DEVIATION;失败时 092 被调用
|
|
|
---
|
|
|
|
|
|
## task-093
|
|
|
|
|
|
- **ID**:task-093
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 GET /api/rpa/status(Worker/队列/熔断状态)
|
|
|
- **输入**:PRD §3.1 管理端 RPA 进度;技术设计 §4.2 admin rpa:operate
|
|
|
- **输出**:`app/api/rpa/status/route.ts`;返回 `{ workers[], queueDepth, circuitOpen, successRate24h }`
|
|
|
- **实现范围**:读 Redis circuit + BullMQ 队列 + Worker 心跳;admin + rpa:operate 权限
|
|
|
- **验证**:admin JWT → 200 + 字段非空;宿主 token → 403;熔断时 circuitOpen=true
|
|
|
---
|
|
|
|
|
|
## task-099
|
|
|
|
|
|
- **ID**:task-099
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 POST /api/rpa/worker/pause(管理员手动暂停 Worker)
|
|
|
- **输入**:PRD §2.4「暂停 RPA Worker」;task-005 rpa:operate
|
|
|
- **输出**:`app/api/rpa/worker/pause/route.ts`;body `{ worker_id, duration_minutes }`;写 Redis worker:pause:{id}
|
|
|
- **实现范围**:admin 手动暂停;不含自动 CAPTCHA 暂停(task-054)
|
|
|
- **验证**:admin 调用 → Worker 指定时间内不接 job;非 admin → 403
|
|
|
---
|
|
|
|
|
|
## task-100
|
|
|
|
|
|
- **ID**:task-100
|
|
|
- **状态**:已完成
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 POST /api/quotes/manual-fallback(人工兜底录入)
|
|
|
- **输入**:PRD §2.4「触发人工兜底录入」
|
|
|
- **输出**:`app/api/quotes/manual-fallback/route.ts`;admin 录入四档原始价 → 写 L3 + UPDATE quote done(stale)
|
|
|
- **实现范围**:仅 admin;校验四档 + 金额>0;写 audit_log + alert_log(MANUAL_FALLBACK)
|
|
|
- **验证**:admin 提交合法四档 → quote status=done source=stale;非 admin → 403
|
|
|
---
|
|
|
|
|
|
|
|
|
# Phase 5 — RPA v1.2 重构(PRD v0.6)
|
|
|
|
|
|
> 执行顺序:task-123 → task-124 → task-125 → task-126 → task-127 → task-128 → task-129 → task-130 → task-131
|
|
|
> **里程碑 M2(v1.2)**:`RPA_MOCK_MODE=false` 下 `scripts/probe-rpa.ts` 连续 3 次 exit 0,四档 `source_type=rpa`。
|
|
|
|
|
|
## task-123
|
|
|
|
|
|
- **ID**:task-123
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:实现 QuotePageAdapter 多 URL 探测
|
|
|
- **输入**:技术设计 §7.2.3 CHG-T05/T06;task-122
|
|
|
- **输出**:`workers/rpa/quote-page-adapter.ts`:`resolveQuoteEntry` / `preCheck` / `isLoginWithoutCredentials`
|
|
|
- **实现范围**:按 `MOTHERSHIP_QUOTE_URLS` 顺序 goto + preCheck;全失败 → QUOTE_ENTRY_UNAVAILABLE
|
|
|
- **验证**:单测 mock 多 URL;首个可用 URL 写入 activeUrl;全失败抛 QUOTE_ENTRY_UNAVAILABLE
|
|
|
---
|
|
|
|
|
|
## task-124
|
|
|
|
|
|
- **ID**:task-124
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:storageState 生命周期(§7.5)
|
|
|
- **输入**:技术设计 §7.5 CHG-T09;task-120
|
|
|
- **输出**:重构 `workers/rpa/session-manager.ts`:`createContext` load、`persistContext` writeback、`invalidateStorageState`
|
|
|
- **实现范围**:成功查价写回;login 无凭据先 invalidate 再重试 goto
|
|
|
- **验证**:单测:存在 state 文件时 load;成功后 persist;invalidate 删除文件
|
|
|
---
|
|
|
|
|
|
## task-125
|
|
|
|
|
|
- **ID**:task-125
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:地址联想 selector env 化
|
|
|
- **输入**:技术设计 §7.2.1 CHG-T07;task-123
|
|
|
- **输出**:`workers/rpa/mothership.ts` `fillAddress` 改用 `RPA_SELECTOR_PICKUP/DELIVERY/ADDRESS_SUGGESTION`
|
|
|
- **实现范围**:失败 → ADDRESS_SUGGESTION_NOT_FOUND;不重试
|
|
|
- **验证**:mock 页面:联想成功锁定;无下拉 → ADDRESS_SUGGESTION_NOT_FOUND
|
|
|
---
|
|
|
|
|
|
## task-126
|
|
|
|
|
|
- **ID**:task-126
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:表单填写与提交 selector env 化
|
|
|
- **输入**:技术设计 §7.2 CHG-T07;task-125
|
|
|
- **输出**:`fillCargo` + submit 改用 `RPA_SELECTOR_SUBMIT` 等 env
|
|
|
- **实现范围**:移除硬编码 data-testid;全部读 `process.env.RPA_SELECTOR_*`
|
|
|
- **验证**:录制回放:字段填入正确;submit 点击成功
|
|
|
---
|
|
|
|
|
|
## task-127
|
|
|
|
|
|
- **ID**:task-127
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:四档抓取 selector env 化
|
|
|
- **输入**:技术设计 §7.2 步骤 7 CHG-T07;task-126
|
|
|
- **输出**:`scrapeQuotes` 改用 `RPA_SELECTOR_STANDARD/GUARANTEED/LOWEST/FASTEST/PRICE`
|
|
|
- **实现范围**:standard/guaranteed × lowest/fastest 各抓取一次;items.length===4
|
|
|
- **验证**:mock 结果页返回 4 档;每档 rawFreight>0
|
|
|
---
|
|
|
|
|
|
## task-128
|
|
|
|
|
|
- **ID**:task-128
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:错误分类 v0.6(入口 vs 抓取)
|
|
|
- **输入**:PRD §5.6 §11.5 CHG-P03/P04;技术设计 §7.3 CHG-T08
|
|
|
- **输出**:`workers/rpa/error-mapper.ts` + job-handler:QUOTE_ENTRY_UNAVAILABLE;login 无凭据 → STRUCT_CHANGE
|
|
|
- **实现范围**:入口/地址错误不重试;SESSION_EXPIRED 仅已配置凭据且曾成功登录
|
|
|
- **验证**:单测各错误分支映射;QUOTE_ENTRY_UNAVAILABLE 不触发 job 重试
|
|
|
---
|
|
|
|
|
|
## task-129
|
|
|
|
|
|
- **ID**:task-129
|
|
|
- **状态**:待执行
|
|
|
- **模块**:Quote Engine
|
|
|
- **目标**:Fallback 入口错误处理
|
|
|
- **输入**:PRD §2.5 CHG-P03;task-128
|
|
|
- **输出**:增补 `modules/quote/fallback-orchestrator.ts`:入口类错误不消耗 RPA job 重试配额
|
|
|
- **实现范围**:QUOTE_ENTRY_UNAVAILABLE / ADDRESS_SUGGESTION_NOT_FOUND 直接走 L3 或 400
|
|
|
- **验证**:单测:入口错误 job attempts=1;PAGE_LOAD_TIMEOUT 仍可重试 1 次
|
|
|
---
|
|
|
|
|
|
## task-130
|
|
|
|
|
|
- **ID**:task-130
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:升级 probe 探针(连续 3 次 exit 0)
|
|
|
- **输入**:PRD TC-601 §12.7 CHG-P08/P09;技术设计 CHG-T02
|
|
|
- **输出**:升级 `scripts/probe-rpa.ts` + `scripts/probe-rpa-3x.sh`;断言 items.length===4
|
|
|
- **实现范围**:`RPA_MOCK_MODE=false` 真实探测;3x 脚本循环调用
|
|
|
- **验证**:`bash scripts/probe-rpa-3x.sh` 连续 3 次 exit 0;输出 4 档 JSON
|
|
|
---
|
|
|
|
|
|
## task-131
|
|
|
|
|
|
- **ID**:task-131
|
|
|
- **状态**:待执行
|
|
|
- **模块**:RPA
|
|
|
- **目标**:Patchright 可选驱动(P1)
|
|
|
- **输入**:技术设计 §6.2 `RPA_USE_PATCHRIGHT` CHG-T03
|
|
|
- **输出**:`workers/rpa/session-manager.ts` 支持 `RPA_USE_PATCHRIGHT=true` 切换浏览器
|
|
|
- **实现范围**:默认 Playwright;Patchright 为可选降级路径
|
|
|
- **验证**:`RPA_USE_PATCHRIGHT=true` 时 worker 启动无报错;false 时行为不变
|
|
|
---
|
|
|
|
|
|
# Phase 6 — 预警 / 审计 / 管理 API
|
|
|
|
|
|
> 执行顺序:task-050 → task-051 → task-052 → task-053 → task-054 → task-055 → task-094 → task-105 → task-056 → task-057
|
|
|
|
|
|
## task-050
|
|
|
|
|
|
- **ID**:task-050
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 AlertService.writeAlert() 通用写入函数
|
|
|
- **输入**:Prisma AlertLog;PRD §7.7
|
|
|
- **输出**:`modules/alert/service.ts`;`writeAlert(type, quoteId?, cargoHash?, detail?)` 异步写 alert_log
|
|
|
- **实现范围**:INSERT alert_log;失败时仅 log error,不影响主流程(try-catch)
|
|
|
- **验证**:Vitest:调用后 DB alert_log 新增1条;DB 写失败 → 不抛出异常,主流程继续
|
|
|
---
|
|
|
|
|
|
## task-051
|
|
|
|
|
|
- **ID**:task-051
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现偏差检测逻辑(与 quote_cache_meta 比对)
|
|
|
- **输入**:task-050;Prisma QuoteCacheMeta;PRD §7.5
|
|
|
- **输出**:`modules/alert/deviation-detector.ts`;`detectDeviation(cargoHash, newRawTotals)` → 偏差≥5%时写 PRICE_DEVIATION
|
|
|
- **实现范围**:abs(new-prev)/prev ≥0.05 触发;首次无基准不触发;触发后更新 quote_cache_meta
|
|
|
- **验证**:TC-210:prev=352,new=370(5.1%)→ PRICE_DEVIATION;TC-211:偏差4.9%→不触发;TC-212:首次→不触发
|
|
|
---
|
|
|
|
|
|
## task-052
|
|
|
|
|
|
- **ID**:task-052
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 STALE_FALLBACK 预警(L3 降级时写入)
|
|
|
- **输入**:task-050;PRD §7.7
|
|
|
- **输出**:在 Fallback 流程中调用 `writeAlert('STALE_FALLBACK', quoteId, cargoHash)`
|
|
|
- **实现范围**:仅在 L3 命中路径写入;不含通知机制(站内仅DB)
|
|
|
- **验证**:L3 命中路径 → alert_log 新增 STALE_FALLBACK(TC-205)
|
|
|
---
|
|
|
|
|
|
## task-053
|
|
|
|
|
|
- **ID**:task-053
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 RPA_FAILED 预警(RPA失败无L3时写入)
|
|
|
- **输入**:task-050;PRD §7.7
|
|
|
- **输出**:在 Fallback 无L3路径中调用 `writeAlert('RPA_FAILED', quoteId, cargoHash)`
|
|
|
- **实现范围**:仅在 L3 miss 路径写入
|
|
|
- **验证**:L3 miss + RPA失败 → alert_log 新增 RPA_FAILED(TC-206)
|
|
|
---
|
|
|
|
|
|
## task-054
|
|
|
|
|
|
- **ID**:task-054
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 RPA_CAPTCHA 预警 + Worker 暂停
|
|
|
- **输入**:task-050;task-036(熔断);PRD §11.5
|
|
|
- **输出**:RPA_CAPTCHA 错误处理:写 alert_log + 暂停当前 Worker 10min
|
|
|
- **实现范围**:写预警 + Redis 标记 Worker 暂停状态;不含多 Worker 调度
|
|
|
- **验证**:RPA 触发验证码 → alert_log RPA_CAPTCHA + Worker 10分钟内不接新 job(TC-603)
|
|
|
---
|
|
|
|
|
|
## task-055
|
|
|
|
|
|
- **ID**:task-055
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 STRUCT_CHANGE 预警写入
|
|
|
- **输入**:task-050;PRD §11.5
|
|
|
- **输出**:STRUCT_CHANGE 错误处理:写 alert_log;preCheck 失败触发
|
|
|
- **实现范围**:仅写预警;不含 RPA 重试(不重试)
|
|
|
- **验证**:preCheck 失败 → alert_log STRUCT_CHANGE(TC-604)
|
|
|
---
|
|
|
|
|
|
## task-094
|
|
|
|
|
|
- **ID**:task-094
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert / DB
|
|
|
- **目标**:实现 audit_log 表 + AuditService 写入
|
|
|
- **输入**:PRD §10.6 §11.9;task-070 schema 含 AuditLog
|
|
|
- **输出**:`modules/audit/service.ts`;`writeAudit(action, actorId, resource, detail)` → audit_log 表
|
|
|
- **实现范围**:越权/注入/敏感操作写入;失败 log 不阻断主流程
|
|
|
- **验证**:TC-501 越权后 audit_log 有记录;TC-504 注入尝试有 SECURITY audit
|
|
|
---
|
|
|
|
|
|
## task-105
|
|
|
|
|
|
- **ID**:task-105
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 SESSION_EXPIRED / SECURITY 预警类型写入
|
|
|
- **输入**:PRD §11.5 §11.9;附录 C alert_type
|
|
|
- **输出**:在 session 过期路径写 SESSION_EXPIRED;IP 封禁/注入写 SECURITY alert
|
|
|
- **实现范围**:扩展 task-050 writeAlert 支持新 type;不含通知推送
|
|
|
- **验证**:SESSION_EXPIRED 触发 → alert_log 有记录;IP 超限 → SECURITY alert
|
|
|
|
|
|
---
|
|
|
|
|
|
# 观测与指标(Metrics)
|
|
|
---
|
|
|
|
|
|
## task-056
|
|
|
|
|
|
- **ID**:task-056
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 GET /api/alerts 路由(分页,筛选,admin 权限)
|
|
|
- **输入**:task-005(alert:read);Prisma AlertLog;PRD §8.5
|
|
|
- **输出**:`app/api/alerts/route.ts`;支持 type/status 筛选;强制分页;admin 权限
|
|
|
- **实现范围**:鉴权 + 分页 + 筛选;不含处理操作
|
|
|
- **验证**:admin JWT + type=RPA_FAILED → 仅返回该类型;宿主 token → 403
|
|
|
---
|
|
|
|
|
|
## task-057
|
|
|
|
|
|
- **ID**:task-057
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Alert
|
|
|
- **目标**:实现 POST /api/alerts/{id}/resolve 路由
|
|
|
- **输入**:task-003;Prisma AlertLog;PRD §8.5
|
|
|
- **输出**:`app/api/alerts/[id]/resolve/route.ts`;UPDATE status=resolved + resolver_id + resolved_at
|
|
|
- **实现范围**:鉴权(admin)+ DB 更新;不含通知
|
|
|
- **验证**:admin 调用 → 200 + status=resolved;非 admin → 403;不存在 id → 404
|
|
|
---
|
|
|
|
|
|
# Phase 7 — 观测指标
|
|
|
|
|
|
> 执行顺序:task-101 → task-102
|
|
|
|
|
|
## task-101
|
|
|
|
|
|
- **ID**:task-101
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Metrics
|
|
|
- **目标**:实现 MetricsCollector(GD-4 三率 + 缓存命中率)
|
|
|
- **输入**:PRD §10.2 GD-4 §12.7
|
|
|
- **输出**:`modules/metrics/collector.ts`;Redis 计数器:`api_success_rate` / `rpa_success_rate` / `realtime_rate` / `cache_hit_rate`
|
|
|
- **实现范围**:POST/GET quotes 与 RPA 回调处埋点;不含可视化
|
|
|
- **验证**:100 次 mock 请求后 GET 内部指标 API 可算出三率;公式与 PRD 一致
|
|
|
---
|
|
|
|
|
|
## task-102
|
|
|
|
|
|
- **ID**:task-102
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Metrics
|
|
|
- **目标**:实现 GET /api/metrics/dashboard(admin 汇总指标)
|
|
|
- **输入**:task-101;PRD §12.7 Go-No-Go
|
|
|
- **输出**:`app/api/metrics/dashboard/route.ts`;返回三率、P95、队列深度、未处理预警数
|
|
|
- **实现范围**:admin 只读;供 /admin/dashboard 消费
|
|
|
- **验证**:admin → 200 + 含 GD-4 四指标;非 admin → 403
|
|
|
|
|
|
---
|
|
|
|
|
|
# 前端 UI 系统
|
|
|
---
|
|
|
|
|
|
# Phase 8 — 前端基础与嵌入组件
|
|
|
|
|
|
> 执行顺序:task-060 → task-061 → task-062 → task-065 → task-066 → task-067 → task-068 → task-069 → task-106
|
|
|
|
|
|
## task-060
|
|
|
|
|
|
- **ID**:task-060
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现项目基础配置(Tailwind + CSS 变量 + 字体)
|
|
|
- **输入**:UI设计 §3.1~3.5 §4.4 §4.5
|
|
|
- **输出**:`app/globals.css`(CSS 变量);`tailwind.config.ts`(primary=teal-600);Geist 字体引入
|
|
|
- **实现范围**:仅样式基础配置;不含组件
|
|
|
- **验证**:`--color-primary: #0D9488` 生效;Geist Sans 字体加载
|
|
|
---
|
|
|
|
|
|
## task-061
|
|
|
|
|
|
- **ID**:task-061
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现基础 UI 组件库(7个通用组件)
|
|
|
- **输入**:UI设计 §3.6 §4.1
|
|
|
- **输出**:`components/ui/` 下:PrimaryButton / InputField / SelectField / LoadingSpinner / Skeleton / ErrorBanner / WarningBanner
|
|
|
- **实现范围**:各组件含 loading/disabled/error 状态;phosphor-icons;UI-01~UI-11 规则
|
|
|
- **验证**:Storybook/手动验证:每个组件 loading 状态 + error 状态正常渲染
|
|
|
---
|
|
|
|
|
|
## task-062
|
|
|
|
|
|
- **ID**:task-062
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现布局组件(AppLayout + AdminLayout)
|
|
|
- **输入**:UI设计 §1.4 §4.6
|
|
|
- **输出**:`components/layout/app-layout.tsx` + `components/layout/admin-layout.tsx`
|
|
|
- **实现范围**:顶栏导航 + 响应式侧栏;未登录 → 重定向 /login
|
|
|
- **验证**:客户路由使用 AppLayout 正确渲染;管理端路由使用 AdminLayout;移动端单列布局
|
|
|
---
|
|
|
|
|
|
## task-065
|
|
|
|
|
|
- **ID**:task-065
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 QuoteForm 组件(查价表单)
|
|
|
- **输入**:UI设计 §2.4 表单字段布局;PRD §4.2;react-hook-form + zod
|
|
|
- **输出**:`components/quote/quote-form.tsx`;含地址、单托重量/尺寸、托盘数、货物类型;**display_unit 公制/英制切换(PRD §4.2.7,仅 UI)**
|
|
|
- **实现范围**:表单字段 + onBlur 校验 + display_unit 切换;提交仍按 weight.unit/dimensions.unit
|
|
|
- **验证**:zip=9001 → 「邮编格式无效」;pallet_count=11 → 超范围;kg/cm 切换 UI 正确;selected_from_suggestions=false → 「请从地址列表中选择」
|
|
|
---
|
|
|
|
|
|
## task-066
|
|
|
|
|
|
- **ID**:task-066
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 useQuotePolling hook(2s 轮询 + 网络退避,§11.3)
|
|
|
- **输入**:UI设计 §2.4;PRD GD-5 §11.3
|
|
|
- **输出**:`hooks/use-quote-polling.ts`;每 2s GET;最多 15 次;**响应丢失时指数退避重试 GET**
|
|
|
- **实现范围**:轮询 + 退避逻辑;不含 UI 渲染
|
|
|
- **验证**:mock 前 3 次网络失败第 4 次成功 → 最终得 done;15 次仍 processing → QUOTE_TIMEOUT
|
|
|
---
|
|
|
|
|
|
## task-067
|
|
|
|
|
|
- **ID**:task-067
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 QuoteCard 组件(四档报价展示)
|
|
|
- **输入**:UI设计 §3.6 Card;PRD §5.1 §5.4
|
|
|
- **输出**:`components/quote/quote-card.tsx`;Tab:standard/guaranteed;子Tab:lowest/fastest;价格 font-mono;is_realtime=false → WarningBanner角标
|
|
|
- **实现范围**:Tab 切换 + 价格明细 breakdown 展示 + 非实时角标;不含倒计时
|
|
|
- **验证**:4档数据渲染正确;is_realtime=false → WarningBanner 显示(UI-06);金额 font-mono $1,234.56 格式
|
|
|
---
|
|
|
|
|
|
## task-068
|
|
|
|
|
|
- **ID**:task-068
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 CountdownTimer 组件(3分钟倒计时)
|
|
|
- **输入**:UI设计 §2.4;PRD §7.4
|
|
|
- **输出**:`components/quote/countdown-timer.tsx`;<30s 时文字变 Warning 色;归零 → ConfirmDialog
|
|
|
- **实现范围**:倒计时逻辑 + 颜色变化 + 过期确认弹窗
|
|
|
- **验证**:valid_until 传入 → 正确倒计时;<30s → warning 色;归零 → ConfirmDialog 弹出(UI-05)
|
|
|
---
|
|
|
|
|
|
## task-069
|
|
|
|
|
|
- **ID**:task-069
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 QuoteResultPanel(7种状态容器)
|
|
|
- **输入**:UI设计 §2.4 页面状态机;task-067 task-068
|
|
|
- **输出**:`components/quote/quote-result-panel.tsx`;根据 QuotePageStatus 渲染对应 UI
|
|
|
- **实现范围**:idle→插画;processing→Skeleton+进度条;success/fallback→QuoteCard+CountdownTimer;error→ErrorBanner;expired→ConfirmDialog
|
|
|
- **验证**:7种状态逐一切换,UI 对应正确
|
|
|
---
|
|
|
|
|
|
## task-106
|
|
|
|
|
|
- **ID**:task-106
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现宿主内嵌查价组件 EmbeddedQuoteWidget(GD-11)
|
|
|
- **输入**:task-065~069;task-015/016;PRD §2.2 §3.1;技术设计 §8.1
|
|
|
- **输出**:`components/embed/embedded-quote-widget.tsx` + `packages/embed/` 或 `public/embed.js`;props:`serviceToken, customerId, apiBaseUrl`
|
|
|
- **实现范围**:QuoteForm + QuoteResultPanel + 轮询 + 防重复提交;**无独立 /quote 路由**;供宿主 iframe/script 嵌入
|
|
|
- **验证**:demo 宿主页嵌入 → 完整查价流程;TC-101 UI;POST 带宿主 token + customer_id
|
|
|
---
|
|
|
|
|
|
# Phase 8 — 管理端页面
|
|
|
|
|
|
> 执行顺序:task-063 → task-064 → task-079 → task-103 → task-104 → task-080
|
|
|
|
|
|
## task-063
|
|
|
|
|
|
- **ID**:task-063
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 P01 登录页 /login
|
|
|
- **输入**:UI设计 §2.2;task-002(login API)
|
|
|
- **输出**:`app/login/page.tsx`;状态机:idle/loading/error/success;调 POST /api/auth/login
|
|
|
- **实现范围**:表单 + 状态机 + 跳转 /dashboard;不含注册
|
|
|
- **验证**:正确凭据 → 跳转 /dashboard;错误密码 → ErrorBanner「用户名或密码错误」;loading 时按钮 disabled
|
|
|
---
|
|
|
|
|
|
## task-064
|
|
|
|
|
|
- **ID**:task-064
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 P02 工作台 /dashboard(**仅 admin**,GD-11)
|
|
|
- **输入**:UI设计 §2.3(admin 部分);PRD GD-11
|
|
|
- **输出**:`app/dashboard/page.tsx`;快捷入口:预警中心、RPA 状态、指标看板;未处理预警角标
|
|
|
- **实现范围**:**不含 customer/operator 卡片**;admin 登录后默认页
|
|
|
- **验证**:admin → 显示「预警中心」「RPA 状态」「指标看板」;无 customer 入口
|
|
|
---
|
|
|
|
|
|
## task-079
|
|
|
|
|
|
- **ID**:task-079
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 P07 预警中心页 /admin/alerts(含管理操作)
|
|
|
- **输入**:UI设计 §2.8;task-056 task-057 task-099 task-100
|
|
|
- **输出**:`app/admin/alerts/page.tsx`;筛选 + DataTable + Drawer + **标记已处理 / 跳转 RPA 暂停 / 人工兜底录入入口**
|
|
|
- **实现范围**:类型颜色标签 + 分页 + 处理按钮 + 链到 manual-fallback
|
|
|
- **验证**:RPA_FAILED → Error 标签;处理后 resolved;可跳转暂停 Worker 或人工录入
|
|
|
---
|
|
|
|
|
|
## task-103
|
|
|
|
|
|
- **ID**:task-103
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 /admin/rpa RPA 状态与开发进度页
|
|
|
- **输入**:PRD §3.1 管理端 RPA;task-093
|
|
|
- **输出**:`app/admin/rpa/page.tsx`;展示 Worker 状态、队列深度、成功率、熔断状态;**暂停 Worker 按钮(调 task-099)**
|
|
|
- **实现范围**:只读 + 暂停操作;不含 Worker 代码
|
|
|
- **验证**:admin → 页面渲染指标;点击暂停 → Worker 停止接 job;非 admin → 403
|
|
|
---
|
|
|
|
|
|
## task-104
|
|
|
|
|
|
- **ID**:task-104
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 /admin/dashboard 指标汇总页
|
|
|
- **输入**:task-102;PRD §12.7 Go-No-Go
|
|
|
- **输出**:`app/admin/dashboard/page.tsx`;展示 GD-4 三率、缓存命中率、P95、未处理预警
|
|
|
- **实现范围**:只读 dashboard;数据来自 GET /api/metrics/dashboard
|
|
|
- **验证**:admin → 四指标卡片有值;低于阈值时 Warning 色
|
|
|
---
|
|
|
|
|
|
## task-132
|
|
|
|
|
|
- **ID**:task-132
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 /admin/queues Bull Board 队列监控页(P1)
|
|
|
- **输入**:技术设计 §8.2 CHG-T10;task-021
|
|
|
- **输出**:`app/admin/queues/page.tsx` + Bull Board 挂载;admin RBAC
|
|
|
- **实现范围**:展示 quote-rpa 队列深度、失败 job、延迟;文案全中文
|
|
|
- **验证**:admin → 页面渲染队列指标;非 admin → 403
|
|
|
---
|
|
|
|
|
|
## task-080
|
|
|
|
|
|
- **ID**:task-080
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Frontend
|
|
|
- **目标**:实现 P08/P09 错误页(403/404)
|
|
|
- **输入**:UI设计 §1.1
|
|
|
- **输出**:`app/403/page.tsx` + `app/not-found.tsx`;含返回按钮
|
|
|
- **实现范围**:静态错误页
|
|
|
- **验证**:访问 /403 → 显示「无访问权限」;未知路由 → 显示「页面不存在」
|
|
|
|
|
|
---
|
|
|
|
|
|
# 测试系统(Test)
|
|
|
---
|
|
|
|
|
|
# 已废止(v1.1 不实施)
|
|
|
|
|
|
> 执行顺序:task-075 → task-076 → task-077 → task-078
|
|
|
|
|
|
## ~~task-075~~(v1.1 已废止)
|
|
|
|
|
|
- **ID**:task-075
|
|
|
- **状态**:**已废止** — 由 task-106 替代(PRD GD-11 宿主嵌入,不做独立 `/quote` 页)
|
|
|
- **原目标**:独立查价页 /quote
|
|
|
---
|
|
|
|
|
|
## ~~task-076~~(v1.1 已废止)
|
|
|
|
|
|
- **ID**:task-076
|
|
|
- **状态**:**已废止** — 嵌入组件内展示结果,不单独 `/quote/result/[id]` 页
|
|
|
---
|
|
|
|
|
|
## ~~task-077~~(v1.1 已废止)
|
|
|
|
|
|
- **ID**:task-077
|
|
|
- **状态**:**已废止** — 询价历史由宿主系统调用 GET /api/quotes/history,中台不提供 C 端历史页
|
|
|
---
|
|
|
|
|
|
## ~~task-078~~(v1.1 已废止)
|
|
|
|
|
|
- **ID**:task-078
|
|
|
- **状态**:**已废止** — PRD §2.3 禁止中台运营加价 UI;加价由宿主调 PUT /api/markup-configs
|
|
|
---
|
|
|
|
|
|
# Phase 9 — 测试与 CI(建议随模块并行,收尾全量回归)
|
|
|
|
|
|
> 执行顺序:task-081 → task-082 → task-083 → task-084 → task-085 → task-086 → task-087 → task-112 → task-113 → task-114 → task-115 → task-116 → task-088 → task-089 → task-090 → task-117 → task-118 → task-091
|
|
|
|
|
|
## task-081
|
|
|
|
|
|
- **ID**:task-081
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现 PricingEngine 单元测试(TC-103)
|
|
|
- **输入**:task-041;PRD §6.3 §12.1 TC-103
|
|
|
- **输出**:`__tests__/pricing/engine.test.ts`;覆盖 raw_freight=320/surcharges=15/markup=10%;含surcharge=0场景;边界值markup=0/30
|
|
|
- **实现范围**:仅 PricingEngine 函数测试
|
|
|
- **验证**:`npm run test:unit` → 全部 pass;markup_amount=32.00,final_total=367.00 精确到分
|
|
|
---
|
|
|
|
|
|
## task-082
|
|
|
|
|
|
- **ID**:task-082
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现 ValidationModule 单元测试(TC-201~204+E4系列)
|
|
|
- **输入**:task-010;PRD §4.5 §12.2
|
|
|
- **输出**:`__tests__/validation/validation.test.ts`;覆盖8类异常(缺字段/超范围/地址未点选/非UUID/超Mothership范围/注入/枚举外/件数字段)
|
|
|
- **实现范围**:仅校验函数测试
|
|
|
- **验证**:全部场景通过;边界值 pallet_count=25通过/26失败;总重249975通过/249976失败
|
|
|
---
|
|
|
|
|
|
## task-083
|
|
|
|
|
|
- **ID**:task-083
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现 cargo_hash + 单位换算 单元测试(TC-106 TC-107)
|
|
|
- **输入**:task-011 task-012
|
|
|
- **输出**:`__tests__/quote/cargo-hash.test.ts` + `__tests__/quote/unit-converter.test.ts`
|
|
|
- **实现范围**:相同货物→相同hash;service_level不同→相同hash;227kg→500.05lb;122cm→48.03in
|
|
|
- **验证**:全部 pass
|
|
|
---
|
|
|
|
|
|
## task-084
|
|
|
|
|
|
- **ID**:task-084
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现 IdempotencyModule 单元测试(TC-105 TC-207 TC-404)
|
|
|
- **输入**:task-013;Testcontainers(Redis)
|
|
|
- **输出**:`__tests__/quote/idempotency.test.ts`;并发10次相同 request_id → 仅1条DB记录
|
|
|
- **实现范围**:Redis mock + DB 事务测试
|
|
|
- **验证**:并发10次 → DB 仅1条;重启后DB兜底返回原 quote_id
|
|
|
---
|
|
|
|
|
|
## task-085
|
|
|
|
|
|
- **ID**:task-085
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现 POST /api/quotes 集成测试(TC-101~108主功能)
|
|
|
- **输入**:task-015;Supertest;Testcontainers(MySQL+Redis)
|
|
|
- **输出**:`__tests__/api/quotes.test.ts`;覆盖正常流程/L2命中/L1幂等/L3降级/失败
|
|
|
- **实现范围**:HTTP 层集成测试(mock RPA)
|
|
|
- **验证**:TC-101~108 全部列于验证行;TC-101(source=rpa);TC-104(cache);TC-105(幂等);TC-106/107(换算/hash);TC-108(L2 TTL 过期,见 task-112);TC-205/206(Fallback)
|
|
|
---
|
|
|
|
|
|
## task-086
|
|
|
|
|
|
- **ID**:task-086
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现安全与权限集成测试(TC-501~506)
|
|
|
- **输入**:task-003 task-005;Supertest
|
|
|
- **输出**:`__tests__/api/security.test.ts`;覆盖越权/RBAC/限流/SQL注入
|
|
|
- **实现范围**:HTTP 层安全测试
|
|
|
- **验证**:TC-501(越权403+**task-094 audit**);TC-502;TC-503(customer+**IP task-035-ip**);TC-504(注入400+SECURITY alert);TC-505;TC-506
|
|
|
---
|
|
|
|
|
|
## task-087
|
|
|
|
|
|
- **ID**:task-087
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现偏差预警集成测试(TC-210~212)
|
|
|
- **输入**:task-051;Prisma QuoteCacheMeta
|
|
|
- **输出**:`__tests__/alert/deviation.test.ts`;覆盖5.1%触发/4.9%不触发/首次不触发
|
|
|
- **实现范围**:仅偏差检测逻辑测试
|
|
|
- **验证**:TC-210/TC-211/TC-212 全部 pass
|
|
|
---
|
|
|
|
|
|
## task-112
|
|
|
|
|
|
- **ID**:task-112
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:TC-108 L2 过期触发新 RPA 集成测试
|
|
|
- **输入**:task-032;task-014
|
|
|
- **输出**:`__tests__/api/l2-expire.test.ts`;mock TTL 过期或手动 DEL L2 后 POST → source=rpa
|
|
|
- **验证**:L2 不存在 → 触发 RPA(mock);source_type=rpa
|
|
|
---
|
|
|
|
|
|
## task-113
|
|
|
|
|
|
- **ID**:task-113
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:TC-401 cache vs RPA 一致性测试
|
|
|
- **输入**:task-032 task-051
|
|
|
- **输出**:`__tests__/api/cache-consistency.test.ts`
|
|
|
- **验证**:L2 raw_total 与写入时 RPA 一致;新 RPA 偏差≥5% 触发 PRICE_DEVIATION
|
|
|
---
|
|
|
|
|
|
## task-114
|
|
|
|
|
|
- **ID**:task-114
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:TC-403 加价可复算 DB 集成测试
|
|
|
- **输入**:task-041
|
|
|
- **输出**:`__tests__/pricing/recalc-from-db.test.ts`
|
|
|
- **验证**:从 DB 读 raw_freight + markup 重算 final_total 误差 ≤0.01
|
|
|
---
|
|
|
|
|
|
## task-115
|
|
|
|
|
|
- **ID**:task-115
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:FallbackOrchestrator 集成测试(TC-205/206/602)
|
|
|
- **输入**:task-092
|
|
|
- **输出**:`__tests__/quote/fallback-orchestrator.test.ts`
|
|
|
- **验证**:有 L3 → stale done + STALE_FALLBACK;无 L3 → failed + RPA_FAILED
|
|
|
---
|
|
|
|
|
|
## task-116
|
|
|
|
|
|
- **ID**:task-116
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:RPA 状态 API + 管理员暂停集成测试
|
|
|
- **输入**:task-093 task-099
|
|
|
- **输出**:`__tests__/api/rpa-admin.test.ts`
|
|
|
- **验证**:GET status 200;POST pause 后 Worker 不接 job
|
|
|
---
|
|
|
|
|
|
## task-088
|
|
|
|
|
|
- **ID**:task-088
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现 RPA Worker E2E 测试(TC-601~605,使用 RPA mock)
|
|
|
- **输入**:task-021~029;Playwright mock;技术设计 §11.1
|
|
|
- **输出**:`e2e/rpa.test.ts`;模拟正常/验证码/DOM变更/超时场景
|
|
|
- **实现范围**:Worker 链路测试(不真实调 Mothership)
|
|
|
- **验证**:TC-601(**quotes=4 档**,非 PRD TC-601 笔误的 2 条);TC-602~605
|
|
|
- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接
|
|
|
|
|
|
---
|
|
|
|
|
|
## task-089
|
|
|
|
|
|
- **ID**:task-089
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现查价页 E2E 测试(Playwright,核心路径)
|
|
|
- **输入**:task-106;Playwright E2E;PRD §12.8
|
|
|
- **输出**:`e2e/quote-flow.test.ts`;5 条核心路径(嵌入组件 demo 页)
|
|
|
- **实现范围**:浏览器级 E2E(mock API 响应)
|
|
|
- **验证**:5条路径全部 pass;processing Skeleton 显示;成功后显示4档Tab;stale 时 WarningBanner 显示
|
|
|
---
|
|
|
|
|
|
## task-090
|
|
|
|
|
|
- **ID**:task-090
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:实现限流性能测试(TC-303 TC-301)
|
|
|
- **输入**:k6;task-035(限流);PRD §12.3
|
|
|
- **输出**:`load-tests/rate-limit.js` + `load-tests/rpa-p95.js` + **`load-tests/l2-p95.js`(TC-302)**
|
|
|
- **实现范围**:customer 60/min;RPA P95<30s;**L2 命中 P95<300ms**
|
|
|
- **验证**:TC-303;TC-301;**TC-302 P95<300ms**
|
|
|
---
|
|
|
|
|
|
## task-117
|
|
|
|
|
|
- **ID**:task-117
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:Go-No-Go GD-4 指标验收脚本
|
|
|
- **输入**:task-101 task-102;PRD §12.7
|
|
|
- **输出**:`scripts/go-no-go-check.ts`;输出 PASS/FAIL 与四指标实测值
|
|
|
- **验证**:1000 次 mock 后脚本判定三率/命中率是否达标
|
|
|
- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接
|
|
|
|
|
|
---
|
|
|
|
|
|
## task-118
|
|
|
|
|
|
- **ID**:task-118
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:E2E + Load nightly workflow
|
|
|
- **输入**:task-089 task-090 task-117
|
|
|
- **输出**:`.github/workflows/e2e-nightly.yml`
|
|
|
- **验证**:cron 触发;artifact 上传 k6 报告
|
|
|
|
|
|
---
|
|
|
---
|
|
|
|
|
|
## task-133
|
|
|
|
|
|
- **ID**:task-133
|
|
|
- **状态**:待执行
|
|
|
- **模块**:Test
|
|
|
- **目标**:TC-601 修订(四档 + probe 对齐 PRD v0.6)
|
|
|
- **输入**:PRD TC-601 CHG-P08;task-130
|
|
|
- **输出**:修订 `e2e/rpa.test.ts` / `__tests__/integration/rpa-probe.test.ts`;items=4 断言
|
|
|
- **实现范围**:mock 关闭时 probe 四档;文档对齐 v0.6 口径
|
|
|
- **验证**:TC-601 items.length===4;非 2 条
|
|
|
---
|
|
|
|
|
|
## task-134
|
|
|
|
|
|
- **ID**:task-134
|
|
|
- **状态**:待执行
|
|
|
- **模块**:Test
|
|
|
- **目标**:Go-No-Go probe 3/3 门禁
|
|
|
- **输入**:PRD §12.7 CHG-P09;task-130
|
|
|
- **输出**:`scripts/go-no-go-check.ts` 增 probe 3/3 条件(`RPA_MOCK_MODE=false` 时启用)
|
|
|
- **实现范围**:probe 失败 → Go-No-Go FAIL;mock 模式跳过 probe 门禁
|
|
|
- **验证**:mock 模式 PASS;真实 env 下 probe 3/3 才 PASS
|
|
|
---
|
|
|
|
|
|
## task-135
|
|
|
|
|
|
- **ID**:task-135
|
|
|
- **状态**:待执行
|
|
|
- **模块**:Test
|
|
|
- **目标**:CI nightly probe 步骤
|
|
|
- **输入**:task-134;task-118
|
|
|
- **输出**:`.github/workflows/e2e-nightly.yml` 可选 headed probe 步骤(secrets 配 MOTHERSHIP env)
|
|
|
- **实现范围**:nightly 不阻断 merge;probe 失败上传 artifact
|
|
|
- **验证**:workflow 语法有效;secrets 缺失时跳过 probe 步骤
|
|
|
---
|
|
|
|
|
|
## task-091
|
|
|
|
|
|
- **ID**:task-091
|
|
|
- **状态**:已完成
|
|
|
- **模块**:Test
|
|
|
- **目标**:配置 CI 门禁(GitHub Actions)
|
|
|
- **输入**:技术设计 §11.2;package.json scripts
|
|
|
- **输出**:`.github/workflows/ci.yml`;lint + test:unit + test:integration + build;**`.github/workflows/e2e-nightly.yml` 含 E2E + load(task-118)**
|
|
|
- **实现范围**:CI 阻断 unit/integration;E2E/load nightly 不阻断 merge 但报告 Go-No-Go
|
|
|
- **验证**:PR push → unit/integration pass 才绿;nightly 跑 e2e + k6
|
|
|
---
|
|
|
|
|
|
## 附录 A:验收用例 → Task 映射(v1.2 全覆盖)
|
|
|
|
|
|
| TC | 主验证 Task | 辅助 Task |
|
|
|
|----|-------------|-----------|
|
|
|
| TC-101~108 | 085, 112 | 014, 032, 013, 011, 012 |
|
|
|
| TC-201~204 | 082, 010 | — |
|
|
|
| TC-205~206 | 115, 092, **129** | 052, 053 |
|
|
|
| TC-207 | 084, 013 | — |
|
|
|
| TC-208~209 | 018, 028, 036, **128** | 092 |
|
|
|
| TC-210~212 | 087, 051 | — |
|
|
|
| TC-301~304 | 090, 018, 021 | — |
|
|
|
| TC-401 | 113 | 032, 051 |
|
|
|
| TC-402~404 | 084, 019 | 013 |
|
|
|
| TC-403 | 114 | 041 |
|
|
|
| TC-501~506 | 086, 094, 035-ip | 004, 010 |
|
|
|
| TC-601~605 | 088, 115, **130, 133** | 026, 027, 092, 123–127 |
|
|
|
| Go-No-Go | 117, 101, 102, **134** | 090, **130** |
|
|
|
| probe 3/3 | **130, 134, 135** | 121, 122 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 附录 B:完整性矩阵(PRD v0.6 + 技术设计 v1.2)
|
|
|
|
|
|
### B.1 维度覆盖
|
|
|
|
|
|
| 维度 | v1.1 基线 Task | v1.2 增量 Task |
|
|
|
|------|----------------|----------------|
|
|
|
| PRD 功能点 | 001–118(除废止) | GD-15(119–122)、错误码(128–129)、probe(130,133–135)、Bull Board(132) |
|
|
|
| 技术模块 | 各 Phase 已覆盖 | quote-page-adapter(123)、storageState(124)、selector env(125–127)、probe(130) |
|
|
|
| UI 页面 | 106 + admin 6 页 | `/admin/queues`(132) |
|
|
|
| API(§8.5 七接口 + RPA admin) | 015–018, 042–043, 056–057, 029/093/099/100 | 无新增 REST |
|
|
|
| 数据表(6 张) | 070–073 | 无新增表 |
|
|
|
|
|
|
### B.2 PRD v0.6 变更 → Task
|
|
|
|
|
|
| CHG ID | 章节 | Task |
|
|
|
|--------|------|------|
|
|
|
| CHG-P02 | GD-15 | 119, 121, 122 |
|
|
|
| CHG-P03 | §2.5 | 128, 129 |
|
|
|
| CHG-P04 | §5.6 | 128 |
|
|
|
| CHG-P05 | §8.2 | 123, 124 |
|
|
|
| CHG-P06 | §8.2.1~8.2.3 | 119, 121, 122, 125–127 |
|
|
|
| CHG-P07 | §11.5 | 124, 128 |
|
|
|
| CHG-P08 | TC-601 | 130, 133 |
|
|
|
| CHG-P09 | §12.7 | 134, 135 |
|
|
|
|
|
|
### B.3 技术设计 v1.2 变更 → Task
|
|
|
|
|
|
| CHG ID | 章节 | Task |
|
|
|
|--------|------|------|
|
|
|
| CHG-T02 | §5 目录 | 121, 130 |
|
|
|
| CHG-T03 | §6.2 env | 119, 131 |
|
|
|
| CHG-T04 | §6.1 compose | 120 |
|
|
|
| CHG-T05~T07 | §7.2 | 123–127 |
|
|
|
| CHG-T08 | §7.3 | 128 |
|
|
|
| CHG-T09 | §7.5 | 124, 120 |
|
|
|
| CHG-T10 | §8.2 | 132 |
|
|
|
| CHG-T11~T12 | §9.2 | 121, 122 |
|
|
|
|
|
|
### B.4 v1.1 Traceability Matrix 缺口(已闭合,基线)
|
|
|
|
|
|
| Matrix 缺口 | Task |
|
|
|
|-------------|------|
|
|
|
| Fallback 编排无独立 Task | task-092, 115 |
|
|
|
| GET /api/rpa/status + /admin/rpa | task-093, 103 |
|
|
|
| audit log | task-070(schema), 094 |
|
|
|
| confidence_score | task-095 |
|
|
|
| lib/response.ts / lib/prisma.ts | task-096, 097 |
|
|
|
| GD-4 metrics | task-101, 102, 104, 117 |
|
|
|
| IP 限流 | task-035-ip |
|
|
|
| 503 retry | task-098 |
|
|
|
| 管理员暂停 RPA | task-099, 103 |
|
|
|
| 人工兜底录入 | task-100, 079 |
|
|
|
| display_unit | task-065 |
|
|
|
| 客户端退避 | task-066 |
|
|
|
| 宿主嵌入 vs 独立页 | task-106(废止 075~077) |
|
|
|
| 中台加价 UI 漂移 | 废止 078 |
|
|
|
| TC-108/302/401/403 | task-112~114 |
|
|
|
| quote_id 冲突 | task-109 |
|
|
|
| CARRIER/ADDRESS 错误码 | task-110 |
|
|
|
| BR-5.5 四档齐全 | task-111 |
|
|
|
| SESSION_EXPIRED/SECURITY | task-105 |
|
|
|
| rpa:operate RBAC | task-005 |
|
|
|
|