worker 领取条件(order_writeback_worker.go:81)增加:status='failed' AND error_code='SYB_SESSION_UNAVAILABLE' AND lease_expires_at <= now AND attempt_count < 上限。其他失败码(SYB_READ_FAILED、SYB_WRITE_REJECTED、TASK_UNAVAILABLE 等)行为不变。
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
原始需求
用户 2026-09-21:「260918B9AYC6Y 同个商品不同颜色,采购成功,但回填单号不成功」。经分析后确认:回填 PDD 订单号到 SYB 时因 SYB 会话不可用而失败,且失败后无自动恢复。用户选定「方案 ②」:会话检测 + 会话类失败自动延迟重试。
事实(2026-09-21 生产库与代码核验)
订单 260918B9AYC6YT(SYB 明细 16860
16865,6 个颜色)对应采购任务 414419:order_createdpurchase_order_writeback)failed/SYB_SESSION_UNAVAILABLE全表分布:
succeeded 66、failed 10,失败全部是同一错误码,且集中在 2026-09-20 11:15~11:23 这 8 分钟内。回写逻辑本身没有问题。订单号已取得,只是回写 SYB 这一步失败。
对比档口入库码 / 商品同步(均走
sybimport.Connect:读缓存 →CheckSession主动探测 → 明确失效才删除并 OCR 重登 → 保存),回填走的restoreOrderWritebackClient只做Load+ImportCookiesJSON就返回,不检查会话是否仍有效,读不到即直接失败。每小时同步任务(
GoAutoSYBHourlySync,每小时 :05)全天真实运行(每次 2~8 分钟),近 20 余次运行没有任何一条登录失败,其错误均为/am/stock/list网络类。syb_session仅 1 行,updated_at=2026-09-21 13:05:04、expires_at=2026-09-22 13:05:04,与该任务触发点吻合,说明会话由它刷新。回填失败的真实原因没有被记录:
restoreOrderWritebackClient可能返回四类错误(无会话记录 / 已过期 / cookie 导入失败 / 地址配置错误),全部折叠成同一句固定文案。因此无法据现有数据断言 9/20 那次就是会话过期——库里只保留最新一次会话,当时状态无法还原。重试安全性(幂等):
write_started在order_writeback_worker.go第 187 行才置为 true,而会话获取在第 115 行,会话类失败一定发生在任何 SYB 写入开始之前,自动重试不会造成重复写入。目标
非目标
AGENTS.md中该例外仅覆盖既有路径,不新增自动触发点);sybimport.Connect,不改档口入库码、商品同步、店铺发现;failed存量(另行人工重试)。方案
restoreOrderWritebackClient在导入 cookie 后调用client.CheckSession(ctx, session.UserID, cfg.Username)(sybclient已被本包 import,无新增依赖、无循环)。ErrSessionInvalid表示明确失效;其他错误表示网络/格式问题,不代表未登录(沿用Connect的既有区分,不因网络抖动误判)。finish()的error_message写入真实原因类别(无会话 / 已失效 / 网络类 / 配置缺失),不再只写固定文案。error_code保持SYB_SESSION_UNAVAILABLE以兼容现有前端与筛选。SYB_SESSION_UNAVAILABLE生效;回到可认领状态并在下一次同步刷新会话之后再被 worker 认领。重试次数与间隔有上限,超限后转为需人工处理的failed,不无限重试。已知局限(须如实记录)
Connect的逻辑是会话仍有效则复用、不写库,因此会话是在真正失效之后的下一个 :05 才被刷新,等待时长为「失效时刻到下一个整点」,最长约 1 小时。Connect自行重登)已评估并明确不在本单范围,理由是会新增「回填自动触发 OCR」这一安全边界变化,需另行评估授权。验收
CheckSession返回ErrSessionInvalid→ 回填不写 SYB、error_message含「已失效」,并进入自动重试;CheckSession返回网络类错误 → 不判定为已失效,error_message区分为网络类;write_started只置一次(无重复写入);failed,不再重试;order_writeback相关测试不改动通过。风险与回退
AGENTS.md将权限、安全、并发列为高风险,实施前需人工确认。restoreOrderWritebackClient与finish()原样,并移除重试逻辑。文档影响
回填失败原因与自动重试行为属长期业务规则变化,需更新
docs/03-business-rules-and-glossary.md;若新增迁移则同步docs/13-deployment-and-operations.md。方案修订(2026-09-21,经用户确认按 1–3 点修订)
复核代码基线:origin/main
4261a54。修订 1:主修复与辅助修复重新定位
SYB_SESSION_UNAVAILABLE,失败点在Factory(Load/New/ImportCookiesJSON),不是远端 cookie 失效(远端失效时Load成功,会在read()失败并记为SYB_READ_FAILED)。CheckSession是辅助,作用是把远端失效提前识别为会话类失败并纳入重试,而不是落成SYB_READ_FAILED。CheckSession返回非ErrSessionInvalid错误(网络、5xx、格式)时也按会话类可重试处理,error_message 记录类别。SessionStore.Delete,不写 syb_session,不登录、不触发 OCR(沿用非目标)。UserID == 0的会话(如手工登录路径写入)是否存在;若存在,CheckSession会误判为失效,须在该情况下跳过检查或按不可用处理并记录,不得误删。修订 2:不影响手工"重新回填"按钮
order_writeback.go:111:CanSubmit要求LeaseExpiresAt为空或已过期。LeaseExpiresAt作退避到期时间,退避期内按钮会被隐藏。采用方案 (b):CanSubmit对会话类失败(status=failed且error_code=SYB_SESSION_UNAVAILABLE)不受LeaseExpiresAt限制。order_writeback.go:200已清空 lease)照常进入 pending,与自动重试不冲突(worker 以行锁 + lease_owner 领取)。next_attempt_at。修订 3:重试与状态规则
order_writeback_worker.go:81)增加:status='failed' AND error_code='SYB_SESSION_UNAVAILABLE' AND lease_expires_at <= now AND attempt_count < 上限。其他失败码(SYB_READ_FAILED、SYB_WRITE_REJECTED、TASK_UNAVAILABLE等)行为不变。finish写lease_expires_at = now + 退避(有界退避,上限与间隔在实施时定为常量并写入工单回写),达到上限后lease_expires_at = nil,保持failed等人工处理。attempt_count置 0,避免历史手工重试耗尽自动重试额度。验收补充
CanSubmit=true;手工提交重置计数;其他失败码不被自动重试。sybimport.Connect零改动(diff 仅限 purchase 包与文档)。风险
并发/状态机变化,属高风险,实施前仍需人工确认。
实施回写(2026-09-21)
提交:
01510a8、08b7095(分支fix/330-syb-writeback-session),已快进合并到 main(4261a54..08b7095)。未部署。实现(按评论 8419 修订方案)
restoreOrderWritebackClient:Load → ImportCookies →CheckSession;UserID<=0显式判不可用(SessionStore.Save已拒绝 UserID<=0,正常路径不可达,属防御)。回填路径不删除/不写 syb_session,不登录、不 OCR。SYB_SESSION_UNAVAILABLE)有界自动重试:最多 6 次,退避 5/10/15/30/30 分钟(合计约 90 分钟,大于一个整点同步周期),退避到期时间复用lease_expires_at;达上限清空并保持failed等人工。其他失败码行为不变。CanSubmit:会话类失败退避期内仍可手工重新回填;手工重新提交将attempt_count置 0 并清空租约。AttemptCount比数据库少 1。server/app/goauto/purchase/3 个文件,sybimport/sybinnercode/sybclient 零改动。验证(审核人独立复跑)
go vet ./app/goauto/purchase/通过;go test -count=1 ./app/goauto/purchase/...通过。/am/user/get跑真实restoreOrderWritebackClient(有效、串号失效、500、超时)及 worker 端到端,均断言 syb_session 未被删除;退避期 CanSubmit;手工提交重置计数;其他失败码不自动重试。sybimport有 12 个既有失败,实施方报告与干净 main 一致,审核人未独立复核。未验证 / 遗留
failed(lease 为空)不会被自动领取,仍需人工重试(本单非目标)。753d035(2026-09-21 02:21Z,"docs: document purchaser device ownership (#328)")被删除了除 Business-Rules-and-Glossary 外的全部页面(Home、Architecture-and-Code-Map、API 契约等),harness.py sync --check报 Home 不存在。需先恢复 Wiki 后再补 #330 的业务规则(#305 章节)与架构页(order_writeback_worker 条目)更新。状态:待 Wiki 恢复并补文档后进入待验收。
文档回写(2026-09-21)
753d035(#328 文档提交)误删除了 Business-Rules-and-Glossary 以外的全部页面。经用户授权,以753d035~1的树为基础、保留753d035对 Business-Rules-and-Glossary 的 #328 改动,生成恢复提交03ea269(64 个文件)。6cb34c9;Architecture-and-Code-Map 新增“会话类失败自动重试(#330)”,revision8e2cfa7。harness.py sync与sync --check通过;镜像提交5e0a9d1已推送 main(17 个镜像文件,除 02/03 的 #330 段落外只更新了 revision 头)。状态:待验收。未部署、未做生产验证;遗留问题见评论 8422(第 6 次失败的文案、10 条历史失败记录需人工重试)。
ila referenced this issue2026-09-21 17:41:45 +08:00