Commit Graph
114 Commits
Author SHA1 Message Date
QiuSW bb1a410e8a feat(#329): add device purchaser ownership 2026-09-21 09:59:00 +08:00
QiuSW 368f2c2357 feat: filter purchase tasks by successful SYB order writeback (#327) 2026-09-19 15:38:42 +08:00
QiuSW 3a2472dd20 feat: complete purchase order information and simplify SYB writeback (#326) 2026-09-19 15:16:39 +08:00
QiuSW e89de1a085 feat: queue and reconcile SYB purchase order numbers (#305) 2026-09-18 10:13:20 +08:00
QiuSW 07a3817591 test: verify optional order amount in purchase detail (#306) 2026-09-18 09:30:20 +08:00
QiuSW 8c01329f97 feat: capture optional PDD order amount in manual backfill (#306) 2026-09-18 09:22:12 +08:00
QiuSW 9194fd664f fix(ops): allow local Admin LAN access (#304) 2026-09-17 17:02:35 +08:00
QiuSWandClaude Opus 5 60c75261d3 fix: 采购管理批量重试改为原地重试选中任务,不再新建 (#303)
采购员勾选失败任务点重试,要的是这条任务本身再跑一次。原先 BatchRetry 一律
调用 Create 新建(CG-224 → CG-234),任务号变化,同一 SYB 明细的多次尝试分散在
多个任务上。

- BatchRetry 改为逐条调用既有 Reset:任务号不变,状态回到 pending,本次执行记入
  purchase_task_attempt。Reset 的全部保护原样沿用——碰过下单边界、同一明细已有
  更新任务、规格快照不完整均拒绝并返回原因,拒绝后不退回新建。
- 先识别重放再做资格预检:首次重试后任务已是 pending,先预检会把同一 requestId
  的重复提交判为「只有失败任务可以重试」,破坏幂等。测试抓到后修正。
- 资格预检关闭设备占用检查,同一设备上勾选的多条可排队;真正的占用判断在 Reset
  事务内,ensureDeviceFree 不把租约为空的 pending 计为占用。
- AgentRetry(替代商品已匹配、继续采购)改走 batchRetryCreate,保持新建:商品已
  替换,原任务的商品与规格快照不能复用。
- 前端文案改为「已重试 / 第 N 次执行」。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-17 14:50:05 +08:00
QiuSWandClaude Opus 5 03647dd7dd feat(web): 采集任务页增加来源筛选与「取消未开始的任务」按钮 (#298)
页面混显三种来源却只有 goodsId 和状态两个筛选,批量取消时无法限定范围。

- 服务端 AdminList 增加 source 过滤,非法值报参数错误而非静默忽略。
- 前端增加来源筛选与来源列;状态补 cancelled(info 色,取消不是错误,不与
  failed 共用红色)。
- 「取消未开始的任务」按钮带实时数量,确认框列出将被取消的任务,并写明范围是
  整个筛选条件而非当前页。hasMore 时提示还有未处理的任务。

`[必须]` 取消范围包含 goodsId。少了这一维,按 goods_id 筛出两条、按钮却取消
三十几条——那正是当初放弃「两个固定按钮」、改用「筛选 + 一个按钮」想避免的事。
BatchCancel 的 goodsId 与 AdminList 用同一种匹配方式,否则两边范围会悄悄错开。

实施:sonnet 子代理;goodsId 范围一致性由复核补入。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-16 16:39:07 +08:00
QiuSWandClaude Opus 5 2f1e88ea04 fix(web): 未关联 PDD 的 SYB 明细可勾选,打通图搜采集入口 (#291)
勾选框的 isSelectableCandidate 只有采购、采集、AI 匹配三个判据,三个都要求
虾皮商品已关联 PDD。图搜采集的用途恰恰是给未关联的商品找 PDD 商品,于是
入口对它最该服务的那类商品一直不可达,按钮计数恒为 0。

增加 isImageSearchCandidate,与 imageSearchRows 的过滤条件一致(有虾皮商品
且有参考图)。只放宽勾选,三个按钮的候选集合各自的判据不变,采购门禁不受影响。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-16 10:59:43 +08:00
QiuSWandClaude Opus 5 41996f33b2 feat(server): 图搜采集成功后自动触发规格匹配 (#287)
图搜是异步的:Agent 在手机上跑几分钟,采购员必须离开页面、稍后回来、
重新勾选、再点 AI 匹配。旧流程虽然全手工但一气呵成,图搜把它切成了
两段,中间靠人记着回来。本次在关联写入之后接一个钩子消除这个断层。

`[必须]` 只能接在这里。图搜批量结果对话框在任务创建后立刻弹出,那时
Agent 一个都还没执行,无从匹配。

autoLinkImageSearch 改为返回是否确实写入关联及受影响的 SYB 明细。
RowsAffected 为 0 表示乐观并发谓词拒写——任务创建后有人改过这条关联,
目标商品已不是我们找到的那个,此时匹配上去只会把错误结果写进档案,
因此不触发。

匹配失败只记录不冒泡:采集结果已经提交完成,不能因为这个可选增强让
SubmitResult 失败、让 Agent 以为采集没成功。

不自动创建采购。匹配只把明细推到“采购就绪”,创建采购仍由人点击——那是
整条链上唯一的人工检查点,因为图搜找商品、自动关联、AI 匹配规格三步
都没有人看过,而 #200 已取消 SYB 批量入口的置信度门槛,不能以“AI 没
把握会停下”来兜底。

标记文案相应扩展为“商品由图搜找到、规格由 AI 自动匹配,均未经人工
确认”,覆盖 SYB 商品页、采购任务列表与详情、虾皮商品详情抽屉。

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:51:42 +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
QiuSWandClaude Opus 5 750b8763dc feat(#280): 图搜确认后覆盖已有关联,去掉覆盖复选框
用户 2026-09-15 选定行为:去掉「覆盖已有关联」复选框,点击创建时若
所选商品中存在已关联的,弹窗说明将被覆盖,确认后覆盖。

原复选框是误导的:它只放行任务创建,落库时的 CAS 谓词仍要求
pdd_product_id IS NULL,所以勾上之后设备白跑 20-40 秒、关联一点不变,
界面还显示任务已完成。

服务端把关联写入从「必须为空」改为乐观并发:比对快照里的
OriginalPDDProductID,只要任务创建后没人动过这条关联就写入。直接去掉
谓词会让 Agent 执行的 20-40 秒变成静默吞掉人工改动的窗口;改为乐观并发
既满足覆盖需求,又让并发的人工改动继续胜出。该快照字段与
sameImageSearchLink 早已存在,此前未被关联路径使用。

前端确认框只在确实存在已关联商品时弹出并给出条数,一条都没有时不弹,
避免变成每次都要点掉的噪声;取消则整批不创建。

测试重写为四个用例:未变更时覆盖、任务创建后被改则放弃、创建时无关联
但执行中被抢先关联则放弃、常规无关联路径正常写入。原
TestAutoLinkImageSearchDoesNotOverwriteManualAssociation 断言的是被本次
取代的旧契约,已移除。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-15 15:23:34 +08:00
QiuSWandClaude Opus 5 b4393dac84 fix(web): SYB 商品页按钮归位为 图搜采集 | 创建采集 | 创建采购 (#280)
图搜采集原先是个游离按钮,挂在工具栏下方的提示条之后,与另外两个
批量操作分离。按用户 2026-09-15 的要求移入工具栏,并把三个批量操作
按从轻到重排列:图搜采集 -> 创建采集 -> 创建采购。

只调整按钮位置与顺序,不改动 v-if、disabled 条件、点击处理和计数。
顺带去掉图搜按钮文案与计数之间多余的空格,与相邻两个按钮一致。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-15 15:07:04 +08:00
QiuSW 2ddfd52f5d Merge branch 'fix/b280-imagesearch' into integrate/imagesearch 2026-09-15 10:36:34 +08:00
QiuSWandClaude Opus 5 c2c0bae056 feat(purchase): surface image-search-linked marker in purchase views (#279)
Item 5 of #279 required bringing the image-search auto-link annotation
into the purchase-facing views, display-only, without gating anything.

- BatchPreviewItem and AdminTaskItem now carry imageSearchLinked, sourced
  read-only from shopee_product.image_search_linked (already loaded in
  the batch preview dataset; bulk-loaded for the task list/detail views).
- Batch purchase preview dialog and purchase task list/detail now show a
  "图搜未核" tag plus a restrained summary hint when applicable.
- Does not touch Eligible/ReasonCode/Reason/NextAction or the #283
  mappingTargetsValid gate; regression test confirms an ineligible row's
  outcome is unchanged when imageSearchLinked is true.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-15 10:35:22 +08:00
QiuSWandClaude Opus 5 b7babfba7e feat(#280): cap image-search batch size and warn about device time
Server: BatchCreateImageSearch now rejects a batch whose deduplicated
task count (one task per shopee_product, after merging SYB detail
rows) exceeds imageSearchMaxBatchTasks (50), returning
IMAGE_SEARCH_BATCH_TOO_LARGE with a readable Chinese message instead
of silently truncating. The limit is overridable via
GOAUTO_IMAGE_SEARCH_MAX_BATCH_TASKS. #277's scheduling floor (采购 >
采集 > 图搜) isn't implemented yet, so this cap is the only guard
against a large image-search batch starving manually started
collection/purchase tasks.

Web: the batch confirm dialog now shows the deduplicated task count
and an estimated device-occupation time range (20-40s/task), and
disables the submit button with an explanation when the batch would
exceed the same limit, instead of letting the request fail after
submit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-15 10:35:21 +08:00
QiuSW ba7d2a6b72 feat(#280): remember image search device 2026-09-15 09:57:17 +08:00
QiuSW 56706a0b45 fix(#280): show image search association marker 2026-09-15 09:45:50 +08:00
QiuSW c80d0c91d5 feat: add image search batch dialog to syb products (#280) 2026-09-15 09:07:45 +08:00
QiuSW be491c7692 feat: expose image search batch API to web (#280) 2026-09-15 09:02:08 +08:00
QiuSW 804448ede0 feat: show image search link marker in shopee detail (#279) 2026-09-15 08:59:37 +08:00
QiuSW 5a7e219230 feat: add image-search linked filter to shopee list (#279) 2026-09-15 08:58:35 +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 20f59b68bd fix(goauto): record SYB filter hits per rule, not per kind #269
UpdateHits wrote one aggregate per kind, so every structural rule stored
the same number. The disable-confirmation dialog quotes that number
("停用「#」后…约 N 条明细会恢复入库"), and on real data "#" matches 8498
rows while "-" matches 6392 — the dialog would have warned with a figure
2100 rows off for one of them.

Match now returns the rule that matched instead of a char/keyword pair,
so the caller can attribute each hit to one rule, and UpdateHits writes
every rule's own count. Rules that matched nothing are written as 0 so a
previous run's number is never left behind.

The management page is reformatted to the repo's usual Vue layout and
gains the search / 只看启用 / 重置 controls the approved prototype shows.
Cancelling the disable confirmation now just reverts the switch instead
of throwing an unhandled rejection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTDbDcwbDw1TSAcE6wfh2F
2026-09-11 16:27:13 +08:00
QiuSW 652bf30f5a feat(goauto): add SYB product variation filters (#269) 2026-09-11 16:20:26 +08:00
QiuSW 3181302c16 test: capture stable replacement confirmation states #258 2026-09-10 15:48:17 +08:00
QiuSW 2b01974fe5 feat(syb): confirm one-click replacement from latest collection #258 2026-09-10 15:47:27 +08:00
QiuSW b1594dc7bd feat(syb): link latest device collection and match specs #253 2026-09-10 11:34:22 +08:00
QiuSW 9088e6b734 fix(shopee): bound matching waits and recover drawer loading #254 2026-09-10 09:37:02 +08:00
QiuSW c6a962d522 fix(syb): persist validated pages and report partial sync success (#239) 2026-09-08 09:35:36 +08:00
QiuSW 71f7751754 fix(client-api): support HTTP and HTTPS by default (#237) 2026-09-07 17:25:55 +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 1900dab32e feat(web): remember batch purchase device per user (#233) 2026-09-07 11:25:12 +08:00
QiuSW 58e058f8ac feat: 一键匹配并确认蝦皮颜色尺码 (#194)
(cherry picked from commit 041cd8d03f)
2026-09-03 15:05:53 +08:00
QiuSW 2c263e687e feat(device): 后台自动恢复设备身份 (#201) 2026-09-03 11:16:03 +08:00
QiuSW 379a83fe94 feat(device): 安全恢复原设备身份 (#201) 2026-09-03 10:41:20 +08:00
QiuSW 81c1a7ead2 fix: add scheduled job execution history (#199) 2026-09-02 23:12:48 +08:00
QiuSW d7924619b6 feat: 增加蝦皮规格定时与手动自动匹配 (#195) 2026-09-01 23:41:11 +08:00
QiuSW 041cd8d03f feat: 一键匹配并确认蝦皮颜色尺码 (#194) 2026-09-01 22:20:48 +08:00
QiuSW 55c2b6b254 Revert "feat(web): SYB 列表页就地人工修正解析结果 (#190)"
This reverts commit 16c9216a5a.
2026-09-01 17:42:17 +08:00
QiuSWandClaude Opus 5 16c9216a5a feat(web): SYB 列表页就地人工修正解析结果 (#190)
解析失败或存疑时,列表行动作原先跳转打开详情抽屉,用户还需在抽屉内
再找一次「人工修正」。改为直接打开修正弹层,减少两次跳转。

- runPurchaseNextAction 的 reparse 分支改为 openCorrect(row)
- openCorrect 支持列表行与详情抽屉两个来源,fromList 决定保存后
  刷新列表还是刷新抽屉
- 动作按钮文案「查看并处理」改为「人工修正」
- 修正抽屉内 @click="openCorrect" 会把 MouseEvent 当作 row 传入的
  缺陷,改为 @click="openCorrect()"

后端与 ManuallyConfirmed 通道已存在(4a1b4af),本次不改服务端。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 16:56:57 +08:00
QiuSW 776db137be fix: require saved spec match before purchase (#188) 2026-09-01 15:32:26 +08:00
QiuSW 5446ebeaae fix: tighten batch AI matching eligibility (#188) 2026-09-01 14:49:00 +08:00
QiuSW 3ef0f72ff9 feat: batch AI match SYB purchase specs (#188) 2026-09-01 12:13:33 +08:00
QiuSW 6ab2f62f39 feat(web): 内联展示商品详情抽屉 (#186) 2026-09-01 11:23:09 +08:00
QiuSW b31896ef75 fix(web): restore cached tab state (#187) 2026-09-01 10:33:27 +08:00