Admin:顺运宝货运单同步 #46

Closed
opened 2026-08-09 11:13:54 +08:00 by ila · 1 comment
Owner

基本信息

  • 状态:已完成
  • 类型:需求(外部系统对接)
  • 父级大工单:#14
  • 所属 MVP / 版本:#15 / MVP
  • 前置:08 顺运宝接口(接口契约已从抓包还原)
  • 关联:#38(ParseSpec 可复用)、#20(迁移只追加)、#43(分页模式)

要解决什么

顺运宝数据模块是骨架,「同步」按钮点了提示"同步功能待接入"。
5195 个蝦皮商品已经进系统了,但货运单(也就是真实订单)一条都没有,
后面的规格匹配和采购任务无从谈起。

做什么 / 不做什么

做:

  1. 从 admin/config.yaml 读顺运宝配置
  2. 登录(界面显示验证码图片,操作员手工输)
  3. 会话缓存到 SQLite,重启免登录;过期自动提示重登
  4. 「同步」按增量日期范围拉货运单 + 明细,落 syb_orders
  5. 列表页显示同步结果

不做(各自独立工单):

  • 不做规格匹配(shopee_sku_id 留空)
  • 不做「创建采购任务」(SybCreateTask 保持 501)
  • 不引入 Redis、不引入 OCR 服务
  • 不做收件人信息的展示(见下)

怎么做

迁移 v5(三条,只追加)

[必须] 新增 v5,v1–v4 一个字节都不许改(#20 的规则,admin/AGENTS.md 已写死)。

-- ① 会话缓存。存 Cookie 就够——08 §3.1 已确认 JWT 不参与认证。
CREATE TABLE syb_session (
    username    TEXT PRIMARY KEY,
    cookies     TEXT NOT NULL,   -- JSON 数组
    expires_at  TEXT NOT NULL,   -- min(JWT exp, 24h)
    updated_at  TEXT NOT NULL
);

-- ② 上次同步到哪
CREATE TABLE syb_sync_state (
    id              INTEGER PRIMARY KEY CHECK (id = 1),  -- 只允许一行
    last_synced_at  TEXT,
    updated_at      TEXT NOT NULL
);

-- ③ 规格原文。匹配要用它,放列里才能查、才能在界面显示。
--    对应 shopee_skus.spec_raw,两边同一个概念。
ALTER TABLE syb_orders ADD COLUMN product_spec TEXT;

[必须] CheckSchema 要覆盖新表和新列。

[必须] #20 的三起点收敛测试要把 v5 覆盖进去。

增量边界 —— 本工单最容易写错的地方

[必须] created 的筛选粒度是日期(YYYY-MM-DD),
而 last_synced_at 是精确到秒的。

下次同步必须从「上次同步日期当天」重新拉,不是从第二天。

上次同步  2026-08-09 14:30
✗ 下次从 2026-08-10 拉  →  漏掉 8-09 14:30 之后创建的单,永远补不回来
✓ 下次从 2026-08-09 拉  →  重复拉当天已有的,靠 upsert 幂等

[必须] 宁可重复拉也不能漏。漏单意味着有订单永远不会被采购,
而且不会报错,没人会发现。

[必须] 首次同步用 config.yaml 的 sync_from。

[必须] 结束日期用今天,不要用未来日期。

upsert 白名单 —— 第二个最容易出事的地方

[必须] shopee_sku_id 绝不能被同步覆盖。

它是规格匹配的结果(人工确认或自动匹配产生),顺运宝那边根本没有这个值
(08 §6.2:顺运宝只给 11 位商品ID,蝦皮規格ID 是 12 位)。
同步时写进去就是写 NULL,把人工攒的匹配成果洗掉,而且不报错。

这和 #38 的 pdd_goods_url 是同一类问题,#38 那次有独立测试守着,这次也要有。

[必须] 可以覆盖的:order_no / title / product_spec / quantity /
price_twd_cent / image_url / syb_data / shopee_goods_id / updated_at。

[必须] shopee_goods_id 可以覆盖——它就是 details[].productId,
来自顺运宝,不是人工填的。

字段映射

一行 syb_orders = 一个商品明细(details[] 的一项),不是一张货运单。

列 来源 注意
syb_id details[].id 明细行主键,转成字符串
order_no 外层 code 不是 orderCode,那个是空的
title details[].productTitle
product_spec details[].productSpec 规格原文,原样存
shopee_goods_id details[].productId 11 位蝦皮商品 ID
shopee_sku_id 留空 匹配工单负责
quantity details[].productQty 表上有 CHECK (quantity > 0)
price_twd_cent details[].productPrice × 100 见下
image_url {base_url}/api/p/file?id={productThumb} productThumb 是数字 ID 不是 URL
syb_data 外层 stock + 该 detail 合并的 JSON 原样保留,审计用

[必须] 金额取 detail/listByStock 的值,不要用列表接口的。
08 §5.1 实测:同一张单 amtOrder 在列表是 61200(分),在明细是 612.0(元),
但 escrowAmount 在列表是 505、明细也是 505.0——同一响应里两个金额字段单位不同。
明细接口的单位是统一的元,自己 ×100 转分。

[必须] productPrice 是浮点,×100 转整数时先四舍五入再转,
不要直接截断(239.0 * 100 在浮点下可能是 23899.999...)。

[必须] quantity <= 0 的明细跳过并计数报告,不要硬塞——表上有 CHECK 会直接报错。

[建议] 收件人姓名/电话/地址(receiver / receiverTel / receiverAddr)
不入库。做采购决策用不到,属于个人信息(08 §5)。
syb_data 里如果带着,落库前剔掉。

登录与会话

[必须] Admin 要持有一个顺运宝 HTTP 客户端(含 Cookie Jar)。
验证码和登录必须走同一个 Cookie Jar,换客户端拿到的验证码对不上(08 §3.2)。

界面流程:

顺运宝数据页 → 点「同步」
  ├─ 会话有效 ────────────────→ 直接开始同步
  └─ 会话无效/过期 ──→ 弹出登录框
                        账号  <config.yaml 带出,只读>
                        验证码 [图片] [换一张] [____]
                        [登录并同步]

[必须] 密码从 config.yaml 读,不在界面上显示,也不回显到 HTML。

[必须] 判断会话是否有效必须区分「明确未登录」和「网络故障」(08 §3.5):
超时/5xx/格式错要抛错,不能当成未登录。否则网络抖一下就弹验证码,
而且会把本来有效的会话丢掉。

[必须] 缓存有效期取 min(JWT 剩余, 24h)。08 §3.4 已确认没有滚动续期,
不要移植示例脚本里的 _capture_refreshed_token——那是死代码
(4 份 HAR 共 18 个响应,带那个头的 0 个)。

[必须] 缓存写失败不能让已登录的会话失效。缓存就是缓存。

同步过程

① 算日期范围(上次同步日 → 今天)
② POST /am/stock/listTotal  → 总数
③ 总数 > config.max_matches → 报错,提示缩小范围,不要硬拉
④ 按 page_size 翻页 POST /am/stock/list → 收集 stock 行和 id
⑤ 按 100 个一批 POST /am/stock/detail/listByStock
⑥ 展开 details[],逐条 upsert
⑦ 全部成功后才更新 last_synced_at

[必须] 第 ⑦ 步:中途失败不更新 last_synced_at。 更新了的话下次同步
会跳过这段区间,漏掉的单永远补不回来。

[必须] 整个同步不要放在一个大事务里——几千条明细的事务会长时间持锁。
按货运单为单位提交,失败了已成功的部分保留(下次重拉会 upsert 覆盖,幂等)。

[必须] 同步是长任务,不能阻塞 HTTP 请求线程直到结束。
[建议] 最简做法:同步接口立即返回,结果写进 syb_sync_state 或状态条,
页面刷新后显示。不要为此引入后台协程池——一次只允许一个同步在跑,
用一个互斥标志挡住重复点击即可。

报告要说清楚

[必须] 同步完成后状态条显示:

同步完成:日期范围 2026-08-09 ~ 2026-08-09,货运单 12 张,商品明细 27 条
(新增 20,更新 7,跳过 0)

[必须] 有跳过或失败时列出来,不要只给个数字。

[必须] 失败时说清下一步该干嘛:会话过期→重新登录;超过上限→缩小日期范围;
网络错→稍后重试。

密码不许进日志

[必须] 任何日志、错误信息、界面提示都不得出现密码。
日志可能被贴进工单排查问题。

[建议] 配置结构体的 String() 方法把密码打成 ****,从源头防住。

预计修改文件

文件 改什么
admin/config/config.go 读 config.yaml;缺文件时给出「复制 example」的提示
admin/config/config_test.go 新建
admin/repository/db.go 迁移 v5;CheckSchema 覆盖新表新列
admin/repository/migrate_test.go 三起点收敛测试覆盖 v5
admin/repository/syb.go 新建:会话读写、同步状态、syb_orders 白名单 upsert、列表查询
admin/syb/client.go 新建:顺运宝 HTTP 客户端(验证码、登录、列表、明细)
admin/syb/client_test.go 新建:用 httptest 起假服务端,不打真实站点
admin/service/syb.go 新建:同步编排、字段映射、报告组装
admin/service/syb_test.go 新建
admin/handler/web/others.go SybList / SybSync 真实实现;新增登录与验证码路由
admin/handler/web/web.go 新路由
admin/templates/syb/list.html 表格、登录弹窗
admin/go.mod github.com/goccy/go-yaml 从 indirect 转 direct
docs/admin/03-data-model.md v5、syb_orders 字段来源
docs/admin/05-ui-specification.md §6 顺运宝页
docs/admin/00-getting-started.md 配置文件怎么建

[必须] 测试绝不能打真实的 shunyunbaoerp.com。用 httptest 起假服务端,
响应体照 08 文档里的形状造。打真站会污染对方数据、可能触发风控。

验收标准

配置

  • 缺 config.yaml 时提示「复制 config.example.yaml 并填账号密码」,不是「读取失败」
  • 密码是纯数字且加了引号时能正常读出字符串
  • 日志、错误信息、页面里不出现密码(有测试)

迁移

  • v5 是新增的,v1–v4 未改
  • 三起点收敛测试通过且覆盖 v5
  • CheckSchema 覆盖 syb_session / syb_sync_state / syb_orders.product_spec

会话

  • 登录成功后 Cookie 存进 syb_session
  • 重启 Admin 后不用重新登录
  • 会话过期后点同步 → 弹登录框,不是报错
  • 网络超时/5xx 时不判定为未登录(有测试)
  • 缓存写失败不影响已登录会话

同步

  • 首次用 sync_from,之后用 last_synced_at
  • 增量从「上次同步日期当天」重拉,不是第二天(有测试)
  • 中途失败不更新 last_synced_at(有测试)
  • 总数超过 max_matches → 报错并提示缩小范围,不硬拉
  • 一张货运单的多个商品各落一行
  • price_twd_cent = productPrice × 100,四舍五入
  • image_url 拼成 {base_url}/api/p/file?id={productThumb}
  • quantity <= 0 的明细被跳过并计入报告
  • 收件人姓名/电话/地址不入库

upsert

  • 已有的 shopee_sku_id 同步后仍在(独立测试,这条最要紧)
  • shopee_goods_id 会被同步更新
  • 重复同步同一区间,行数不翻倍

其他

  • 测试不打真实站点(用 httptest)
  • SybCreateTask / SybMatch 仍是 501
  • 五个页面均 200
  • GOTOOLCHAIN=go1.23.0 下 go vet / gofmt -l . / go test ./... 全过

怎么验证

cd D:\chengma\cmautobuy\admin
$env:GOTOOLCHAIN="go1.23.0"
go vet ./...; gofmt -l .; go test ./... -count=1
Remove-Item Env:GOTOOLCHAIN

真实同步(需要真实账号,admin/config.yaml 已配好):

  1. go run . → 打开 /syb → 点「同步���→ 应弹登录框并显示验证码图片
  2. 输验证码 → 登录成功 → 开始同步 → 状态条显示日期范围和条数
  3. 重启 Admin,再点同步 → 不应再要验证码(会话缓存生效)
  4. 手工 UPDATE syb_orders SET shopee_sku_id='TEST-SKU' WHERE syb_id='<任选>'
  5. 再同步一次 → SELECT 出来确认 shopee_sku_id 还是 TEST-SKU

[必须] 第 3 步和第 5 步的实际输出要贴出来。第 5 步是这张工单唯一
「错了要几周后才发现」的点——匹配成果被洗掉不会报错,等到建采购任务才发现。

风险和回退

风险 应对
同步覆盖人工匹配的 shopee_sku_id upsert 白名单 + 独立测试 + 验证步骤单列一条
增量边界漏单,且不报错 从上次同步日当天重拉;已列为验收项
中途失败却推进了 last_synced_at 全部成功才更新;已列为验收项
金额单位取错,差 100 倍 一律取明细接口的值,08 §5.1 已写明
测试打真实站点 强制 httptest;已列为验收项
密码进日志 已列为验收项

回退:git revert。已迁到 v5 的库回退后会因「版本高于程序支持」拒绝启动,
这是 Migrate 已有的正确行为。同步进来的数据可以清表重来。

## 基本信息 - 状态:已完成 - 类型:需求(外部系统对接) - 父级大工单:#14 - 所属 MVP / 版本:#15 / MVP - 前置:[08 顺运宝接口](../docs/admin/08-顺运宝接口.md)(接口契约已从抓包还原) - 关联:#38(`ParseSpec` 可复用)、#20(迁移只追加)、#43(分页模式) ## 要解决什么 顺运宝数据模块是骨架,「同步」按钮点了提示"同步功能待接入"。 5195 个蝦皮商品已经进系统了,但**货运单(也就是真实订单)一条都没有**, 后面的规格匹配和采购任务无从谈起。 ## 做什么 / 不做什么 做: 1. 从 `admin/config.yaml` 读顺运宝配置 2. 登录(界面显示验证码图片,操作员手工输) 3. 会话缓存到 **SQLite**,重启免登录;过期自动提示重登 4. 「同步」按增量日期范围拉货运单 + 明细,落 `syb_orders` 5. 列表页显示同步结果 不做(各自独立工单): - **不做规格匹配**(`shopee_sku_id` 留空) - 不做「创建采购任务」(`SybCreateTask` 保持 501) - 不引入 Redis、不引入 OCR 服务 - 不做收件人信息的展示(见下) ## 怎么做 ### 迁移 v5(三条,只追加) `[必须]` **新增 v5,v1–v4 一个字节都不许改**(#20 的规则,`admin/AGENTS.md` 已写死)。 ```sql -- ① 会话缓存。存 Cookie 就够——08 §3.1 已确认 JWT 不参与认证。 CREATE TABLE syb_session ( username TEXT PRIMARY KEY, cookies TEXT NOT NULL, -- JSON 数组 expires_at TEXT NOT NULL, -- min(JWT exp, 24h) updated_at TEXT NOT NULL ); -- ② 上次同步到哪 CREATE TABLE syb_sync_state ( id INTEGER PRIMARY KEY CHECK (id = 1), -- 只允许一行 last_synced_at TEXT, updated_at TEXT NOT NULL ); -- ③ 规格原文。匹配要用它,放列里才能查、才能在界面显示。 -- 对应 shopee_skus.spec_raw,两边同一个概念。 ALTER TABLE syb_orders ADD COLUMN product_spec TEXT; ``` `[必须]` `CheckSchema` 要覆盖新表和新列。 `[必须]` #20 的三起点收敛测试要把 v5 覆盖进去。 ### 增量边界 —— 本工单最容易写错的地方 `[必须]` `created` 的筛选粒度是**日期**(`YYYY-MM-DD`), 而 `last_synced_at` 是精确到秒的。 **下次同步必须从「上次同步日期当天」重新拉,不是从第二天。** ```text 上次同步 2026-08-09 14:30 ✗ 下次从 2026-08-10 拉 → 漏掉 8-09 14:30 之后创建的单,永远补不回来 ✓ 下次从 2026-08-09 拉 → 重复拉当天已有的,靠 upsert 幂等 ``` `[必须]` 宁可重复拉也不能漏。漏单意味着有订单永远不会被采购, 而且**不会报错**,没人会发现。 `[必须]` 首次同步用 `config.yaml` 的 `sync_from`。 `[必须]` 结束日期用**今天**,不要用未来日期。 ### upsert 白名单 —— 第二个最容易出事的地方 `[必须]` `shopee_sku_id` **绝不能被同步覆盖**。 它是规格匹配的结果(人工确认或自动匹配产生),顺运宝那边根本没有这个值 (08 §6.2:顺运宝只给 11 位商品ID,蝦皮規格ID 是 12 位)。 同步时写进去就是写 NULL,**把人工攒的匹配成果洗掉,而且不报错**。 这和 #38 的 `pdd_goods_url` 是同一类问题,#38 那次有独立测试守着,这次也要有。 `[必须]` 可以覆盖的:`order_no` / `title` / `product_spec` / `quantity` / `price_twd_cent` / `image_url` / `syb_data` / `shopee_goods_id` / `updated_at`。 `[必须]` `shopee_goods_id` **可以**覆盖——它就是 `details[].productId`, 来自顺运宝,不是人工填的。 ### 字段映射 一行 `syb_orders` = 一个商品明细(`details[]` 的一项),不是一张货运单。 | 列 | 来源 | 注意 | |---|---|---| | `syb_id` | `details[].id` | 明细行主键,转成字符串 | | `order_no` | 外层 `code` | 不是 `orderCode`,那个是空的 | | `title` | `details[].productTitle` | | | `product_spec` | `details[].productSpec` | 规格原文,原样存 | | `shopee_goods_id` | `details[].productId` | 11 位蝦皮商品 ID | | `shopee_sku_id` | **留空** | 匹配工单负责 | | `quantity` | `details[].productQty` | 表上有 `CHECK (quantity > 0)` | | `price_twd_cent` | `details[].productPrice` **× 100** | 见下 | | `image_url` | `{base_url}/api/p/file?id={productThumb}` | `productThumb` 是数字 ID 不是 URL | | `syb_data` | 外层 stock + 该 detail 合并的 JSON | 原样保留,审计用 | `[必须]` **金额取 `detail/listByStock` 的值,不要用列表接口的。** 08 §5.1 实测:同一张单 `amtOrder` 在列表是 `61200`(分),在明细是 `612.0`(元), 但 `escrowAmount` 在列表是 `505`、明细也是 `505.0`——**同一响应里两个金额字段单位不同**。 明细接口的单位是统一的元,自己 ×100 转分。 `[必须]` `productPrice` 是浮点,×100 转整数时**先四舍五入再转**, 不要直接截断(`239.0 * 100` 在浮点下可能是 `23899.999...`)。 `[必须]` `quantity <= 0` 的明细**跳过并计数报告**,不要硬塞——表上有 CHECK 会直接报错。 `[建议]` 收件人姓名/电话/地址(`receiver` / `receiverTel` / `receiverAddr`) **不入库**。做采购决策用不到,属于个人信息(08 §5)。 `syb_data` 里如果带着,落库前剔掉。 ### 登录与会话 `[必须]` Admin 要持有**一个**顺运宝 HTTP 客户端(含 Cookie Jar)。 验证码和登录必须走同一个 Cookie Jar,换客户端拿到的验证码对不上(08 §3.2)。 界面流程: ```text 顺运宝数据页 → 点「同步」 ├─ 会话有效 ────────────────→ 直接开始同步 └─ 会话无效/过期 ──→ 弹出登录框 账号 <config.yaml 带出,只读> 验证码 [图片] [换一张] [____] [登录并同步] ``` `[必须]` 密码从 `config.yaml` 读,**不在界面上显示,也不回显到 HTML**。 `[必须]` **判断会话是否有效必须区分「明确未登录」和「网络故障」**(08 §3.5): 超时/5xx/格式错要抛错,**不能当成未登录**。否则网络抖一下就弹验证码, 而且会把本来有效的会话丢掉。 `[必须]` 缓存有效期取 `min(JWT 剩余, 24h)`。08 §3.4 已确认**没有滚动续期**, 不要移植示例脚本里的 `_capture_refreshed_token`——那是死代码 (4 份 HAR 共 18 个响应,带那个头的 0 个)。 `[必须]` 缓存写失败**不能让已登录的会话失效**。缓存就是缓存。 ### 同步过程 ```text ① 算日期范围(上次同步日 → 今天) ② POST /am/stock/listTotal → 总数 ③ 总数 > config.max_matches → 报错,提示缩小范围,不要硬拉 ④ 按 page_size 翻页 POST /am/stock/list → 收集 stock 行和 id ⑤ 按 100 个一批 POST /am/stock/detail/listByStock ⑥ 展开 details[],逐条 upsert ⑦ 全部成功后才更新 last_synced_at ``` `[必须]` **第 ⑦ 步:中途失败不更新 `last_synced_at`。** 更新了的话下次同步 会跳过这段区间,漏掉的单永远补不回来。 `[必须]` 整个同步**不要放在一个大事务里**——几千条明细的事务会长时间持锁。 按货运单为单位提交,失败了已成功的部分保留(下次重拉会 upsert 覆盖,幂等)。 `[必须]` 同步是长任务,**不能阻塞 HTTP 请求线程直到结束**。 `[建议]` 最简做法:同步接口立即返回,结果写进 `syb_sync_state` 或状态条, 页面刷新后显示。不要为此引入后台协程池——一次只允许一个同步在跑, 用一个互斥标志挡住重复点击即可。 ### 报告要说清楚 `[必须]` 同步完成后状态条显示: ```text 同步完成:日期范围 2026-08-09 ~ 2026-08-09,货运单 12 张,商品明细 27 条 (新增 20,更新 7,跳过 0) ``` `[必须]` 有跳过或失败时**列出来**,不要只给个数字。 `[必须]` 失败时说清**下一步该干嘛**:会话过期→重新登录;超过上限→缩小日期范围; 网络错→稍后重试。 ### 密码不许进日志 `[必须]` 任何日志、错误信息、界面提示都不得出现密码。 日志可能被贴进工单排查问题。 `[建议]` 配置结构体的 `String()` 方法把密码打成 `****`,从源头防住。 ## 预计修改文件 | 文件 | 改什么 | |---|---| | `admin/config/config.go` | 读 `config.yaml`;缺文件时给出「复制 example」的提示 | | `admin/config/config_test.go` | 新建 | | `admin/repository/db.go` | 迁移 v5;`CheckSchema` 覆盖新表新列 | | `admin/repository/migrate_test.go` | 三起点收敛测试覆盖 v5 | | `admin/repository/syb.go` | 新建:会话读写、同步状态、`syb_orders` 白名单 upsert、列表查询 | | `admin/syb/client.go` | 新建:顺运宝 HTTP 客户端(验证码、登录、列表、明细) | | `admin/syb/client_test.go` | 新建:**用 `httptest` 起假服务端**,不打真实站点 | | `admin/service/syb.go` | 新建:同步编排、字段映射、报告组装 | | `admin/service/syb_test.go` | 新建 | | `admin/handler/web/others.go` | `SybList` / `SybSync` 真实实现;新增登录与验证码路由 | | `admin/handler/web/web.go` | 新路由 | | `admin/templates/syb/list.html` | 表格、登录弹窗 | | `admin/go.mod` | `github.com/goccy/go-yaml` 从 indirect 转 direct | | `docs/admin/03-data-model.md` | v5、`syb_orders` 字段来源 | | `docs/admin/05-ui-specification.md` | §6 顺运宝页 | | `docs/admin/00-getting-started.md` | 配置文件怎么建 | `[必须]` **测试绝不能打真实的 `shunyunbaoerp.com`**。用 `httptest` 起假服务端, 响应体照 08 文档里的形状造。打真站会污染对方数据、可能触发风控。 ## 验收标准 **配置** - [x] 缺 `config.yaml` 时提示「复制 config.example.yaml 并填账号密码」,不是「读取失败」 - [x] 密码是纯数字且加了引号时能正常读出字符串 - [x] 日志、错误信息、页面里**不出现密码**(有测试) **迁移** - [x] v5 是**新增**的,v1–v4 未改 - [x] 三起点收敛测试通过且覆盖 v5 - [x] `CheckSchema` 覆盖 `syb_session` / `syb_sync_state` / `syb_orders.product_spec` **会话** - [x] 登录成功后 Cookie 存进 `syb_session` - [x] 重启 Admin 后不用重新登录 - [x] 会话过期后点同步 → 弹登录框,不是报错 - [x] **网络超时/5xx 时不判定为未登录**(有测试) - [x] 缓存写失败不影响已登录会话 **同步** - [x] 首次用 `sync_from`,之后用 `last_synced_at` - [x] **增量从「上次同步日期当天」重拉,不是第二天**(有测试) - [x] **中途失败不更新 `last_synced_at`**(有测试) - [x] 总数超过 `max_matches` → 报错并提示缩小范围,不硬拉 - [x] 一张货运单的多个商品**各落一行** - [x] `price_twd_cent` = `productPrice × 100`,四舍五入 - [x] `image_url` 拼成 `{base_url}/api/p/file?id={productThumb}` - [x] `quantity <= 0` 的明细被跳过并计入报告 - [x] 收件人姓名/电话/地址不入库 **upsert** - [x] **已有的 `shopee_sku_id` 同步后仍在**(独立测试,这条最要紧) - [x] `shopee_goods_id` 会被同步更新 - [x] 重复同步同一区间,行数不翻倍 **其他** - [x] 测试不打真实站点(用 `httptest`) - [x] `SybCreateTask` / `SybMatch` 仍是 501 - [x] 五个页面均 200 - [x] `GOTOOLCHAIN=go1.23.0` 下 `go vet` / `gofmt -l .` / `go test ./...` 全过 ## 怎么验证 ```powershell cd D:\chengma\cmautobuy\admin $env:GOTOOLCHAIN="go1.23.0" go vet ./...; gofmt -l .; go test ./... -count=1 Remove-Item Env:GOTOOLCHAIN ``` **真实同步**(需要真实账号,`admin/config.yaml` 已配好): 1. `go run .` → 打开 `/syb` → 点「同步���→ 应弹登录框并显示验证码图片 2. 输验证码 → 登录成功 → 开始同步 → 状态条显示日期范围和条数 3. **重启 Admin,再点同步** → 不应再要验证码(会话缓存生效) 4. 手工 `UPDATE syb_orders SET shopee_sku_id='TEST-SKU' WHERE syb_id='<任选>'` 5. **再同步一次** → `SELECT` 出来确认 `shopee_sku_id` **还是 `TEST-SKU`** `[必须]` 第 3 步和第 5 步的实际输出要贴出来。第 5 步是这张工单唯一 「错了要几周后才发现」的点——匹配成果被洗掉不会报错,等到建采购任务才发现。 ## 风险和回退 | 风险 | 应对 | |---|---| | **同步覆盖人工匹配的 `shopee_sku_id`** | upsert 白名单 + 独立测试 + 验证步骤单列一条 | | **增量边界漏单,且不报错** | 从上次同步日当天重拉;已列为验收项 | | 中途失败却推进了 `last_synced_at` | 全部成功才更新;已列为验收项 | | 金额单位取错,差 100 倍 | 一律取明细接口的值,08 §5.1 已写明 | | 测试打真实站点 | 强制 `httptest`;已列为验收项 | | 密码进日志 | 已列为验收项 | 回退:`git revert`。已迁到 v5 的库回退后会因「版本高于程序支持」拒绝启动, 这是 `Migrate` 已有的正确行为。同步进来的数据可以清表重来。
Author
Owner

用户已于 2026-08-09 明确通过验收,本工单验收完成。

  • 实现提交:5e426ca
  • 归档提交:d062d38;归档格式修正 72821ea
  • 归档:docs/task/46-顺运宝货运单同步.md
  • 当前回归:Go 1.23.0 go build ./...、go test ./... -count=1、go vet ./... 全部通过。
  • 未重跑:真实顺运宝账号;该限制已写入归档,用户已明确接受。

现关闭工单,并同步 #15、#14 的任务清单。

用户已于 2026-08-09 明确通过验收,本工单验收完成。 - 实现提交:`5e426ca` - 归档提交:`d062d38`;归档格式修正 `72821ea` - 归档:`docs/task/46-顺运宝货运单同步.md` - 当前回归:Go 1.23.0 `go build ./...`、`go test ./... -count=1`、`go vet ./...` 全部通过。 - 未重跑:真实顺运宝账号;该限制已写入归档,用户已明确接受。 现关闭工单,并同步 #15、#14 的任务清单。
ila closed this issue 2026-08-09 21:23:26 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmautobuy#46