Commit Graph
69 Commits
Author SHA1 Message Date
QiuSWandClaude Opus 5.5 5ac8e4c8fd feat(yeeke): show missing-marked and recovered counts per sync run (#338)
Persist how many return items each sync run flipped to "missing" and how
many came back to "ok" (yeeke_sync_run.missing_marked_count /
recovered_count, migration 1789801000000), return them from the sync-runs
API and add 「标记不可用」「恢复可用」 columns to the sync-runs page. When the
20% safety valve skips marking the count stays 0 and the reason remains in
error_message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-24 16:14:30 +08:00
QiuSWandClaude Opus 5.5 ff6e87649c feat(returnmatch): mark yeeke returns unavailable when a complete sync drops them (#338)
A COMPLETE yeeke sync run (natural page-walk end, not stopped by a
duplicate-fingerprint break or MaxPages exhaustion) that also wrote zero
record failures now flips any yeeke_return_package/yeeke_return_item still
"ok" from before the run to sync_status="missing" with missing_since
stamped, in one transaction. Rows are never deleted. A 20% safety valve
skips marking (and records why in error_message) when the candidate count
would be too large; a reappearing record recovers to "ok" with
missing_since cleared.

returnmatch.availableReturnPool now excludes missing items/packages from
matching, without auto-cancelling any existing active match; List()/Detail()
surface syncStatus/missingSince so the SYB products match column, its
compare dialog, and the yeeke returns list can warn or label rows as
unavailable.

Adds migration 1789800900000_return_missing (AutoMigrate alone does not
reach existing databases).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-24 15:57:51 +08:00
QiuSWandClaude Opus 5.5 1e944b9d2e feat(returnmatch): record every batch-match submission and its outcomes (#338)
Each click of 「匹配退货」 now writes a return_match_batch row: operator,
time, submitted/matched/skipped counts and the per-SYB-product result
(in submission order), plus the error if the batch aborted. Recording
happens after the per-row transactions commit, so a logging failure is
logged server-side instead of failing an already-applied match.
GET /return-matches/batches and /return-matches/batches/:batchId expose
the records. Migration version 1789800800000 creates the table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-24 14:22:06 +08:00
QiuSWandClaude Opus 5.5 20a8144be0 fix(db): add migration version that creates return_match tables (#338)
Registering the models in MigratedModels alone does nothing on databases
whose earlier versions are already recorded in sys_migration; the migrate
command's post-check rejected the run with "迁移后仍缺少表". Add the
1789800700000 version so existing databases create return_match and
return_match_log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-24 14:03:06 +08:00
QiuSWandClaude Opus 5 ab292d6a17 feat(yeeke): add admin read-only return-package/sync-run modules (#337)
Add read-only server endpoints for yeeke return packages (paginated list
with order/tracking/shop/item/variation/claim-status/create-time filters,
plus package detail with items) and extend the existing sync-runs endpoint
with trigger/status/started-at filters, backward compatible. Split the
admin menu into two modules per the ticket's confirmed scope adjustment:
yeeke_returns (packages) and the new yeeke_sync_runs, both under the
采集采购 menu group, wired via a new sys_menu migration mirroring #237's
precedent. Add the corresponding Vue pages, API wrappers and Go tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-23 14:23:51 +08:00
QiuSWandClaude Opus 5 3e82ad6570 feat(yeeke): complete admin surface, scheduling and tests for return sync #336
Builds on cf70021, which added the read-only yeeke client/session/sync core
but left it unreachable and unconfigurable. This commit:

- Wires config.ExtConfig.Yeeke (settings.yml + config.yaml + GOAUTO_YEEKE_*
  env vars), mirroring the existing SYB credential pattern exactly, with a
  dedicated OcrURL and shared OCR client from sybclient.
- Adds yeeke.StartSync as the single entry point for both a manual admin
  trigger and the scheduled job, sharing one in-memory gate plus the existing
  DB-level unique active_slot lease so they can never run concurrently.
- Fixes sync.go bugs found in review: Service.Sync always returned a nil
  error even when the run failed (start/resume semantics were untestable),
  item upserts on ctx-less s.db calls, and no error_message/last_success_at
  was ever recorded on the run row.
- Adds status_unrecognized to yeeke_return_package: an unknown claim status
  is preserved verbatim and flagged rather than silently bucketed.
- Adds the admin read-only surface (GET .../sync-runs, GET
  .../sync-runs/:runId, POST .../sync) under /api/admin/v1/yeeke-returns,
  visible to admin and purchaser per the #336 review comment, registered as
  a GoAuto access module/menu group and purchaser API.
- Registers GoAutoYeekeReturnSync in the existing job/lease framework
  (app/jobs), seeded disabled (Status 2) by a new version-local migration,
  following 1786701600000_syb_hourly_sync_job.go's pattern exactly.
- Expands tests: session reuse/bounded re-login/timeout-preserves-cache in
  yeekeclient; paging robustness (total changing mid-run, duplicate page,
  empty page, timeout, simulated restart/resume), idempotent upserts,
  unrecognized-status flagging, active_slot lease contention, StartSync gate
  contention, and a credential/captcha redaction check in yeeke; settings.yml
  binding and env var precedence in config.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-23 11:03:46 +08:00
QiuSWandCodex GPT-6-astra b015719947 feat(yeeke): add read-only return sync #336
Co-Authored-By: Codex GPT-6-astra <noreply@openai.com>

Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-23 11:03:44 +08:00
QiuSW beec630187 fix(#329): record device ownership migration 2026-09-21 10:38:54 +08:00
QiuSW bb1a410e8a feat(#329): add device purchaser ownership 2026-09-21 09:59:00 +08:00
QiuSW e89de1a085 feat: queue and reconcile SYB purchase order numbers (#305) 2026-09-18 10:13:20 +08:00
QiuSW 8c01329f97 feat: capture optional PDD order amount in manual backfill (#306) 2026-09-18 09:22:12 +08:00
QiuSWandClaude Sonnet 5 f634996dad fix: order_result_unknown 补充"是否见过支付/待付款页"诊断标记 (#302)
order_result_unknown 目前是全有或全无:parseOrderEvidence 要求订单号、下单
时间、待付款/支付文案同时命中才算 order_created,任何一项缺失就落进同一个
order_result_unknown,无法区分"确实到过支付页只是没读全证据"和"根本没到
那一步"——前者大概率已在 PDD 建了真实订单。

Agent:readOrderResult 采样循环中,只要命中过支付页 Activity 或
unpaidContextVisible(待付款/待支付/去支付文案),记 paymentPageObserved,
与订单号是否解析成功无关,随 order_result_unknown 一起上报。

服务端:PurchaseTask 新增 PaymentPageObservedAt,仅在请求带
paymentPageObserved=true 时写入服务端当前时间;不回填既有 107 笔历史记录,
无法从历史数据反推当时是否见过支付页。

不改判定结果本身,order_created 的四项条件、批量重试逻辑均未动。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-17 14:25:47 +08:00
QiuSWandClaude Opus 5 8f337d5248 fix(server): 取消状态的 CHECK 约束改由 version-local 迁移应用 (#297)
约束修复原本放在 goautomigrations.Migrate 里,而 Migrate 只被 version-local 下的
迁移文件调用——那些文件在既有库上都已应用、会被跳过,于是修复永远不执行。

线上 2026-09-16 发布后实测:服务起来了、新代码在跑,ck_collection_task_status
却仍只认旧五个状态,一点取消就会被数据库拒绝。verify.go 里早有同样的警告:
「只把模型加进 migrations.Migrate 对已有数据库无效」。

单测能过是因为测试库是新建的,GORM 按模型标签直接建出含 cancelled 的约束;
既有库拿不到。

新增 1789700000000_collection_task_cancelled.go,并把约束函数导出供其调用。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-16 16:48:41 +08:00
QiuSWandClaude Opus 5 5b3c8df3bc feat(server): 同步虾皮完整颜色尺码并一次性匹配 (#290)
图搜采集成功并自动关联后,拉取该虾皮商品的完整颜色尺码清单合并进档案,
再触发一次匹配。此后同一虾皮商品的其它颜色 SYB 订单无需再采集、再点匹配。

- 新增 shopeespec 叶子包,凭据只从 GOAUTO_ERPGO_BASE_URL/GOAUTO_ERPGO_APIKEY
  读取;错误文本不携带 URL,避免 apikey 流进日志。
- 写入档案前经 sybspec.StripAnnotations 剥离 【...】,与 SYB 明细同源,
  否则 confirmedMappings 查不到、映射全部落空。
- 标记记录同步时对着哪个 PDD 商品(spec_sync_pdd_product_id),不是布尔值,
  重新关联时自动失效。
- 已完整同步的商品再次图搜时返回 IMAGE_SEARCH_SPEC_SYNCED。
- 迁移只加列不回填:既有档案仍是从 SYB 明细增量累积的,不能假装已同步。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-16 10:33:53 +08:00
QiuSWandClaude Opus 5 174d92a1c9 fix(server): SYB 结构过滤合并为一条,要求同时包含 - 和 # (#286)
结构过滤原为两条 char 规则(- 和 #),RuleSet.Match 逐条 Contains、
命中即返回,是或关系。线上最近一次完整同步(syb_sync_run 541)里
- 命中 384、# 命中 148,合计 532 条被过滤;因 - 先判且命中即返回,
那 148 条是含 # 但不含 - 的。

改为一条,keyword 存必需字符集合 -#,匹配要求集合中每个字符都出现:

- 同时含 - 和 # → 过滤
- 只含其一 → 放行
- 都不含 → 放行

已向用户说明每次同步至少多放行 148 条,用户确认后实施。

`[必须]` migrations/migrate.go 的种子必须同步改成一条。该函数每次启动
都会 FirstOrCreate,若继续播种旧的两条,下次重启就会把迁移
1789500000000 合并掉的行重新建回来,过滤静默退回或关系。已加
TestFreshDatabaseSeedsOneStructuralRule 锁住。

关键词过滤不变,仍是子串匹配,并加测试防止字符集合语义误用到关键词上。

连带效果:真实样本 300斤牛奶絲圓領#A057 只含 #,不再被结构过滤命中后
落到关键词规则上仍被过滤——#269 把关键词称为 forward safety net,这是
它第一次真的接住东西。realworld 用例的统计由 char=5/keyword=0/kept=6
变为 char=3/keyword=1/kept=7。

app/goauto/sybimport 的 12 个失败先于本次存在(#285),未新增。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-15 17:15:40 +08:00
QiuSW ffa5fc0710 fix: add idempotent image snapshot column migration (#278) 2026-09-14 18:02:22 +08:00
QiuSW 35b69fc25a fix: narrow image search migration (#278) 2026-09-14 16:31:07 +08:00
QiuSW b4dc7fc9ae feat: track image search links and preserve manual overrides (#279) 2026-09-14 16:08:55 +08:00
QiuSW baa9408a36 feat: allow image search collection task source (#278) 2026-09-14 16:05:01 +08:00
QiuSW aed295c8c1 fix(syb): reduce parse status to success or failed (#272) 2026-09-12 11:36:43 +08:00
QiuSWandClaude Opus 5 503b3cbbc5 fix(goauto): create syb_product_filter on existing databases #269
Three defects that only a real MySQL run could show.

The table had no version-local migration. Adding the model to
migrations.Migrate covers databases built from scratch, but every existing
one already has the older versions recorded in sys_migration, so that list
never runs again there. cmd/migrate's post-migration table check caught it:
"迁移后仍缺少表:syb_product_filter". The new version file creates the table
and seeds the same eight rules with FirstOrCreate on the normalized key, so
running both paths against one database cannot duplicate them.

Keyword and NormalizedKeyword were typed text, which MySQL refuses to put in
a key specification without a prefix length (Error 1170), so the unique index
could not be built at all. They are sized 200 now, matching how SYBShop sizes
its own matching key. SQLite accepts text in an index, which is why the whole
test suite passed while the real migration failed.

The 只看启用 filter is removed rather than kept: hiding a disabled structural
rule would conceal the one state on this page that matters most, since a
disabled "#" means roughly 65% of 档口 rows start entering the system again.
Keyword search stays.

Verified against the local MySQL database: migration applies, eight rules
present with traditional Chinese preserved (印花大學T normalizes to 印花大學t),
menu row created under 采采管理.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-11 17:00:11 +08:00
QiuSWandClaude Opus 5 cad9f6d185 fix(goauto): add the missing menu migration for SYB product filters #269
Registering a module in access.GoAutoModules() does not make its page
reachable on an existing database. sys_menu rows are written by the
versioned migration that first created them, and that version is already
recorded in sys_migration everywhere — it never runs again. Startup only
reconciles the casbin API matrix, not menus.

So on a fresh database the page appeared and on every real one it did not:
the table, the API and the route were all present with no way to navigate
there. This adds a migration that upserts the entry under 采采管理 beside
「SYB 店铺」 and binds it to both 管理员 and 采购员.

Verified by deleting the row from an already-migrated database and
re-running migrate: 新执行 1 个,跳过 42 个, row restored with both roles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-11 16:49:56 +08:00
QiuSW 57b0f1595f feat(client-api): add scoped editable client keys (#237) 2026-09-07 16:20:46 +08:00
QiuSW 29ba16e4f9 feat(syb): allow purchaser manual sync (#236) 2026-09-07 15:03:31 +08:00
QiuSW bf58ad0005 merge: integrate main and purchase fixes for release (#234) 2026-09-07 11:58:24 +08:00
QiuSW bd386cfae4 fix(syb): 修正规格颜色尺码反序 (#216) 2026-09-04 19:33:35 +08:00
QiuSW 1ac50064bd fix(syb): 回填历史店铺标准化键 (#213) 2026-09-04 10:43:36 +08:00
QiuSW 13164adde0 fix(migrate): 补齐设备身份恢复字段 (#201) 2026-09-03 11:12:07 +08:00
QiuSW 81c1a7ead2 fix: add scheduled job execution history (#199) 2026-09-02 23:12:48 +08:00
QiuSW aa2ecc6ef7 feat: add scheduled SYB spec AI parsing (#198) 2026-09-02 21:33:14 +08:00
QiuSW b12460825c fix: prevent AI matching write timeout (#197) 2026-09-02 17:35:12 +08:00
QiuSW d7924619b6 feat: 增加蝦皮规格定时与手动自动匹配 (#195) 2026-09-01 23:41:11 +08:00
QiuSW d90bf66cc2 fix(auth): reload purchaser policies after reconcile (#161) 2026-08-31 14:23:12 +08:00
QiuSW f0f5f5bd14 feat(goauto): manage authenticated agent releases (#143) 2026-08-29 21:34:32 +08:00
QiuSW 148ba96521 feat(goauto): add managed purchase rules (#127) 2026-08-29 21:20:55 +08:00
QiuSW 7c18159d70 fix(#156): reconcile purchaser permissions at startup 2026-08-29 16:30:23 +08:00
QiuSW cc326ebc6c feat(#155): reuse failed collection tasks by attempt 2026-08-29 16:05:37 +08:00
QiuSW afa29f1d86 fix(#149): add purchase match work item migration 2026-08-29 14:06:00 +08:00
QiuSW 11c407d582 feat(#148): match purchase specs asynchronously 2026-08-29 11:51:32 +08:00
QiuSW f84ea5df3b feat(#142): group GoAuto admin navigation 2026-08-29 09:44:06 +08:00
QiuSW 6f611a04ef feat(goauto): collect cropped color images (#133) 2026-08-28 23:31:23 +08:00
QiuSW 72f54f76ac feat(goauto): add configurable module menus (#137) 2026-08-28 21:21:40 +08:00
QiuSW 679f4691a4 feat(goauto): add stock purchase task type (#135) 2026-08-28 20:53:51 +08:00
QiuSW 33d4bb17ad feat(#130): collect replacement products from Agent 2026-08-28 18:20:24 +08:00
QiuSW 92385c4a8d feat(#131): activate product replacements safely 2026-08-28 17:56:17 +08:00
QiuSW 696e0bca52 feat(#129): add PDD product replacement model 2026-08-28 17:33:15 +08:00
QiuSW 7b5574506a fix(#125): reject duplicate migration versions 2026-08-28 10:41:04 +08:00
QiuSW 5c6655f706 feat(#119 #120 #121): implement SYB inner-code workflow 2026-08-28 10:24:15 +08:00
QiuSW 28c3cb5138 feat: collect current PDD page from Agent (#101) 2026-08-27 10:53:48 +08:00
QiuSW b885a4ef66 feat(agent): implement #90 task history 2026-08-26 09:26:38 +08:00