Files
goauto/docs/02-architecture-and-code-map.md
T

138 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Architecture-and-Code-Map
wiki_url: https://git.ilapage.cn/OPC/goauto/wiki/Architecture-and-Code-Map.-
wiki_revision: 8f036bcfd2db01aeb2b1c0559d066267e9d7908c
synchronized_at: 2026-08-21T07:01:32Z
<!-- gitea-wiki-mirror:end -->
# 架构与代码地图
## MVP 架构
```text
go-admin-ui
│ REST:PDD商品、规则、任务、任务详情
▼
go-admin Server ───────── 数据库
│ ├─ pdd_product
│ REST:注册/心跳/任务 ├─ collection_rule
│ ├─ collection_task(任务+结果)
▼ └─ dimension/value/color_price/sku 子表
Android Portal/Agent
├─ 注册、心跳与设备串行锁
├─ 领取指定任务或空闲领取未指定任务
├─ 执行任务内的 URL 与规则快照
└─ 提交结构化结果或明确错误
```
## 最小闭环
1. 管理员添加 PDD URL,服务端规范化 URL 并提取唯一 `goods_id`。
2. 管理员创建规则;规则创建后立即可用于创建任务。
3. 管理员从一个 PDD 商品创建任务,可指定设备,也可留空等待空闲设备领取。
4. 任务固化 URL、goods_id 和完整规则快照。
5. Android 串行执行并把结果写回同一条任务;规格与 SKU 写入任务结果子表。
6. 同一事务把 `completed` 结果全量覆盖到 PDD 商品最新档案,把 `completed_partial` 明确采集到的字段和规格安全合并;`failed` 不修改商品。
7. 管理员在任务详情查看原始结构化结果,也可在 PDD 商品页面查看和人工覆盖最新商品资料。
## 关键设计
- `pdd_product.goods_id` 唯一;重复时提示商品已存在,不重复新增。
- 规则没有草稿、发布和版本流程;软删除只阻止创建新任务。
- 已有任务不依赖规则当前状态,始终执行自身 `rule_snapshot`。
- `collection_task` 同时保存任务生命周期和结果摘要,不建立 `collection_result` 主表。
- 同一 PDD 商品最多存在一个 `pending` 或 `running` 任务。
- 指定设备只能由该设备领取;未指定任务由在线空闲设备原子领取。
- 重置在事务中清空原结果,保留 URL、goods_id、规则和设备快照,状态恢复为 `pending`。
- Android 本地互斥与服务端原子领取共同保证单设备串行。
- 正式采购在 Android 本地 SQLite 事务中先保存不可逆状态、稳定服务端请求 ID 和脱敏最终确认快照,再通知服务端并只允许一次创建订单点击;重启后只重放服务端标记、只读核单或上报,不再次点击。
- 原始控件树和截图不持久化;Android Agent 端第一期不使用 OCR/VLM。服务端 SYB 登录验证码识别是唯一例外,见 [#48](https://git.ilapage.cn/OPC/goauto/issues/48)。
## 最小业务数据
| 表 | 必要内容 |
|---|---|
| `agent_device` | 唯一 `install_id`、设备信息、状态、Token 摘要、版本化能力、最后心跳 |
| `pdd_product` | 唯一 `goods_id`、当前 URL、标题、店铺、数字销量/评价、三态状态和通用多维 `specs_json` 最新值 |
| `collection_rule` | `id`、`name`、`content_json`、创建/更新时间、`deleted_at` |
| `collection_task` | 商品/设备外键、五态状态、URL/goods_id/规则快照、租约、结果摘要、错误和时间 |
| `collection_dimension` | 任务、维度键、名称、排序 |
| `collection_dimension_value` | 维度、值、排序 |
| `collection_color_price` | 任务、颜色、该颜色统一使用的整数分价格 |
| `collection_sku` | 任务、整数分价格、可用性、完整性 |
| `collection_sku_value` | SKU 与规格值的多对多关联 |
| `pdd_account` | 可选的账号调度引用,只保存名称和状态,不保存凭据 |
| `purchase_task` | 商品外键和不可变快照、执行模式、状态/租约 guard、价格边界、订单、人工支付复核、物流与回填事实 |
| `purchase_task_attempt` | `task_id + attempt_id` 幂等执行记录、阶段、规则哈希、固化规格决策和结构化错误 |
`collection_task` 的状态仅为 `pending`、`running`、`completed`、`completed_partial`、`failed`。设备身份和心跳表属于 Agent 领取任务的必要基础,不承载 PDD 业务数据。
设备以 JSON 数组保存最后一次注册或心跳上报的版本化能力。v1 任务兼容未上报能力的旧 Agent;v2 任务在创建指定设备任务、获取下一任务、领取和开始四个边界重复校验能力,避免旧 APK 执行未知规则。
数据库使用两个可空 guard 列表达跨数据库唯一约束:活动任务的 `active_slot=1`,运行中设备的 `device_run_slot=1`;终态记录对应列为 `NULL`。复合唯一索引据此保证同商品最多一个活动任务、同设备最多一个运行中任务,同时允许保留任意数量的终态历史任务。状态与 guard 列还有数据库检查约束,必须在同一条状态变更语句中更新。
采购表使用同一 guard 思路:`purchase_task.active_slot` 保证同一 SYB 明细最多一个活动任务,`device_run_slot` 保证设备串行,`account_run_slot` 在账号已知时保证账号串行。账号未知是合法状态。正式任务必须引用 SYB 的规则由模型钩子和后续创建服务双重校验;MySQL 8.4 不允许 `syb_product_id` 同时参与带参照动作的外键和跨字段 CHECK,因此不在该列添加数据库 CHECK。
## 配置分层
配置分三层,下层覆盖上层:
| 层 | 文件 | 是否进 Git | 放什么 |
|---|---|---|---|
| 1 默认值 | `server/config/settings.yml` | **是** | 应用配置和非机密运维参数(`extend.syb` 的 `baseurl` / `pagesize` / `maxmatches` / `ocrurl`) |
| 2 部署本地值 | `config.yaml`(仓库根) | **否**(`.gitignore`) | 数据库、端口、顺云宝凭据 |
| 3 覆盖值 | `GOAUTO_*` 环境变量 | — | 服务、容器和 CI 用;优先级最高 |
`[必须]` **凭据只允许出现在第 2、3 层。** `settings.yml` 被 Git 跟踪,任何时候都不能往里写账号密码。
服务端自己读第 2 层(`config/local.go` 的 `ApplyLocalConfig`),查找顺序为 `GOAUTO_CONFIG` 指定的路径 → 当前目录 `./config.yaml` → 可执行文件同级目录。**找不到不是错误**:容器场景只用环境变量,本来就没有这个文件。回调注册在 `ApplyEnvironment` 之前,环境变量因此始终有最后决定权。
`[必须]` 第 2 层的标量按 YAML 原始类型读取后统一转字符串。纯数字密码不加引号会被 YAML 读成整数,这里不能因此启动失败——但仍应加引号,否则前导零会丢。
开发启动脚本把 `config.yaml` 的路径通过 `GOAUTO_CONFIG` 传给服务端,不在 PowerShell 里重复解析 YAML。
## 前端伺服
`web/.env.production` 的 `VUE_APP_BASE_API` 为空,即生产构建发的是相对请求,**前端与接口必须同源**。服务端在 `dist/index.html` 存在时伺服构建产物并为 history 路由回退到 `index.html`(`app/admin/router/spa.go`)。
`[必须]` 回退只对非接口路径的 GET 生效。给写错的接口路径回 200 + HTML,客户端看到的会是 JSON 解析错误而不是 404。
`[必须]` `dist` 不存在时不注册 `NoRoute`。开发环境前端跑在 vite 独立端口上,装了回退会把每个真 404 变成一张 HTML 页。
## 已建立的工程入口
| 功能 | 当前目录 |
|---|---|
| 服务端基线 | `server/`(go-admin v2.3.0) |
| 最小闭环数据模型 | `server/app/goauto/models/` |
| 数据迁移与约束测试 | `server/app/goauto/migrations/` |
| go-admin 迁移注册 | `server/cmd/migrate/migration/version-local/1786700000000_goauto_schema.go` |
| 设备注册、认证、停用与吊销 | `server/app/goauto/device/` |
| PDD 商品与规则 | `server/app/goauto/product/`、`server/app/goauto/rule/` |
| PDD 商品档案增量迁移 | `server/cmd/migrate/migration/version-local/1786700500000_pdd_product_archive.go` |
| 虾皮商品档案与规格映射 | `server/app/goauto/shopeeproduct/`(服务端 API 已实现;Admin 页面待实现) |
| 虾皮商品档案增量迁移 | `server/cmd/migrate/migration/version-local/1786700600000_shopee_product_archive.go` |
| SYB 商品明细导入、解析与虾皮档案合并 | `server/app/goauto/sybimport/`(解析、幂等落库、同步编排、导入端点、管理端 API 和 Admin 页面均已实现) |
| 顺云宝(SYB)ERP HTTP 客户端与登录会话 | `server/app/goauto/sybclient/`(登录、OCR 验证码、会话缓存、列表与明细读取;见 [SYB-ERP-Interface-Contract](SYB-ERP-Interface-Contract)) |
| SYB 商品明细增量迁移 | `server/cmd/migrate/migration/version-local/1786700700000_syb_product_import.go` |
| SYB 店铺准入、发现与过滤 | `server/app/goauto/sybshop/`、`server/app/goauto/sybimport/`;迁移 `server/cmd/migrate/migration/version-local/1786700900000_syb_shop.go` |
| SYB 后台导入任务、进度、单任务互斥与启动恢复 | `server/app/goauto/sybimport/sync_run.go`、`sync_run_handler.go`;表 `syb_sync_run`,迁移 `server/cmd/migrate/migration/version-local/1786701000000_syb_sync_run.go` |
| 采购任务数据与类型化规则契约 | `server/app/goauto/models/purchase.go`、`server/app/goauto/purchasecontract/`;迁移 `server/cmd/migrate/migration/version-local/1786701100000_purchase_contract.go` |
| 采购任务单条/批量预检与创建、租约、attempt 幂等、Admin 只读查询和人工处置状态机 | `server/app/goauto/purchase/`;批量编排复用单任务事务且不新增迁移;既有追加迁移为 `server/cmd/migrate/migration/version-local/1786701200000_purchase_state_machine.go` |
| 任务领取、结果、重置与删除 | `server/app/goauto/task/` |
| 管理端基线 | `web/`(go-admin-ui v3.0.0) |
| 管理端闭环页面 | `web/src/views/goauto/` |
| Admin 采购任务列表、详情与人工处理 | `web/src/views/goauto/purchase-tasks/`、`web/src/api/goauto/purchase-tasks.js`;创建入口不在本模块 |
| Admin 失败采购任务批量重试 | `POST /api/admin/v1/purchase-tasks/batch-retry`;服务端 `server/app/goauto/purchase/retry.go` 负责资格判定、逐项幂等创建与部分成功结果,Admin 页面只允许选择服务端标记可重试的行;不修改 Android Agent |
| SYB 店铺管理页面与接口封装 | `web/src/views/goauto/syb-shops/`、`web/src/api/goauto/syb-shops.js`;确认原型快照 `prototypes/49/v1/index.html` |
| SYB 异步导入、当前页采购选择/确认/逐条结果与同步记录页面 | `web/src/views/goauto/syb-products/`、`web/src/views/goauto/syb-sync-runs/`、`web/src/api/goauto/syb-products.js`、`web/src/api/goauto/purchase-tasks.js`;确认原型见 #44 设计证据,导入原型快照为 `prototypes/50/v2/index.html` |
| Android Agent 基线 | `android/app/src/main/java/cn/ilapage/goauto/agent/` |
| Android 无障碍规则执行 | `android/app/src/main/java/cn/ilapage/goauto/agent/automation/` |
| PDD 安全规格入口与评论页误触恢复 | `automation/PddProductDetailCollector.kt`:明确选择语义、底部购买文字、评论上下文排除、一次返回重试和结构化错误;不保存控件树或截图 |
| Android 采购规则解释器与正式地址/订单动作 | `android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseRuleContract.kt`、`PurchaseRehearsalExecutor.kt`、`PurchaseLiveAutomation.kt` |
| Android 采购任务、attempt 与 Outbox 持久化 | `android/app/src/main/java/cn/ilapage/goauto/agent/persistence/`;调度入口 `service/AgentForegroundService.kt` |
| 三端统一验证 | `scripts/verify.ps1` |
以上入口均已落地;新增 API、表或执行动作时必须同步更新本代码地图与共享契约。