|
|
# 查价项目
|
|
|
|
|
|
> **新人入口文档**:5 分钟了解项目、跑起来、别踩红线。
|
|
|
> 权威细节以 PRD / 技术设计 / 工程任务拆解为准;本文是索引与速查。
|
|
|
|
|
|
| 项 | 值 |
|
|
|
|----|-----|
|
|
|
| 文档版本 | v1.3 |
|
|
|
| 更新日期 | 2026-06-30 |
|
|
|
| 产品名 | 美美与共报价中台(查价系统) |
|
|
|
| 代码仓库 | `chajia` |
|
|
|
|
|
|
---
|
|
|
|
|
|
## AI 项目交接文档
|
|
|
|
|
|
> 供下一 AI 会话快速接手。执行日志:`cursor_project_rules/implementation-plan.mdc`(Step 11.75~11.86)。
|
|
|
|
|
|
### 1. 项目核心身份 (Project Identity)
|
|
|
|
|
|
**项目名称与一句话定义**
|
|
|
|
|
|
**美美与共报价中台(chajia)** — 通过 Playwright RPA 抓取 MotherShip 公开报价页,一次询价返回多档 LTL 报价,经加价引擎输出给宿主系统内嵌查价组件;中台自身提供 admin 管理端(预警、RPA 运维、队列监控)。
|
|
|
|
|
|
**技术栈清单**(来源:`package.json` / `docker-compose.yml`)
|
|
|
|
|
|
| 类别 | 技术 | 版本 |
|
|
|
|------|------|------|
|
|
|
| 语言 | TypeScript | ^5.9.2 |
|
|
|
| 运行时 | Node.js | [需人工补充: 最低 Node 版本] |
|
|
|
| Web 框架 | Next.js (App Router) | ^15.5.6 |
|
|
|
| UI | React | ^19.1.1 |
|
|
|
| 样式 | Tailwind CSS | ^3.4.19 |
|
|
|
| 组件 | shadcn/ui 风格 + Geist 字体 + @phosphor-icons/react ^2.1.10 | — |
|
|
|
| 表单 | react-hook-form ^7.79.0 + zod ^3.25.76 | — |
|
|
|
| API | Next.js Route Handlers | — |
|
|
|
| ORM | Prisma | ^6.19.0 |
|
|
|
| 数据库 | MySQL | 8.0(docker image `mysql:8.0`) |
|
|
|
| 缓存/队列 | Redis 7-alpine + ioredis ^5.8.1 + BullMQ ^5.78.1 | 本机 WSL 实测 6.0.16 有警告 |
|
|
|
| RPA | Playwright ^1.61.0;可选 playwright-extra + stealth | — |
|
|
|
| 队列 UI | Bull Board (@bull-board/* ^8.0.0) + Hono ^4.12.25 | — |
|
|
|
| 鉴权 | jose ^5.10.0 + bcryptjs ^3.0.2 | — |
|
|
|
| 测试 | Vitest ^3.2.4 + @playwright/test ^1.61.0 | — |
|
|
|
| 构建/脚本 | tsx ^4.20.5 | — |
|
|
|
| 部署 | Docker Compose(全栈或宝塔宿主机 MySQL + Docker 应用) | 见 `deploy/` |
|
|
|
|
|
|
**目录结构语义化说明**
|
|
|
|
|
|
| 路径 | 职责 | 数据流向 |
|
|
|
|------|------|----------|
|
|
|
| `app/` | Next.js 页面与 API Routes(薄路由层) | 宿主/Admin HTTP → `modules/*` |
|
|
|
| `app/api/quotes/` | 询价创建与轮询(方式 B) | → `modules/quote/orchestrator` → BullMQ |
|
|
|
| `app/api/host/quote/` | 宿主两步公开接口(方式 A,免 Token) | candidates → submit(服务端内部轮询) |
|
|
|
| `app/api/addresses/mothership-candidates/` | 地址候选(方式 B 第 1 步) | → BullMQ `address-candidates` job |
|
|
|
| `lib/axel/` | MotherShip axel HTTP 直连(search/place/quote) | `buildAxelCargo` 整数磅/英寸 |
|
|
|
| `lib/constants/host-service.ts` | 默认 Service Token / customer_id | embed-demo 与内网对接 |
|
|
|
| `components/` | 宿主内嵌查价 UI、管理端组件 | hooks → API |
|
|
|
| `modules/quote/` | 询价编排、校验、幂等、fallback | L1→L2→RPA→L3 状态机 |
|
|
|
| `modules/pricing/` | 加价引擎(仅 raw_freight,0~30%) | 读 MarkupConfig → 写 QuoteRecord |
|
|
|
| `modules/cache/` | Redis L1/L2/L3、限流、熔断 | 读写 Redis |
|
|
|
| `workers/rpa/` | BullMQ Consumer + Playwright 填表/抓价 | 消费 queue → 写 MySQL/Redis |
|
|
|
| `workers/rpa/quote-capture/` | Network-First 报价捕获(axel/quote 契约解析) | submit 后监听 network → normalize |
|
|
|
| `workers/rpa/address-adapter/` | 地址选择抽象(real/mock) | DOM + axel search/place API |
|
|
|
| `workers/rpa/kernel/` | RPA 步骤编排(address/cargo/quote steps) | 内部状态机 |
|
|
|
| `lib/` | prisma/redis/response/rpa env 等基础设施 | 被 app/modules/workers 引用 |
|
|
|
| `prisma/` | Schema、migrations、seed | MySQL 持久化 |
|
|
|
| `scripts/` | probe、prove、smoke、录证、准确度测试 | CLI 验收/排障 |
|
|
|
| `cursor_project_rules/` | AI 编码规则 + implementation-plan 逐步日志 | 必读 |
|
|
|
| `.rpa/` | storageState、session、chain-proof 产物 | 勿提交敏感态(gitignore) |
|
|
|
| `.dev/logs/` | worker/next 运行日志、失败截图 | 排障 |
|
|
|
|
|
|
**端到端数据流**
|
|
|
|
|
|
```
|
|
|
【方式 A — 宿主公开接口,推荐】
|
|
|
宿主前端
|
|
|
→ POST /api/host/quote/candidates(地址+货物 → RPA 枚举候选 + host_session_id)
|
|
|
→ 用户选候选
|
|
|
→ POST /api/host/quote/submit(服务端内部轮询,同步返回最终报价)
|
|
|
|
|
|
【方式 B — Token 三步,embed-demo / 后期鉴权】
|
|
|
EmbeddedQuoteWidget
|
|
|
→ POST /api/addresses/mothership-candidates
|
|
|
→ 用户确认弹窗
|
|
|
→ POST /api/quotes → L1/L2 未命中 → BullMQ quote-rpa
|
|
|
→ workers/rpa:Direct 优先 node fetchAxelQuoteItems → 失败可 Widget fallback(生产建议关闭)
|
|
|
→ 写 L2/L3 + quote_record → GET /api/quotes/{id} 轮询
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2. 当前开发状态快照 (Current State Snapshot)
|
|
|
|
|
|
**已完成模块**(单测/集成/E2E 在 Mock 或隔离路径下通过)
|
|
|
|
|
|
| 模块 | 验收依据 |
|
|
|
|------|----------|
|
|
|
| Phase 0~4 底座 | M1:POST /quotes、L2 缓存、幂等、校验 400 |
|
|
|
| 鉴权 RBAC + Service Token | middleware + 单测 |
|
|
|
| 三级缓存 + 限流 + 熔断 | redis-cache / rate-limiter 单测 |
|
|
|
| 加价引擎 | engine.test.ts TC-103 |
|
|
|
| 询价编排 + fallback + 超时 sweeper | orchestrator / timeout-sweeper 单测 |
|
|
|
| BullMQ 队列 + Worker 管理 API | rpa status/pause |
|
|
|
| 宿主内嵌查价 UI + 地址确认弹窗 | embed-demo + E2E |
|
|
|
| 管理端(预警/RPA/队列/指标/客户加价) | admin 路由 |
|
|
|
| 宿主公开 API(方式 A) | `app/api/host/quote/*` + `deploy/第三方对接指南.md` |
|
|
|
| 内网生产部署(宝塔 MySQL) | `deploy/docker-compose.bt-host-mysql.yml` + `install-bt-host-mysql.sh` |
|
|
|
| axel 公制取整 | `toAxelWholeInches` / `toAxelWholePounds`(直连 API 边界) |
|
|
|
| CI lint + test:unit + test:integration + build | `.github/workflows/ci.yml` |
|
|
|
| RPA v1.2 骨架 | QuotePageAdapter、selector env、Network-First capture、contract guard |
|
|
|
| 阶段 0 录证 | `.rpa/phase0/quote-contract-summary.json`:headed 人工可拿到 axel/quote |
|
|
|
| 货物填表(mock 路径) | fillCargoInDrawer + cargo-reset 单测 |
|
|
|
|
|
|
**进行中任务**
|
|
|
|
|
|
| 任务 | 进度 | 说明 |
|
|
|
|------|------|------|
|
|
|
| **M2-v1.2** 真实 RPA probe 3/3 | **待跑** | `npm run probe:rpa:3x` |
|
|
|
| **内网生产验收** | **进行中** | 宝塔 MySQL + `192.168.2.14:30325`;公制货物 + Direct 模式 |
|
|
|
| task-133~135 CI probe 门禁 | 待执行 | TC-601 |
|
|
|
| Priority1 FTL/LTL 录制与 benchmark | **实验** | 非 MVP;`visual:priority1-*` / `record:priority1-*` |
|
|
|
|
|
|
**最近 3 次关键变更**
|
|
|
|
|
|
| 次序 | 变更 | 原因 | 影响模块 |
|
|
|
|------|------|------|----------|
|
|
|
| 1 | **宿主公开 API**(`/api/host/quote/candidates` + `submit`) | 美美与共等宿主免 Token、服务端同步返回报价 | `app/api/host/`、`deploy/API.md` |
|
|
|
| 2 | **生产 Direct 报价 + 关闭 Widget fallback** | 避免浏览器回退卡 6~12 分钟;内网快速失败 | `quote-strategy.ts`、`RPA_QUOTE_MODE=direct`、`RPA_DISABLE_WIDGET_QUOTE_FALLBACK=true` |
|
|
|
| 3 | **axel 公制取整**(磅/英寸 `Math.ceil`) | MotherShip `axel/quote` 要求整数;公制 API 会算出小数 | `unit-converter.ts`、`lib/axel/shipment.ts` |
|
|
|
|
|
|
---
|
|
|
|
|
|
### 3. 问题与阻塞项登记 (Issues & Blockers Registry)
|
|
|
|
|
|
> **2026-06-30 状态**:人工 docx 10 组 `probe:human-doc-system` **10/10 PASS**。内网已部署宝塔 MySQL + Docker 应用(`30325`);生产模板默认 Direct + 禁 Widget fallback。公制 API 须走 `toAxelWhole*` 取整后发 axel。
|
|
|
|
|
|
| 问题ID | 状态 | 问题描述 | 复现路径/报错信息 | 已尝试的解决方案(含失败原因) | 下一步建议方案 | 相关文件路径 |
|
|
|
|--------|------|----------|-------------------|------------------------------|----------------|--------------|
|
|
|
| **BLK-012** | 已修复 | **P0** 公制货物直连 axel HTTP 400(磅/英寸须整数) | API 传 500kg / 120cm → `Weight/Length must be a whole number` | 内部 GD-13 保留 2 位小数;边界 `toAxelWholePounds/Inches` 向上取整 | 验收公制样例 + 英制默认样例均 PASS | `unit-converter.ts`、`lib/axel/shipment.ts` |
|
|
|
| **BLK-013** | 已修复 | 打包后 `GET /api/quotes/{id}` 返回 HTML 404 | Windows 打包脚本 `[quote_id]` 被当通配符 | `pack-server-upload.ps1` 使用 `-LiteralPath`;安装脚本预检路由 | 上传后 `curl` 确认 JSON 非 HTML | `deploy/pack-server-upload.ps1` |
|
|
|
| **BLK-014** | 已缓解 | Docker 部署 next-app 缺 Playwright / storageState | 503 或 bootstrap 失败 | `rpa_state` 卷共享;next-app `RPA_AUTO_BOOTSTRAP_STORAGE=false` | worker 负责 bootstrap;两容器共享 `.rpa` | `docker-compose.bt-host-mysql.yml` |
|
|
|
| **BLK-001** | 已缓解 | **P0** 地址 DOM 点选不触发 `GET place/{placeId}`,commit 失败 | 历史:embed-demo → worker `no_place_request` | description-click / keyboard / pac-dispatch 等;**11.77** preknown 跳过 legacy 点选、走 synthetic | 新地址回归 `probe:human-doc-system`;headed 录证见 `docs/真人录证-commit时刻.md` | `mothership-address-adapter.ts`、`address-commit.ts` |
|
|
|
| **BLK-002** | 已缓解 | **P0** 填 delivery 后 pickup 丢失(widget 互斥) | 历史:dual-address `pickupLostAfterDelivery=true` | 交替 refill(11.48);preknown synthetic(11.77) | 同上 | `address-side.ts`、`address-commit.ts` |
|
|
|
| **BLK-003** | 已缓解 | submit 后无 `axel/quote`(contract=0) | 历史:commit 失败导致无 quote | 根因是地址未 commit;Direct-first + Widget fallback(11.75) | 监控 worker 日志 `axel/quote`;勿在 commit 通前改 contract 层 | `quote-capture/pipeline.ts`、`quote-strategy.ts` |
|
|
|
| **BLK-004** | 已关闭 | mock/fixture 假通过误导验收 | `prove:rpa-chain` PASS 但价非 MotherShip | mock-chain-fixture **已删除(11.45)** | 验收 `RPA_ADDRESS_MODE=real` + 无 fixture | `.rpa/chain-proof-72b4e980.json`(历史样例) |
|
|
|
| **BLK-005** | 已更新 | direct POST axel/quote 价与 UI 不一致 | 历史:direct 价偏低 | **11.75** Direct 优先、失败再 Widget fallback | 勿单独启用 Widget 并行/预热 | `quote-strategy.ts`、`axel-quote-direct.ts` |
|
|
|
| **BLK-006** | 开放 | L2 缓存假阳性 | `创建即 done + source_type=cache` | 探针用 `uniqueWeight` 防命中空 cache | 验收前换 cargo 或 `FLUSHDB` | `modules/cache/redis-cache.ts` |
|
|
|
| **BLK-007** | 开放 | 端到端超时 420s | commit 失败时前端超时 | 提 timeout 不能解决 commit;commit 通后约 2–3 分钟/单 | commit 稳定后评估 browser warm/session | `lib/constants/quote.ts` |
|
|
|
| **BLK-008** | 开放 | WSL Redis/MySQL 间歇断连 | worker `ECONNREFUSED 6379/3307` | keepalive + orphan 回收 | 长跑前 `npm run dev:infra:start` | `lib/redis.ts`、`queue-recovery.ts` |
|
|
|
| **BLK-009** | 开放 | 候选 API 60~180s 慢 | POST `/api/addresses/mothership-candidates` | 枚举优化、Worker 队列 | 候选慢可接受;`pickBestMothershipCandidate` 降权 Bridge | `address-candidates.ts`、`pick-mothership-candidate.ts` |
|
|
|
| **BLK-010** | 开放 | storageState 货物残留 | 摘要 2296Lbs vs 期望 500Lbs | freshCargoContext + cargo-reset | real 路径验证 `assertWidgetCargoSummary` | `cargo-reset.ts`、`session-manager.ts` |
|
|
|
| **BLK-011** | 冻结 | 架构过度重构拖慢排障 | kernel/驻留页多轮 | 11.30 放弃驻留页默认路径 | **冻结架构**;仅改 adapter + commit | `workers/rpa/kernel/` |
|
|
|
|
|
|
**根因链(历史 → 当前)**
|
|
|
|
|
|
```
|
|
|
[历史] placeApiHitCount=0 → commit FAILED → 无 axel/quote → 420s 超时
|
|
|
[当前] Direct-first + 系统探针 10/10;内网宝塔部署 + 公制取整;宿主方式 A 两步 API
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
### 4. 架构决策记录 (ADR)
|
|
|
|
|
|
**ADR-1:Network-First 报价捕获(禁止 DOM 刮价)**
|
|
|
|
|
|
- **决策**:报价仅来自 `services.mothership.com/axel/quote` 网络响应,经 `quote-contract-guard` + `payload-decode` 解析;禁止 DOM price extraction。
|
|
|
- **原因**:MotherShip SPA 档位与 DOM 不同步;阶段 0 HAR 已验证 API 契约稳定。
|
|
|
- **权衡**:放弃 DOM fallback;submit 后必须触发真实 quote 请求,**强依赖地址/货物 commit 成功**。
|
|
|
|
|
|
**ADR-2:BullMQ 统一 RPA Worker(候选 + 询价同进程)**
|
|
|
|
|
|
- **决策**:地址候选与询价均入 BullMQ,同一 `workers/rpa/index.ts` 消费;`withRpaSessionLock` 串行。
|
|
|
- **原因**:spawn 子进程与 worker 双浏览器争用 `.rpa/mothership-storage.json`,行为不一致。
|
|
|
- **权衡**:单 worker 吞吐低;Worker 不可用时才 spawn 回退(stall 60s)。
|
|
|
|
|
|
**ADR-3:MotherShip 地址须用户确认 + place API commit**
|
|
|
|
|
|
- **决策**:RPA 枚举候选 → 前端弹窗确认 → 询价时按 `display_label` 点选;commit 以 `GET place/{placeId}` 网络证据为准。
|
|
|
- **原因**:PRD GD 要求点选;仅 DOM 填值不满足 MotherShip widget 状态机。
|
|
|
- **权衡**:端到端 180~360s;**当前 place commit 未打通即全链路失败**。
|
|
|
|
|
|
**ADR-4:Next.js Route Handlers 薄层 + modules 业务编排**
|
|
|
|
|
|
- **决策**:API Route 只鉴权/校验/调 orchestrator;禁止 Route 内写复杂业务。
|
|
|
- **原因**:可测试性、分层清晰、Worker 与 API 共享 modules。
|
|
|
- **权衡**:无 Server Actions;宿主必须 HTTP 集成。
|
|
|
|
|
|
**ADR-5:放弃驻留页默认路径(`RPA_PARKED_SESSION=false`)**
|
|
|
|
|
|
- **决策**:候选完成后不默认 park page;询价 job 独立开页 + storageState;browser warm 保留。
|
|
|
- **原因**:驻留页需 worker+confirm+同进程,验证周期 >5min 且 fetch failed 频发;网络等待占 80%+。
|
|
|
- **权衡**:候选→询价无法零重开;性能优化有限。
|
|
|
|
|
|
**ADR-6:生产环境 Direct 优先、默认关闭 Widget fallback**
|
|
|
|
|
|
- **决策**:`RPA_QUOTE_MODE=direct`;`RPA_DISABLE_WIDGET_QUOTE_FALLBACK=true`(生产模板);node `fetchAxelQuoteItems` 为主路径。
|
|
|
- **原因**:Widget 浏览器填表回退单次 6~12 分钟且 worker 并发为 1,易阻塞队列。
|
|
|
- **权衡**:直连对 payload 更严格(整数磅/英寸、有效 placeId);失败须快速报错而非长时间挂起。
|
|
|
|
|
|
**ADR-7:内部精度与 axel API 边界分离**
|
|
|
|
|
|
- **决策**:`kgToLb` / `cmToIn` 内部保留 2 位小数(GD-13 哈希/展示);调用 MotherShip `axel/quote` 前 `toAxelWholePounds` / `toAxelWholeInches` 向上取整。
|
|
|
- **原因**:embed-demo 默认英制整数不易暴露;公制 API / 宿主对接会触发小数。
|
|
|
- **权衡**:取整仅影响 MotherShip 边界,不改变 L2 cache key 算法(仍用内部精度)。
|
|
|
|
|
|
---
|
|
|
|
|
|
### 5. 新 AI 接手指令 (Onboarding Instructions)
|
|
|
|
|
|
**启动/运行命令**
|
|
|
|
|
|
```bash
|
|
|
cp .env.example .env # 首次
|
|
|
npm run dev:infra:setup # WSL MySQL+Redis(Windows 推荐)
|
|
|
npm run dev:start # infra + migrate + seed + next + worker + scheduler
|
|
|
|
|
|
# 分项
|
|
|
npm run dev # Next.js :3000
|
|
|
npm run worker:rpa # RPA Worker(另开终端)
|
|
|
npm run worker:scheduler # 超时清扫
|
|
|
|
|
|
# 验证
|
|
|
npm run smoke:connect # MySQL/Redis
|
|
|
npm run verify:rpa-stack # 四步全栈验收
|
|
|
npm run prove:rpa-chain # 端到端 → .rpa/chain-proof-*.json
|
|
|
RPA_HEADLESS=false npm run probe:rpa # headed RPA 调试
|
|
|
|
|
|
# 测试
|
|
|
npm run lint
|
|
|
npm run test:unit
|
|
|
npm run test:integration
|
|
|
npm run test:e2e
|
|
|
npm run go-no-go -- --mock
|
|
|
|
|
|
# 停止
|
|
|
npm run dev:stop
|
|
|
```
|
|
|
|
|
|
**环境变量依赖**(名称为准,勿写值)
|
|
|
|
|
|
| 变量 | 用途 |
|
|
|
|------|------|
|
|
|
| `DATABASE_URL` | MySQL 连接(本地通常端口 3307) |
|
|
|
| `REDIS_URL` | Redis 连接 |
|
|
|
| `JWT_SECRET` | 管理端 JWT |
|
|
|
| `HOST_SERVICE_TOKENS` | 宿主 Service Token → customer_id(内网默认 `chajia-neibu-2026`) |
|
|
|
| `HOST_PUBLIC_API_ENABLED` | `true` 启用方式 A 公开接口(免 Token) |
|
|
|
| `HOST_PUBLIC_DEFAULT_CUSTOMER_ID` | 方式 A 默认客户 ID |
|
|
|
| `RPA_QUOTE_MODE` | `direct`(生产)/ `widget`(调试) |
|
|
|
| `RPA_DISABLE_WIDGET_QUOTE_FALLBACK` | 生产建议 `true`,直连失败不浏览器回退 |
|
|
|
| `RPA_AUTO_BOOTSTRAP_STORAGE` | worker `true`;next-app 容器建议 `false` |
|
|
|
| `MOTHERSHIP_QUOTE_URLS` | RPA 入口 URL 列表(必填,禁止 dashboard) |
|
|
|
| `RPA_SELECTOR_*` | 8 项 Playwright selector(PICKUP/DELIVERY/WIDGET/STREET/SUGGESTION/CARGO/SUBMIT 等) |
|
|
|
| `RPA_STORAGE_STATE_PATH` | Playwright storageState 路径 |
|
|
|
| `RPA_MOCK_MODE` | `false`=真实 RPA;`true` 仅 CI/单测 |
|
|
|
| `RPA_ADDRESS_MODE` | `real` / `mock`(mock 不能用于验收) |
|
|
|
| `RPA_HEADLESS` / `RPA_HEADED` | 有头调试 |
|
|
|
| `RPA_BROWSER_LOCALE` | 推荐 `en-US` |
|
|
|
| `RPA_BROWSER_CHANNEL` | 推荐 `chrome` |
|
|
|
| `RPA_USE_STEALTH` | 默认 `false`(曾破坏 widget) |
|
|
|
| `RPA_PARKED_SESSION` | 默认 `false` |
|
|
|
| `RPA_QUEUE_ENABLED` | Worker 是否消费队列 |
|
|
|
| `RPA_WORKER_ID` | Worker 标识(空则自动生成) |
|
|
|
| `MOTHERSHIP_QUOTE_API_URL_PATTERNS` | axel/quote telemetry 白名单 |
|
|
|
| `MOTHERSHIP_EMAIL` / `MOTHERSHIP_PASSWORD` | 仅 login 页时需要 |
|
|
|
| `DEV_INFRA_MODE` | `native` / `docker` / `auto` |
|
|
|
| `MYSQL_HOST_PORT` / `MYSQL_ROOT_PASSWORD` | Docker/WSL MySQL |
|
|
|
|
|
|
**首要任务优先级**
|
|
|
|
|
|
1. **P0** 回归门禁:`npm run probe:human-doc-system` 10/10(改地址/RPA 后必跑;worker 需 `dev:stop` + `dev:start` 重载)
|
|
|
2. **P0** 实网验收:`npm run prove:rpa-chain` 在 `RPA_ADDRESS_MODE=real` 下 PASS(非 cache、非 fixture)
|
|
|
3. **P1** embed-demo 标准样例(LA 90001 → Dallas 75201,2 托 500lb)返回 `source_type=rpa` 且价与 MotherShip 页面一致
|
|
|
4. **P2** M2 门禁:`npm run probe:rpa:3x` 连续 exit 0
|
|
|
5. **禁止**:在 commit 未通前改 timeout/stealth/contract/kernel/驻留页;禁止 Widget 并行/预热/默认路径
|
|
|
|
|
|
**项目特殊约定**
|
|
|
|
|
|
| 类别 | 约定 |
|
|
|
|------|------|
|
|
|
| 编码前必读 | `cursor_project_rules/global-rules.mdc`、`implementation-plan.mdc`、`domain-rules.mdc` |
|
|
|
| 分层 | `app/api` 薄路由 → `modules` 业务 → `workers/rpa` 长任务 |
|
|
|
| 命名 | DB snake_case;TS camelCase/PascalCase;env UPPER_SNAKE |
|
|
|
| 金额 | DECIMAL(12,2);`ROUND_HALF_UP`;禁止 float 运算 |
|
|
|
| 界面文案 | **全部中文**(MotherShip 抓取字段除外) |
|
|
|
| 红线 | 一次询价=1 次 RPA;cache key 不含 service_level;禁止 dashboard URL;禁止 RPA mock 上线 |
|
|
|
| Git | 禁止 main 直推;不提交 `.env`/`.rpa/` 敏感态 |
|
|
|
| 完成步骤 | 在 `implementation-plan.mdc` 写 `Done` + 两行执行记录 |
|
|
|
| 禁止库/模式 | 禁止 DOM 刮价;禁止 mock-chain-fixture;禁止 default dashboard URL;禁止空 catch |
|
|
|
| Windows 开发 | 优先 `npm run dev:start`(PowerShell);WSL native infra 优于 Docker Desktop |
|
|
|
|
|
|
**关键产物路径**
|
|
|
|
|
|
| 路径 | 内容 |
|
|
|
|------|------|
|
|
|
| `.dev/logs/worker-rpa.log` | Worker 实网日志 |
|
|
|
| `.dev/logs/rpa-failure-*.png` | RPA 失败截图 |
|
|
|
| `.rpa/chain-proof-*.json` | prove 链路报告 |
|
|
|
| `.rpa/dual-address-compare-*.json` | 双地址 network 对比 |
|
|
|
| `.rpa/phase0/quote-contract-summary.json` | axel/quote API 契约 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 项目信息
|
|
|
|
|
|
### 做什么
|
|
|
|
|
|
通过 **Playwright RPA** 抓取 Mothership 公开报价页,一次询价返回 **4 档**报价(standard/guaranteed × lowest/fastest),经加价引擎输出给**宿主系统**内嵌查价组件。中台自身仅提供 **admin 管理端**(预警、RPA 状态、队列监控)。
|
|
|
|
|
|
### 不做什么(MVP 边界)
|
|
|
|
|
|
- 不使用 Mothership 官方 API
|
|
|
- 无独立客户/运营登录、无运营加价 UI
|
|
|
- 无 CC 对接、多币种、下单、对账、Priority1
|
|
|
|
|
|
### 技术栈
|
|
|
|
|
|
| 层 | 选型 |
|
|
|
|----|------|
|
|
|
| Web | Next.js 15(App Router)+ React 19 + Tailwind + shadcn/ui |
|
|
|
| API | Next.js Route Handlers |
|
|
|
| 数据 | MySQL 8 + Prisma |
|
|
|
| 缓存/队列 | Redis 7 + BullMQ |
|
|
|
| RPA | Playwright(可选 Patchright) |
|
|
|
| 测试 | Vitest + Playwright E2E |
|
|
|
| 部署 | Docker Compose(全栈 / 宝塔宿主机 MySQL) |
|
|
|
|
|
|
### 核心文档(按阅读顺序)
|
|
|
|
|
|
| 文档 | 用途 |
|
|
|
|------|------|
|
|
|
| [查价系统-PRD.md](./查价系统-PRD.md) v0.6 | 业务规则、GD 决策、验收标准(**最高业务权威**) |
|
|
|
| [查价系统-技术设计.md](./查价系统-技术设计.md) v1.2 | 架构、目录、API、RPA 规格(**实现权威**) |
|
|
|
| [查价系统-工程任务拆解-v1.2.md](./查价系统-工程任务拆解-v1.2.md) | 原子任务与开发顺序 |
|
|
|
| [查价系统-UI设计.md](./查价系统-UI设计.md) | 页面、交互、状态机 |
|
|
|
| [参考-Mothership-RPA-开源借鉴与解阻塞.md](./参考-Mothership-RPA-开源借鉴与解阻塞.md) | RPA 排障参考 |
|
|
|
| [deploy/README.md](../deploy/README.md) | 全 Docker 生产部署 |
|
|
|
| [deploy/宝塔部署说明.md](../deploy/宝塔部署说明.md) | 宝塔 MySQL + Docker 应用(内网 30325) |
|
|
|
| [deploy/API.md](../deploy/API.md) | 外部系统接口速查 |
|
|
|
| [deploy/第三方对接指南.md](../deploy/第三方对接指南.md) | 宿主对接完整指南(方式 A/B) |
|
|
|
| [报价提速与稳定性-改动清单.md](./报价提速与稳定性-改动清单.md) | 提速/稳定性分批实施清单(Phase 12) |
|
|
|
| [AI 项目交接文档](./查价项目.md#ai-项目交接文档) | 完整 handoff:身份/状态/阻塞/ADR/接手指令 | **切换 AI 必读** |
|
|
|
| `cursor_project_rules/` | AI/人工编码规则与实施日志 | implementation-plan 逐步记录 |
|
|
|
|
|
|
### 目录结构(关键路径)
|
|
|
|
|
|
```
|
|
|
chajia/
|
|
|
├── app/ # Next.js 页面 + API Routes
|
|
|
├── components/ # 前端组件(含宿主内嵌查价)
|
|
|
├── modules/ # 业务逻辑(quote/cache/pricing/alert)
|
|
|
├── workers/rpa/ # BullMQ Consumer + Playwright
|
|
|
├── lib/ # prisma/redis/response/rpa
|
|
|
├── prisma/ # schema + migrations + seed
|
|
|
├── scripts/ # probe、smoke、go-no-go、录制
|
|
|
├── __tests__/ # unit / integration / e2e
|
|
|
├── docs/ # 产品与工程文档
|
|
|
└── cursor_project_rules/ # 项目规则 + implementation-plan
|
|
|
```
|
|
|
|
|
|
### 角色与鉴权
|
|
|
|
|
|
| 角色 | 方式 | 能力 |
|
|
|
|------|------|------|
|
|
|
| 宿主系统 | `Authorization: Bearer <HOST_SERVICE_TOKEN>` | 代 `customer_id` 询价、历史、加价配置 |
|
|
|
| 管理员 | JWT + RBAC | 预警、RPA 运维、`/admin/*` |
|
|
|
|
|
|
联调样例:
|
|
|
|
|
|
- 内网宿主 token:`chajia-neibu-2026` → `CUST_001`(见 `lib/constants/host-service.ts`)
|
|
|
- embed-demo 亦使用上述 token(构建时 `NEXT_PUBLIC_EMBED_DEMO_*` 可覆盖)
|
|
|
- 管理端:`admin_demo` / `Demo@123`(仅开发环境)
|
|
|
|
|
|
**宿主对接方式**
|
|
|
|
|
|
| 方式 | 条件 | 接口 |
|
|
|
|------|------|------|
|
|
|
| **A(推荐)** | `HOST_PUBLIC_API_ENABLED=true` | `POST /api/host/quote/candidates` → `POST /api/host/quote/submit`(免 Token,服务端同步返回) |
|
|
|
| **B** | `HOST_PUBLIC_API_ENABLED=false` + Bearer Token | 候选 → `POST /api/quotes` → `GET /api/quotes/{id}` 轮询 |
|
|
|
|
|
|
详见 [deploy/第三方对接指南.md](../deploy/第三方对接指南.md)。
|
|
|
|
|
|
### 联调标准样例
|
|
|
|
|
|
**embed-demo 默认(英制,不易触发 axel 整数校验)**
|
|
|
|
|
|
| 字段 | 值 |
|
|
|
|------|-----|
|
|
|
| 路线 | LA(90001) → Dallas(75201) |
|
|
|
| 单托重量 | 500 lb |
|
|
|
| 单托尺寸 | 48×40×48 in |
|
|
|
| 托盘数 | 2 |
|
|
|
| 货物类型 | `general_freight` |
|
|
|
|
|
|
**宿主 API / 公制验收样例(须走取整后发 axel)**
|
|
|
|
|
|
| 字段 | 值 |
|
|
|
|------|-----|
|
|
|
| 单托重量 | 500 kg → 1103 lb(向上取整) |
|
|
|
| 单托尺寸 | 120×100×150 cm → 48×40×60 in(各边向上取整) |
|
|
|
| 托盘数 | 2 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 常用命令
|
|
|
|
|
|
### 环境准备
|
|
|
|
|
|
```bash
|
|
|
# 复制环境变量(勿提交 .env)
|
|
|
cp .env.example .env
|
|
|
|
|
|
# 一键启动(推荐,含 infra + app + worker)
|
|
|
npm run dev:start
|
|
|
|
|
|
# 仅 Docker infra(MySQL + Redis)
|
|
|
docker compose up -d mysql redis
|
|
|
|
|
|
# WSL/native infra 引导(Windows 开发机)
|
|
|
npm run dev:infra:setup
|
|
|
```
|
|
|
|
|
|
### 数据库
|
|
|
|
|
|
```bash
|
|
|
npm run db:validate # Prisma schema 校验
|
|
|
npm run db:migrate # 本地迁移(开发)
|
|
|
npm run db:deploy # 生产迁移
|
|
|
npm run db:seed # 种子数据
|
|
|
```
|
|
|
|
|
|
### 开发运行
|
|
|
|
|
|
```bash
|
|
|
npm run dev # Next.js :3000
|
|
|
npm run worker:rpa # RPA Worker(另开终端)
|
|
|
npm run worker:scheduler # 超时清扫 Worker
|
|
|
npm run dev:stop # 停止本地进程
|
|
|
npm run dev:clean # 清理临时产物
|
|
|
```
|
|
|
|
|
|
### 测试与门禁
|
|
|
|
|
|
```bash
|
|
|
npm run lint # TypeScript 类型检查
|
|
|
npm run test:unit # 单元测试
|
|
|
npm run test:integration # 集成测试
|
|
|
npm run test:e2e # Playwright E2E
|
|
|
npm run test # 全量 Vitest
|
|
|
|
|
|
npm run smoke:connect # MySQL/Redis 连通
|
|
|
npm run smoke:api # API 冒烟
|
|
|
npm run smoke:rpa # 真实 RPA 冒烟(需 env 配齐)
|
|
|
|
|
|
# RPA 探针(Go/No-Go 门禁:连续 3 次 exit 0)
|
|
|
npm run probe:rpa
|
|
|
npm run probe:rpa:3x
|
|
|
npm run probe:human-doc-system # 人工 docx 10 组,系统 API 全链路
|
|
|
npm run probe:random-address-quote
|
|
|
npm run go-no-go # 上线前指标检查
|
|
|
```
|
|
|
|
|
|
### RPA 录制与排障
|
|
|
|
|
|
```bash
|
|
|
# P0 真人完整链路录证(HAR+录像+Trace+Console+Network,定位 place commit 瞬间)
|
|
|
npm run record:human-commit-baseline
|
|
|
# 详见 docs/真人录证-commit时刻.md
|
|
|
|
|
|
# headed 录制 Mothership 报价流程 → 更新 MOTHERSHIP_QUOTE_URLS / RPA_SELECTOR_*
|
|
|
npm run record:mothership
|
|
|
|
|
|
# P0 真人 vs RPA 网络差分
|
|
|
$env:RPA_HEADLESS="false"
|
|
|
npm run compare:human-rpa-network
|
|
|
```
|
|
|
|
|
|
### Docker 全栈
|
|
|
|
|
|
**方式 1 — 全 Docker(MySQL 在容器内)**:见 [`deploy/README.md`](../deploy/README.md)
|
|
|
|
|
|
```bash
|
|
|
cp deploy/.env.production.example .env # 编辑必填项
|
|
|
bash deploy/install.sh
|
|
|
bash deploy/install.sh --nginx-http # 可选:Nginx 反代
|
|
|
```
|
|
|
|
|
|
**方式 2 — 宝塔宿主机 MySQL + Docker 应用(内网推荐)**:见 [`deploy/宝塔部署说明.md`](../deploy/宝塔部署说明.md)
|
|
|
|
|
|
```bash
|
|
|
# 代码目录示例:/home/project/chajia
|
|
|
cp deploy/.env.bt-host-mysql.example .env # 编辑 DATABASE_URL、HOST_SERVICE_TOKENS 等
|
|
|
bash deploy/install-bt-host-mysql.sh
|
|
|
# 访问 http://<内网IP>:30325
|
|
|
```
|
|
|
|
|
|
上传更新:`deploy/pack-server-upload.ps1` 生成 `上传到服务器/代码/`;**勿覆盖服务器已有 `.env`**。
|
|
|
|
|
|
**本地开发 Compose**:
|
|
|
|
|
|
```bash
|
|
|
docker compose up -d --build
|
|
|
docker compose exec next-app npx prisma migrate deploy
|
|
|
docker compose exec next-app npx prisma db seed
|
|
|
```
|
|
|
|
|
|
### 访问地址
|
|
|
|
|
|
| 入口 | URL |
|
|
|
|------|-----|
|
|
|
| 宿主内嵌演示 | http://localhost:3000/embed-demo |
|
|
|
| 管理端预警 | http://localhost:3000/admin/alerts |
|
|
|
| 管理端 RPA | http://localhost:3000/admin/rpa |
|
|
|
| 队列监控 | http://localhost:3000/admin/queues |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 编码规范
|
|
|
|
|
|
### 分层(禁止跨层)
|
|
|
|
|
|
```
|
|
|
app/api/* → 薄路由,只调 modules
|
|
|
modules/* → 业务编排(orchestrator、pricing、cache)
|
|
|
workers/rpa/* → 长任务 RPA,不经 HTTP 直调 DB 以外层
|
|
|
lib/* → 基础设施封装
|
|
|
components/* → UI,经 hooks/api 调后端
|
|
|
```
|
|
|
|
|
|
- API Route **不得**写复杂业务逻辑
|
|
|
- RPA Worker **不得**绕过 `QuoteProvider` 接口直接返回假数据(CI 除外 `RPA_MOCK_MODE=true`)
|
|
|
|
|
|
### 命名
|
|
|
|
|
|
| 范围 | 规范 | 示例 |
|
|
|
|------|------|------|
|
|
|
| DB 字段 | snake_case | `pallet_count`, `raw_freight` |
|
|
|
| TS 变量/函数 | camelCase | `cargoHash`, `getQuote` |
|
|
|
| TS 类型/组件 | PascalCase | `QuoteRecord`, `QuoteForm` |
|
|
|
| 环境变量 | UPPER_SNAKE | `MOTHERSHIP_QUOTE_URLS` |
|
|
|
| API 路径 | kebab/REST | `/api/quotes/{quote_id}` |
|
|
|
|
|
|
### 金额与精度
|
|
|
|
|
|
- 存储与计算:`DECIMAL(12,2)`
|
|
|
- 舍入:`ROUND_HALF_UP`,保留 2 位
|
|
|
- **禁止**用 `float` / `number` 做金额运算
|
|
|
- **单位换算**:内部 `kgToLb` / `cmToIn` 保留 2 位(GD-13);调用 MotherShip `axel/quote` 前须 `toAxelWholePounds` / `toAxelWholeInches` 向上取整
|
|
|
|
|
|
### API 响应
|
|
|
|
|
|
统一包络:
|
|
|
|
|
|
```json
|
|
|
{ "code": 0, "message": "ok", "data": { } }
|
|
|
```
|
|
|
|
|
|
错误使用标准 `error_code`(见 PRD §5.6 / `domain-rules.mdc` §10)。
|
|
|
|
|
|
### 异步与错误
|
|
|
|
|
|
- 所有 `async` 必须 `catch` 并 `log`(禁止空 catch)
|
|
|
- 用户可见操作必须有 loading + 防重复提交 + 成功/失败反馈
|
|
|
- 外部调用(RPA/Redis/MySQL)须处理超时与失败分类
|
|
|
|
|
|
### 前端
|
|
|
|
|
|
- **界面文案一律中文**(除 Mothership 原文抓取字段)
|
|
|
- 查价表单须支持地址联想点选、`pallet_count`、kg/cm 展示与 lb/in 换算
|
|
|
- 轮询:`GET /quotes/{id}`,间隔 2s,最长 30s
|
|
|
|
|
|
### Git
|
|
|
|
|
|
- 禁止在 `main` 直接开发;使用 feature 分支
|
|
|
- Commit:`feat|fix|docs|refactor(module): 描述`(可附 PRD/任务编号)
|
|
|
- 不提交 `.env`、`.rpa/` 敏感状态(除示例模板)
|
|
|
|
|
|
### 变更流程
|
|
|
|
|
|
1. 先查 PRD / 技术设计 / `implementation-plan.mdc` 是否已有规则
|
|
|
2. 数据结构变更 → Prisma migration → 再改代码
|
|
|
3. 改逻辑 → 补/跑对应测试
|
|
|
4. 完成一步 → 在 `implementation-plan.mdc` 标记 `Done` + 两行执行记录
|
|
|
|
|
|
---
|
|
|
|
|
|
## 红线(绝对不能违反)
|
|
|
|
|
|
### 业务红线
|
|
|
|
|
|
| # | 规则 | 违反后果 |
|
|
|
|---|------|----------|
|
|
|
| R1 | **一次询价 = 1 次 RPA**,单次必须返回 **4 档** | 成本翻倍、验收失败 |
|
|
|
| R2 | `service_level` / `rate_option` **不得进入** cache key | 缓存串档、价格错误 |
|
|
|
| R3 | 地址必须联想**点选**,须 `place_id` + `selected_from_suggestions=true` | Mothership 拒单或 RPA 失败 |
|
|
|
| R4 | 使用 `pallet_count`(托盘数),**禁止**件数/箱数字段 | 与 Mothership 口径不一致 |
|
|
|
| R5 | 加价仅对 `raw_freight`,上限 30%;未配置客户 0% | 财务口径错误 |
|
|
|
| R6 | L3 stale **仅** RPA 失败降级只读,正常路径不得读 L3 | 展示过期价 |
|
|
|
| R7 | `MOTHERSHIP_QUOTE_URLS` 必填且 headed 录制验证;**禁止**默认 `dashboard.mothership.com` 或 `/quote` | RPA 100% 失败 |
|
|
|
| R8 | login 页且无凭据 → `STRUCT_CHANGE`,**禁止**误报 `SESSION_EXPIRED` | 排障误导、错误重试 |
|
|
|
| R9 | RPA selector **禁止**硬编码上线;必须 `RPA_SELECTOR_*` env 或 `lib/rpa/selector-specs.ts` 统一管理 | 页面改版即挂 |
|
|
|
| R10 | `RPA_MOCK_MODE=false` 为验收/生产默认;Mock 仅 CI/单测 | 假数据上线 |
|
|
|
|
|
|
### 工程红线
|
|
|
|
|
|
| # | 规则 |
|
|
|
|---|------|
|
|
|
| E1 | PRD 与代码冲突时 **以 PRD 为准**;实现细节以技术设计为准 |
|
|
|
| E2 | 禁止擅自新增/修改 API 字段、错误码、表字段(须先改 PRD + migration) |
|
|
|
| E3 | 禁止空 catch、静默失败、吞异常 |
|
|
|
| E4 | 禁止占位 UI / 假按钮 / 「敬请期待」入口 |
|
|
|
| E5 | 禁止物理删除业务数据;使用逻辑删除 `is_deleted` |
|
|
|
| E6 | 禁止跳过 migration 直接改库;禁止代码引用未迁移字段 |
|
|
|
| E7 | 禁止在 main 分支直接提交;禁止 `git push -f` 到 main |
|
|
|
| E8 | 禁止提交密钥(`.env`、storageState、真实 token) |
|
|
|
| E9 | 禁止未授权升级依赖或引入未评审大包 |
|
|
|
| E10 | 多租户:`customer_id` 必须校验归属,禁止越权查历史/报价 |
|
|
|
|
|
|
### 安全红线
|
|
|
|
|
|
| # | 规则 |
|
|
|
|---|------|
|
|
|
| S1 | 查价 API 必须校验宿主 Service Token |
|
|
|
| S2 | 管理端 API 必须 JWT + RBAC |
|
|
|
| S3 | 高危操作须审计日志(`AuditLog`) |
|
|
|
| S4 | 限流:单客户 60 次/分钟(见 PRD) |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 技术方案
|
|
|
|
|
|
### 架构总览
|
|
|
|
|
|
```
|
|
|
宿主系统(内嵌查价 UI)
|
|
|
│ Bearer Token + customer_id
|
|
|
▼
|
|
|
Next.js API(QuoteOrchestrator)
|
|
|
├─ L1 idem:{request_id} ──命中──► 直接返回
|
|
|
├─ L2 quote:{cargo_hash} ──命中──► 加价后返回
|
|
|
└─ 未命中 ──► BullMQ(quote-rpa) ──► RPA Worker
|
|
|
│ │
|
|
|
│ ├─ QuotePageAdapter(多 URL 探测)
|
|
|
│ ├─ session-manager(storageState)
|
|
|
│ └─ mothership.ts(填表+四档抓取)
|
|
|
▼
|
|
|
MySQL quote_record / Redis L2/L3
|
|
|
```
|
|
|
|
|
|
### 缓存策略(GD-2)
|
|
|
|
|
|
| 层 | Key | TTL | 用途 |
|
|
|
|----|-----|-----|------|
|
|
|
| L1 | `idem:{request_id}` | 24h | 幂等完整响应 |
|
|
|
| L2 | `quote:{cargo_hash}` | 3min | 四档原始价(未加价) |
|
|
|
| L3 | `stale:{cargo_hash}` | 30min | RPA 失败降级 |
|
|
|
|
|
|
读取顺序:**L1 → L2 → RPA →(失败)L3**。
|
|
|
|
|
|
### 询价状态机
|
|
|
|
|
|
```
|
|
|
processing → done (cache|rpa|stale) | failed (QUOTE_UNAVAILABLE|QUOTE_TIMEOUT)
|
|
|
done --3min--> expired
|
|
|
```
|
|
|
|
|
|
### RPA 方案(v1.2 + 生产直连)
|
|
|
|
|
|
1. `QuotePageAdapter` 按 `MOTHERSHIP_QUOTE_URLS` 顺序探测可用入口
|
|
|
2. `session-manager` 加载/写回 `RPA_STORAGE_STATE_PATH`;`ensureMothershipStorageState` 可自动 bootstrap
|
|
|
3. **报价主路径(生产)**:`RPA_QUOTE_MODE=direct` → node `fetchAxelQuoteItems`(axel HTTP)
|
|
|
4. **兜底(开发/可关)**:Direct 失败后 Widget 浏览器填表;生产设 `RPA_DISABLE_WIDGET_QUOTE_FALLBACK=true`
|
|
|
5. 地址:RPA 枚举候选 → 用户确认 → placeId commit
|
|
|
6. 失败分类:入口错误 / 地址未锁定 / 验证码 / 结构变更 / 数据无效 / axel 400(整数校验)
|
|
|
|
|
|
上线门禁:`probe-rpa` **连续 3 次 exit 0**(TC-601 + §12.7);系统链路 `probe:human-doc-system` 10/10。
|
|
|
|
|
|
### 加价公式
|
|
|
|
|
|
```
|
|
|
markup_amount = ROUND_HALF_UP(raw_freight × markup_percent / 100, 2)
|
|
|
final_total = ROUND_HALF_UP(raw_total + markup_amount, 2)
|
|
|
```
|
|
|
|
|
|
### 关键 API
|
|
|
|
|
|
| 方法 | 路径 | 调用方 |
|
|
|
|------|------|--------|
|
|
|
| POST | `/api/host/quote/candidates` | 宿主(方式 A,免 Token) |
|
|
|
| POST | `/api/host/quote/submit` | 宿主(方式 A,同步返回报价) |
|
|
|
| POST | `/api/quotes` | 宿主(方式 B) |
|
|
|
| GET | `/api/quotes/{quote_id}` | 宿主(方式 B 轮询) |
|
|
|
| POST | `/api/addresses/mothership-candidates` | 宿主(方式 B 地址候选) |
|
|
|
| GET | `/api/quotes/history` | 宿主 |
|
|
|
| PUT | `/api/markup-configs/{customer_id}` | 宿主 |
|
|
|
| GET | `/api/alerts` | 管理员 |
|
|
|
|
|
|
### 环境变量(RPA 最小集)
|
|
|
|
|
|
| 变量 | 必填 | 说明 |
|
|
|
|------|------|------|
|
|
|
| `MOTHERSHIP_QUOTE_URLS` | 是 | 逗号分隔匿名报价 URL |
|
|
|
| `RPA_SELECTOR_*` | 是 | 录制产出 selector |
|
|
|
| `RPA_STORAGE_STATE_PATH` | 否 | 默认 `.rpa/mothership-storage.json` |
|
|
|
| `RPA_MOCK_MODE` | 否 | 默认 `false` |
|
|
|
| `RPA_HEADLESS` | 否 | 调试设 `false` |
|
|
|
| `RPA_QUOTE_MODE` | 否 | 生产 `direct` |
|
|
|
| `RPA_DISABLE_WIDGET_QUOTE_FALLBACK` | 否 | 生产建议 `true` |
|
|
|
| `HOST_PUBLIC_API_ENABLED` | 否 | 宿主方式 A 开关 |
|
|
|
| `DATABASE_URL` / `REDIS_URL` | 是 | 基础设施 |
|
|
|
|
|
|
完整列表见 `.env.example` 与技术设计 §6.2。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 新人上手路径
|
|
|
|
|
|
1. **先读本文「AI 项目交接文档」** → PRD §1–3 → 技术设计 §1、§7
|
|
|
2. `cp .env.example .env`,配 `DATABASE_URL` / `REDIS_URL` / `HOST_SERVICE_TOKENS`
|
|
|
3. `npm run dev:start` 或 `docker compose up -d` + `npm run dev`
|
|
|
4. 打开 http://localhost:3000/embed-demo 走通查价
|
|
|
5. RPA 开发:先 `npm run record:mothership`,再 `npm run probe:rpa`
|
|
|
6. 接任务前查 [工程任务拆解 v1.2](./查价系统-工程任务拆解-v1.2.md) 当前 Phase
|
|
|
|
|
|
---
|
|
|
|
|
|
## 排障速查
|
|
|
|
|
|
| 现象 | 优先检查 |
|
|
|
|------|----------|
|
|
|
| **axel/quote HTTP 400 整数** | 公制货物是否经 `toAxelWhole*`;worker 日志 `Weight/Length must be a whole number` |
|
|
|
| **GET /quotes/{id} 返回 HTML** | 动态路由是否打包进镜像;`deploy/pack-server-upload.ps1` 预检 |
|
|
|
| **embed 询价超时/failed** | **先看「AI 项目交接文档」§3 BLK-001~003**;worker 日志 `no_place_request` |
|
|
|
| **直连失败且极慢** | 是否未关 Widget fallback;查 `RPA_DISABLE_WIDGET_QUOTE_FALLBACK` |
|
|
|
| **Service Token 无效** | token 是否在 `HOST_SERVICE_TOKENS`;embed-demo 与 `NEXT_PUBLIC_EMBED_DEMO_*` 一致 |
|
|
|
| 创建即 done + cache | L2 假阳性;换 cargo 或清 Redis `quote:*` |
|
|
|
| place commit 失败 | headed 对比 network:`GET place/{id}` 是否触发 |
|
|
|
| RPA 进登录页 | `MOTHERSHIP_QUOTE_URLS` 是否 dashboard;storageState 是否失效 |
|
|
|
| preCheck 失败 | `RPA_SELECTOR_*` 是否过期;headed 重录 |
|
|
|
| 地址填不进去 | 是否缺少 `place_id`;联想是否 click 而非 Enter |
|
|
|
| 只返回 2 档 | 违反 GD-1,检查四档抓取逻辑 |
|
|
|
| 429 | 限流;检查 `customer_id` 请求频率 |
|
|
|
| Worker 不消费 | Redis 连通、`RPA_QUEUE_ENABLED`、BullMQ 队列 |
|
|
|
|
|
|
失败截图目录:`.dev/logs/`(如 `rpa-failure-*.png`)。
|
|
|
|
|
|
---
|
|
|
|
|
|
## 文档维护
|
|
|
|
|
|
- 业务变更 → 先改 PRD → 同步技术设计 → 更新本文「项目信息/红线/技术方案」摘要
|
|
|
- 命令变更 → 以 `package.json` `scripts` 为准,同步本文「常用命令」
|
|
|
- 规则变更 → 同步 `cursor_project_rules/domain-rules.mdc`
|