Commit Graph
9 Commits
Author SHA1 Message Date
q792602257 f76c0a1518 feat(trading): support multi-item purchase intents 2026-08-31 11:41:39 +08:00
q792602257andClaude Opus 5 b577d3ac8d fix(trading): 必填选项自动填值跳过「選択してください」占位项,并把选项开放给接口
trading 自动填 choice 时取 values[0],而必填 select 的 values[0] 恒为 id=0 的
「選択してください」——等于把「请选择」当答案提交。4 份真实样本一致(真值从
id=200 起)。同时 /api/item_detail 完全不返回 options,调用方即使想显式指定
choice 也无从知道合法取值。

- purchase_contract.py:新增 ItemOption / ItemOptionValue 与 parse_options /
  auto_choice_for / format_choice。占位判定以结构为主(value_id == 0),日文
  文案仅作兜底。放 shared 是因为「接口声明的合法取值」与「下单实际提交的值」
  必须同源,否则两边各判一次迟早再次分叉
- item.py / scrape.py:ItemDetailData 增 options、has_required_options、
  unfillable_required_options;只解析一次,两个派生结果都取自同一份结果
- site_interact.py:auto_choice_for 取第一个非占位候选;必填项填不出值时
  报错点名是哪些选项,让调用方知道该在 intent.choice 里补什么
- auto_choice_for 只自动填必填项:非必填项要不要选是业务决定,不是我们该替
  调用方做的选择
- README / docs:补 options[] → intent.choice、variants[] → intent.variant_id
  的对照,修掉 order-gateway 示例里已不存在的 "options": {} 字段

真账号验证(scripts/probe_option_choice.py,仅加购不结算不支付):两个商品
提交 確認した / 了解致しました。均被站点接受,购物车 count=2,跑完清空恢复
原状。探针刻意走生产的 add_to_cart_payload 并从其日志截获实际 payload——
probe_purchase_block_v2.py 自己抄了一遍字段构造,与生产代码同错,正是这个
bug 当初藏住的原因。

未覆盖:这两家店铺本身不校验该选项(旧的占位值当年也被收下),所以只证明新值
走得通、语义上才是真答案,证明不了旧值会被拒;必填自由文本项(
unfillable_required_options)无真实样本,仅离线测试覆盖。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 16:07:33 +08:00
q792602257 48c6f2a28f feat(gateway): 下单任务支持 callback_url 终结类事件异步通知
- POST /api/orders 新增可选 callback_url(仅 http/https,其余 422)
- 仅终结类事件各通知一次:terminal report 推入终态(succeeded/failed/
  needs_human)、租约过期被 sweep 置 stale;中间态与终结后的监控上报不通知
- 幂等重发不更新既有任务的回调地址;投递 best-effort 单次尝试,失败只记日志
- CallbackNotifier 发后不管(create_task + 在途任务强引用),关停等在途发完;
  生产路径按回调地址逐次构造客户端,满足统一出站代理策略(test_proxy.py)
- tasks 表加 callback_url 列,GatewayDB.start() 内置迁移兼容既有库
- 新增 RAKUTEN_CALLBACK_TIMEOUT_SECONDS(默认 10);文档补 §4.8;
  openapi.json 重新导出(gitignore 未跟踪);新增 17 条测试,全量 492 通过
2026-08-17 01:05:37 +08:00
q792602257andClaude Opus 5 f6c3976c0a feat(gateway): 定时下派通道——周期性盘点账号订单并回写网关编目
新增 §12 定时下派通道:网关自己定期派 order_list / order_detail 查询,把账号
真实订单沉淀进新表 account_orders(回写网关),上游可直接查 GET /api/account/orders
拿到账号里实际有哪些订单,不必自己记 order_number。

- collector.py: OrderDiscoveryCollector 常驻后台任务(与 sweep 并列)。每隔
  account_discovery_interval_seconds 派 order_list,收割结果后把「编目里没有的
  订单」upsert 进编目,再逐笔派 order_detail 沉淀 delivery_status/order_state。
  状态无痕:不新增编排跟踪表,仅内存 _pending + _detail_dispatched_this_day;
  detail query_id 按天分段(discover-detail-<order>-<日期>),同天幂等防重复派。
  边界:collector 只消费 worker 结果的规范化字段,绝不解析 raw/raw_pages。
- db.py: account_orders 表 + AccountOrderRow + upsert/single/list/stale 访问;
  upsert 以 order_number 为主键,重复采集只刷新,列表重扫不抹详情节点。
- 路由: GET /api/account/orders(列编目)、GET /api/account/orders/{n}(6005)、
  POST /api/account/discovery/trigger(手动立即派一轮 list)。
- config: RAKUTEN_ACCOUNT_DISCOVERY_ENABLED / _INTERVAL_SECONDS / _MAX_PAGES /
  RAKUTEN_ACCOUNT_DETAIL_REFRESH_SECONDS。
- 既有网关测试夹具统一关 discovery(会启动即派扫描污染查询队列语义),
  新表测试在 tests/test_gateway_discovery.py(13 用例,真实 QueryQueue+DB 装配)。

472 tests passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 23:35:51 +08:00
q792602257 3b53b8f28b @
feat(trading): 详情页派送到真实样本,补结构化配送状态并修 stepper 回归

用真账号实测爬取订单详情页(scripts/probe_order_detail.py,样本落盘
.probe/order_detail/),首次拿到 pageType="ph-detail" 的真实 __INITIAL_STATE__,
此前「详情页结构从未有样本、只原样透传」的缺口由此闭合:

- _parse_order_detail_status:新增,优先从 orderData.shippingList[].deliveryInfo
  .deliveryStatus 结构化枚举判配送阶段;映射遵循「只映射实测值」,目前仅
  CHECKING_ORDER,未识别枚举交回 stepper 兜底(防掐掉 SHIPPED/DELIVERED 上报)。
- _ORDER_STEPPER_ITEM_PATTERN:修回归——不锚定 <li class="item--3gWCU"> 时面包屑
  li 会抢走进度条第0项、导致 -active-- 永远判不出当前阶段(真实样本复现)。
- OrderStatusSnapshot 增 delivery_status 字段;query_runner order_detail 透传。
- docs/order-gateway.md §11 更新实测边界;新增详情页样本回溯测试。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@
2026-08-16 23:04:59 +08:00
q792602257andClaude Opus 5 c03158488b feat(gateway): 账号只读查询通道——从已登录账号取真实订单
上游要的不只是网关记的任务状态镜像,还有「已登录账号在站点上的真实订单」,
但账号只在 NAT 后本地机上,只能经网关队列走。新增独立查询通道(§11):

- gateway 单开 account_queries 表 + QueryStatus 状态机,接口
  POST /api/account/queries(幂等)/ lease / {id}/result / {id}
- 不复用下单任务队列:查询是只读,租约过期可安全重投(与下单「绝不自动
  重投」相反),且不该被全局并发度 1 堵死、task_reports 是订单镜像不能污染
- 本地交易服务起第二条常驻循环 query_runner,领到即调 SiteInteractor 真读:
  order_list 复用已实测的 list_recent_orders(规范化字段 + 站点
  orderListData 原文),order_detail 复用 fetch_order_detail(配送阶段 +
  页面 __INITIAL_STATE__ 原样透传,结构未经真实样本,不抽字段)
- 账号级串行仍由 SiteInteractor 的锁保证;每次执行套超时按失败回报
- 错误码 6005/6006(查询通道,可重试只读区别于 6001-6004);/health 暴露
  queued_query_count;结果体积上限先丢原始 JSON

openapi.json 重导,docs/order-gateway.md §11、README、.env.example 补全
配置与实测边界。全量测试 404→454 通过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 22:46:01 +08:00
q792602257andClaude Sonnet 5 51e2538438 实现付款后订单监控:真实探测 order.my.rakuten.co.jp 配送阶段
用真实订单号 306087-20260813-0863947697 探测 order.my.rakuten.co.jp(订单列表/
详情页),拿到真实 DOM 结构后实现 SiteInteractor.check_order_status:

- 详情页 URL 可直接从 site_order_id 构造(shop_id 是订单号第一段)
- 配送阶段用「进度条」组件的 4 个固定阶段(ショップ/出荷/配達店/配達完了),
  当前阶段的 class 带 -active-- 中缀,映射到 OrderState.SHIPPED/DELIVERED
- 查不到订单号、进度条解析不出新阶段都不算错误,交给轮询循环继续重试

runner.WorkerRunner 新增 _monitor_order 后台轮询:付款成功上报后以
asyncio.create_task 起后台任务(不阻塞主循环领下一单,因为 SiteInteractor 的
Playwright 操作全程持锁串行化),状态变化时用 terminal=False 追加 report;
新增 cancel_monitors() 在服务关闭时于 SiteInteractor.close() 之前收尾。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 23:01:35 +08:00
q792602257andClaude Opus 4.6 65e3ed31f8 交易服务收敛为仅 rakuten:移除 rakuma 登录态与下单入口
trading 不再管理 ラクマ 的购物车购买、支付与订单监控。删除 auth_site /
auth_session / login_runner / models.AuthSite 中的 rakuma 分支与常量、
account.yaml.example 的 rakuma 段,并清理相关测试。scraping 侧的
ラクマ 抓取 API(/api/rakuma/*)保留不动。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-28 11:50:07 +08:00
q792602257 5376ced511 补下单任务网关规格
抓取在服务器、下单在本地,本地在 NAT 后没有公网入口,因此下单请求由本地长轮询
主动领取,而不是从服务器推进来。docs/order-gateway.md 给出可直接实现的规格:
任务队列与状态镜像的表结构、四个接口契约、任务状态与订单状态的分层、证据留痕约定、
配置项与验收清单。

其中一条是安全关键:租约过期**不自动重投**。本地可能已经下单成功只是回报断网,
自动重投等于再买一次;过期任务转 stale 告警,恢复时必须先查站点订单列表核对。

站点交互(加购/下单/付款)尚未实测,文档里明确留成接口缝并列出待实测项。
2026-07-27 15:20:11 +08:00