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

29 KiB
Raw Blame History

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: 40b34ee81f4207de6797b1a81ded6459b6dd776d synchronized_at: 2026-08-28T14:09:31Z

架构与代码地图

MVP 架构

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。
  • 蝦皮规格映射独立保存在 shopee_product.specs_json:颜色只能选择关联 PDD 当前可选颜色,允许多个蝦皮颜色共用一个 PDD 颜色;尺码自动匹配只预览格式统一后的唯一确定结果。PDD 目标规格消失后页面标记失效,映射保存和采购创建均拒绝继续使用;无需新增数据库表或 Android 能力。

最小业务数据

表 必要内容
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 幂等执行记录、阶段、规则哈希、固化规格决策和结构化错误
ai_matching_setting 唯一单例的启用状态、OpenAI-compatible Base URL、模型、超时、内部部署明文 API Key 和更新人;仅管理员设置接口可以读取该字段

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/、web/src/views/goauto/shopee-products/;Admin 按确认原型提供颜色下拉人工映射、确定性尺码批量预览、失效提示和单一保存入口,SYB 快捷入口可突出目标颜色;不修改两端原始规格
虾皮商品档案增量迁移 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 商品明细增量迁移 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
SYB 每小时自动同步 server/app/goauto/sybimport/import_handler.go 提供手动/定时共用的 StartImport 服务;scheduled_job.go 通过 go-admin 定时任务调度,固定按 Asia/Shanghai 取今天和昨天;任务注册迁移为 server/cmd/migrate/migration/version-local/1786701600000_syb_hourly_sync_job.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/;batch-preview 通过批量预加载 SYB、蝦皮、PDD 与最新任务执行快速只读预检,不访问 AI;batch-create 仍按最新数据逐项完整复核并可在必要时调用 AI;既有追加迁移为 server/cmd/migrate/migration/version-local/1786701200000_purchase_state_machine.go
采购规格标准化匹配与 AI Provider 设置 server/app/goauto/aimatching/;服务端先做繁简、空白/全半角/大小写和公斤/斤的唯一确定性匹配,再按需调用单一 OpenAI-compatible Provider;1786701300000_ai_matching_setting.go 创建设置表,1786701400000_ai_matching_setting_plain_api_key.go 将原加密列迁移为内部明文 api_key,仅管理员读取
任务领取、结果、重置与删除 server/app/goauto/task/
管理端基线 web/(go-admin-ui v3.0.0)
管理端闭环页面 web/src/views/goauto/
备货采购服务端路径 purchase_task.task_type 与迁移 1787885300000_stock_purchase.go;POST /api/admin/v1/purchase-tasks/stock 由 server/app/goauto/purchase/service.go 校验 PDD 当前可选规格并固化 direct_select,复用既有任务状态机和设备/账号互斥;重试、替换和 SYB 回填显式排除 stock
Admin 采购任务列表、详情与人工处理 web/src/views/goauto/purchase-tasks/、web/src/api/goauto/purchase-tasks.js;创建入口不在本模块
Admin AI 规格匹配设置 web/src/views/goauto/ai-matching-settings/、web/src/api/goauto/ai-matching-settings.js;管理员可查看、维护和测试 Provider(包括内部明文 API Key);采购员菜单硬排除,仍只可通过受控 API 查看启用状态
GoAuto 系统菜单与采购员权限基线 server/app/goauto/access/ 统一声明 11 个模块、路由元数据和 Admin API 权限矩阵;迁移 1787885400000_goauto_menus.go 幂等维护 sys_menu、sys_menu_api_rule、采购员默认菜单绑定和 Casbin 固定白名单。web/src/router/index.js 只保留公共路由,web/src/store/modules/permission.js 在登录时根据 GET /api/v1/menurole 返回结果注册业务路由与侧栏;退出、凭据异常和切换角色会清除旧动态路由,未授权深链接回退工作台。菜单可见性与 API 授权彼此独立
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;商品列表按店铺名称包含匹配,并支持最多 100 个多行订单号精确筛选;确认原型见 #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、表或执行动作时必须同步更新本代码地图与共享契约。

cmautobuy 商品离线导入

server/cmd/import-cmautobuy-products/ 是 #70 的独立单向导入入口,转换逻辑位于 server/app/goauto/cmautobuyimport/。它不注册 HTTP 路由、不参与服务启动,也不是数据库 schema migration。

导入器从 cmautobuy MySQL 只读一致性快照读取有效 pdd_products、shopee_products 和 shopee_skus,再写入 GoAuto 的 pdd_product、shopee_product。旧 PDD 组合 SKU 聚合为 GoAuto 通用维度;同一颜色出现多个不同价格时不猜测价格。旧组合 spec_mappings、SYB、任务、订单和其他域数据不进入导入范围。

默认模式是 dry-run;显式 --apply 才会在目标 MySQL 单事务覆盖同业务键商品。来源与目标配置都来自未跟踪 YAML,连接串和密码不输出。来源蝦皮的字符串 pdd_goods_id 必须通过目标 PDD goods_id 换算为数字 pdd_product_id,提交前重新校验 JSON、唯一键和关联完整性。

Android Agent 0.3 设备端外壳(#88)

  • Android 入口仍为单 Activity,但从动态单页升级为 AndroidX Fragment + Material BottomNavigationView 的四 Tab 外壳:状态、采集、采购、设置;首次启动默认进入状态页,并持久保存最近一次有效 Tab。
  • android/app/src/main/java/cn/ilapage/goauto/agent/ui/ 保存状态页、设置页、任务占位页、无障碍真实就绪检测和共享 UI 组件;采集/采购历史列表与详情由 #90 接入。
  • 状态页同时读取系统启用的 AccessibilityService 组件和 GoAutoAccessibilityService.instance 绑定事实,区分“未开启”“已开启等待连接”“已开启并就绪”,并显示服务连接、当前任务和最近心跳。
  • 设置页维护服务器地址和设备名称,测试连接只访问 GET /api/v1/health;保存后沿用当前 installId / Device Token 重新连接。执行任务或测试连接期间表单与按钮禁用。
  • 设置页只显示 Token 是否配置,不返回或显示明文;不包含 PDD 账号、采购写操作或支付入口。
  • AgentStateStore 保存界面所需的当前任务类型/编号摘要;任务结束后由执行服务清除。它不保存控件树、截图、完整规则或凭据。

Android Agent 任务结束冷却与返回(#89)

  • AgentForegroundService 在采集结果已提交,或采购结果与 Outbox 已在同一事务安全保存后,建立 5 秒任务结束冷却;不新增第二套队列轮询器。
  • 冷却到期仍由现有任务轮询按“采购优先、再采集”依次确认两个队列都为空。任何新任务、网络/服务端异常或本地未解除的不可逆采购边界都会取消返回。
  • IdleReturnCoordinator 只保存冷却时间、自动化前台包名与无障碍前台切换序号,不保存控件树或截图。用户切换到其他 App 后,即使又返回 PDD,也不会被强制拉回 Agent。
  • 返回请求只允许从 Agent 本次自动化停留的 PDD 或受支持浏览器页面发起,并由无障碍服务打开 MainActivity:现有 Activity 保持当前 Tab,重建时恢复最近一次有效 Tab;Android 后台 Activity 启动限制导致失败时只提示手动打开,不重复强拉。
  • 任务执行和冷却分别使用带超时的亮屏锁;MainActivity 仅在运行态标记要求时设置 FLAG_KEEP_SCREEN_ON。任务、新任务接管、取消、返回或服务销毁后统一释放,不绕过设备 PIN、图案或密码。

Agent 任务历史(#90)

  • Android 的采集任务与采购任务 Tab 由 TaskHistoryFragment 承载,复用搜索、状态筛选、分页、加载/空/失败状态和只读详情能力。
  • AgentApiClient 调用服务端当前设备历史接口;任务编号规范化由客户端先处理,服务端再次校验。
  • 服务端采集与采购模块各自使用 agent_history.go 负责 Device Token 设备隔离、最近 30 天过滤、分页和最小化 DTO。
  • purchase_task.actual_unit_price_cent 是可空字段,只记录 Agent 实际观察到的采购单价;旧任务不回填。

Agent 受控重新采集(#91)

  • 服务端 task/lifecycle_service.go 的同一 Reset 事务同时服务管理端和设备端;设备端入口使用显式 Device Token 模式,不能用空 Token 退化为管理端路径。
  • POST /api/agent/v1/collection-tasks/{taskId}/reset 只返回最小状态,设备归属、忙碌和商品冲突在事务内检查。
  • Android TaskHistoryFragment 只在采集终态详情显示“重新采集”,确认成功后调用 AgentForegroundService.start 唤醒现有轮询,任务执行仍由 next/claim/start 完成。

Agent 受控采购重试(#95)

  • 服务端 server/app/goauto/purchase/retry.go 的 AgentRetry 使用 Device Token 校验任务归属,并复用 Admin BatchRetry 的当前档案、不可逆边界、设备能力/忙碌和幂等创建逻辑;旧失败任务不修改,新任务固定原设备。
  • GET /api/agent/v1/purchase-tasks 与详情通过 retryable / retryDisabledReason 返回服务端资格结论;POST /api/agent/v1/purchase-tasks/{taskId}/retry 只接受 requestId,不接受客户端指定设备、规格或规则。
  • Android TaskHistoryFragment 在采购失败列表和详情按服务端资格显示单任务“重试采购”,二次确认后调用新接口;成功后唤醒既有 AgentForegroundService 队列,实际执行仍只走 next → claim → start。Agent 不实现批量重试、取消订单、修改既有订单或支付。

Android 系统辅助功能按钮(#97)

  • Android 8.0 及以上的 GoAutoAccessibilityService 请求系统 flagRequestAccessibilityButton,服务连接后向 AccessibilityButtonController 注册单一回调,销毁时注销;服务重连不会叠加回调。
  • 单击由系统分配给 GoAuto 的辅助功能按钮时,只调用 openAgentPreservingTab(),以前台 MainActivity 打开 Agent 并保持最近一次有效 Tab;不领取、创建、重置或重试任务,不打开 PDD,也不改变 5 秒空闲返回规则。
  • 不支持或未分配系统辅助功能按钮的 ROM 保持安全降级;项目不创建自定义悬浮窗。

Android Agent 状态页手动检查(#98)

  • AgentStatusFragment 使用 Android 原生下拉刷新容器;只有状态页顶部下拉会请求一次立即检查,采集/采购历史页的下拉只刷新记录,两者语义不同。
  • AgentForegroundService.checkNow 只向既有单线程同步与调度入口提交一个可合并请求;不建立第二套轮询器,也不改变 15 秒自动轮询、采购优先、设备互斥或服务端租约。
  • 手动请求在已有同步运行时保留一个待处理标记,当前同步结束后再执行一次;重复手势不会并发请求。
  • 结果通过仅限当前应用包的广播返回状态页,区分无任务、采集任务、采购任务、设备忙、配置/认证和网络错误。状态页无障碍信息只读,唯一系统设置入口保留在设置 Tab。
  • 手动检查不创建、重置或重试任务,不绕过无障碍与身份校验,不打开 PDD,不修改地址、不创建订单、不支付。

Agent 任务记录刷新与摘要缓存(#99)

  • Android 的采集/采购历史页使用单个 SwipeRefreshLayout 包裹既有滚动页,只在列表顶部下拉刷新当前查询;请求中合并重复手势,加载失败页仍保留可见重试入口。
  • 设置页保存 1/3/7/15/30 天记录范围(默认 7 天),用当前 Device Token 分页读取采集与采购摘要。两类读取独立结算,支持完整、空、部分和失败反馈。
  • TaskHistoryCache 使用独立 SharedPreferences 保存当前同步范围的列表摘要,网络读取失败时可显示最近同步摘要;Token 继续只保存在 Android Keystore 保护的设备身份存储中,任务详情不离线镜像。
  • 服务端两个 Agent 历史列表查询增加 days=1..30,并继续在数据库查询中强制设备隔离、分页上限和 30 天最大窗口;详情读取仍保持原 30 天边界。
  • 该链路只读,不接入任务领取、执行、重置、重试、PDD 导航、地址修改、创建订单或支付。

Agent 当前页面临时采集(#101)

  • 服务端入口位于 server/app/goauto/task/current_page.go:CreateCurrentPage 原子完成设备校验、任务创建、规则快照和租约;IdentifyCurrentPage 对 Agent 上报的白名单链接重新校验并最终裁决 goods_id,负责 PDD 商品复用/创建和任务身份绑定;服务端保留受限正文解析作为短链兜底。路由为 /api/agent/v1/current-page-collection-tasks 及其 /{taskId}/identify。
  • 默认规则设置位于 server/app/goauto/rule/agent_manual_setting.go,使用单行表 agent_manual_collection_setting。迁移 1787790000000_agent_current_page_collection.go 增加任务来源、识别幂等字段、可空 PDD 外键和默认规则表,并对旧任务回填 admin。
  • collection_task.source 目前只允许 admin / agent_current_page。当前页面任务创建时由设备运行槽防并发,识别商品后再参与 PDD 商品活动槽;结构化结果仍写入既有任务及规格子表,不新增临时任务表。
  • Android 入口仍由 TaskHistoryFragment 承载;点击确认时先启动 AgentForegroundService,不先把 Agent 退到后台。服务先用 TaskExecutionMutex 预占本地串行槽,服务端创建成功后把预占转为真实 taskId;前台已是 PDD 时不执行返回,前台仍是 Agent 且近期见过 PDD 时只尝试一次返回,否则使用不清理任务栈、不带深链参数的 PDD 启动 Intent 恢复现有任务,并在有界等待确认 PDD 前台后才识别身份和调用既有 PddProductDetailCollector。
  • CurrentPageIdentityRunner 负责页面证据、唯一分享/复制入口和面板清理;ClipboardRelayActivity 使用独立、不可导出的短生命周期任务在前台读取新鲜剪贴板,完成后移除中转任务并露出原 PDD 页面。PddShareLinkExpander 仅在手机侧用无 Cookie、无项目凭据的移动端 GET 有界展开白名单短链,逐跳校验并最多读取 64KB 正文;Agent 不裁决 goods_id。原始剪贴板、链接与响应正文不进入日志或缓存。
  • v2 规则新增可选 currentPageIdentity(分享/复制别名和三个有界超时),旧 v2 规则由 Android 使用安全默认值;新设备能力为 collector.pdd.current-page-share.v1。
  • 该路径复用既有结果/失败接口、PDD 最新档案写回、任务结束返回和采集间隔,不调用浏览器导航,不进入任何采购、地址、创建订单或支付代码。

SYB 档口入库码(#119~#122)

  • 数据模型与迁移位于 server/app/goauto/models/syb_inner_code.go、server/app/goauto/migrations/ 和 server/cmd/migrate/migration/version-local/1786701500000_syb_inner_code.go;Excel 业务记录、逐件入库码、匹配计划、回写批次、检查点、租约和幂等请求分表保存。
  • server/app/goauto/sybinnercode/ 负责 Excel 解析、物理删除、异步只读匹配、回写预览、全局串行回写、逐件回读确认、结果不明确后的只读复核和启动恢复。
  • server/app/goauto/sybclient/ 提供 SYB 读取与三个入库码写接口;匹配器只依赖只读接口,不能取得写能力。真实写入只由用户确认后的回写批次触发。
  • Admin API 注册在 server/app/goauto/sybinnercode/router.go,页面位于 web/src/views/goauto/syb-inner-codes/,菜单入口为“档口入库码”。
  • 服务启动时,未开始的 queued 记录释放回可回写,已经进入远端动作的 applying 记录转为 needs_check,不自动重放写请求。

PDD 商品替换数据模型(#129)

  • server/app/goauto/models/replacement.go 定义审计主表 pdd_product_replacement 与分项表 pdd_product_replacement_item;版本迁移为 1787885000000_pdd_product_replacement.go,只新增表,不修改既有表和数据。
  • 主表用 origin_type + origin_task_id 区分失败采集任务和失败采购任务来源,以 target_collection_task_id 保存替代商品采集证据,以 created_by_device_id 保存发起设备。当前设备模型没有操作人绑定,因此该字段只能追溯到设备,不能追溯到采购员账号。
  • 主表的可空 active_slot 与 source_product_id 组成唯一索引,使同一源商品跨 SQLite、MySQL 和 PostgreSQL 同时最多存在一条 active 替换记录;历史记录使用 superseded,不软删除、不提供删除接口。
  • 分项表按 replacement_id + shopee_product_id 唯一保存本次实际影响集合,并独立记录 matching / matched / manual_required、匹配来源、置信度和持久 worker 的尝试/错误信息。某条采购任务能否续做只能读取对应虾皮商品的分项状态,不能读取主表总体进度。
  • server/app/goauto/replacement/ 提供内部登记、幂等冲突校验、来源/采集证据校验、目标有效性、活动记录唯一性和环检测,以及只读审计查询;管理端只读路由为 GET /api/admin/v1/pdd-product-replacements 与 GET /api/admin/v1/pdd-product-replacements/{replacementId}。Agent 没有直接写入该领域的 HTTP 权限。

PDD 商品替换生效与规格匹配(#131)

  • replacement.Service.RegisterAndActivate 在同一数据库事务内锁定受影响采购任务与虾皮商品:虾皮商品改指替代 PDD、清除旧规格映射,待执行/待探测采购任务取消并释放租约与运行守卫;执行中和终态任务的 PDD 外键及全部快照保持原样,旧 PDD 仅置为 disabled。
  • pdd_product_replacement_item 是持久匹配工作项;pdd_product_replacement_worker_lease 为多实例全局租约。事务提交后异步唤醒,API 服务启动时恢复遗留 matching,最多有限重试,失败转 manual_required。
  • 匹配复用 aimatching.Service.Resolve。确定性 exact_match 可直接确认且不要求置信度;ai_match 只有在开关启用、置信度达到 ai_matching_setting.auto_confirm_min_confidence(默认 0.9)、候选/角色/原因/输入版本全部通过校验时才确认。
  • 人工维护虾皮规格映射后,同一事务同步分项状态与主表 completed / completed_partial 聚合;worker 只更新仍为 matching 且商品关联、规格 JSON 未变化的行,不覆盖人工结果。
  • 纠错使用 CorrectAndActivate:原记录转 superseded,新建原始失效商品到新目标的记录,只迁移原 replacement_item 冻结的虾皮商品集合,不影响共享中间目标的其他商品。
  • 历史采购执行身份继续以 PDDGoodsIDSnapshot / PDDURLSnapshot 为准;当前采购查询未发现按可变 pdd_product_id 聚合历史数据的实现,因此本工单无需改写统计 SQL。