feat(store_mapping): 跨平台店铺映射表 + pricebot 内部上报端点 #48

Merged
marco merged 5 commits from feat/taobao-shopid-store-mapping into main 2026-06-14 01:24:30 +08:00
Owner

新增「平台店铺表」资产层: 淘宝比价拿到 shopId 后, pricebot server→server 把跨平台
店铺身份(各平台 id/名 + 地理 + 来源)落库, 作为未来"我见过这家店→跳过重搜/匹配"的源头。
与 price_observation(价格事实)平行、独立。

  • models/store_mapping.py: store_mapping 表 22 列 —— 跨平台身份(id/name_taobao/meituan/jd)
    • 地理(city/geohash/lng/lat/taobao_address) + 溯源(source_platform/trace_id/device/user)
    • 淘宝原料(share_url/resolved_url/deeplink) + attrs(JSONB) + created_at。
  • schemas/store_mapping.py + repositories/store_mapping.py: append-only, trace_id 幂等
    (pricebot 重试/replay 不重复写; 并发 IntegrityError 兜底返已存在行), 跨方言安全。
  • api/internal/store.py: POST /internal/store-mapping(复用 price.py 共享密钥 X-Internal-Secret 校验)。
  • 注册 model(init) + router(main.py); 迁移 store_mapping_table 接现 head coin_txn_task_ref_uq。

验证: alembic 单 head + 零模型/迁移漂移; TestClient 全链(无密钥401/有密钥inserted=1/幂等inserted=0/错密钥401)。

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

新增「平台店铺表」资产层: 淘宝比价拿到 shopId 后, pricebot server→server 把跨平台 店铺身份(各平台 id/名 + 地理 + 来源)落库, 作为未来"我见过这家店→跳过重搜/匹配"的源头。 与 price_observation(价格事实)平行、独立。 - models/store_mapping.py: store_mapping 表 22 列 —— 跨平台身份(id/name_taobao/meituan/jd) + 地理(city/geohash/lng/lat/taobao_address) + 溯源(source_platform/trace_id/device/user) + 淘宝原料(share_url/resolved_url/deeplink) + attrs(JSONB) + created_at。 - schemas/store_mapping.py + repositories/store_mapping.py: append-only, trace_id 幂等 (pricebot 重试/replay 不重复写; 并发 IntegrityError 兜底返已存在行), 跨方言安全。 - api/internal/store.py: POST /internal/store-mapping(复用 price.py 共享密钥 X-Internal-Secret 校验)。 - 注册 model(__init__) + router(main.py); 迁移 store_mapping_table 接现 head coin_txn_task_ref_uq。 验证: alembic 单 head + 零模型/迁移漂移; TestClient 全链(无密钥401/有密钥inserted=1/幂等inserted=0/错密钥401)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
marco added 1 commit 2026-06-13 20:39:25 +08:00
新增「平台店铺表」资产层: 淘宝比价拿到 shopId 后, pricebot server→server 把跨平台
店铺身份(各平台 id/名 + 地理 + 来源)落库, 作为未来"我见过这家店→跳过重搜/匹配"的源头。
与 price_observation(价格事实)平行、独立。

- models/store_mapping.py: store_mapping 表 22 列 —— 跨平台身份(id/name_taobao/meituan/jd)
  + 地理(city/geohash/lng/lat/taobao_address) + 溯源(source_platform/trace_id/device/user)
  + 淘宝原料(share_url/resolved_url/deeplink) + attrs(JSONB) + created_at。
- schemas/store_mapping.py + repositories/store_mapping.py: append-only, trace_id 幂等
  (pricebot 重试/replay 不重复写; 并发 IntegrityError 兜底返已存在行), 跨方言安全。
- api/internal/store.py: POST /internal/store-mapping(复用 price.py 共享密钥 X-Internal-Secret 校验)。
- 注册 model(__init__) + router(main.py); 迁移 store_mapping_table 接现 head coin_txn_task_ref_uq。

验证: alembic 单 head + 零模型/迁移漂移; TestClient 全链(无密钥401/有密钥inserted=1/幂等inserted=0/错密钥401)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
marco added 1 commit 2026-06-13 21:54:10 +08:00
store_mapping 的语义是"一次比价(一个 trace)= 一个真实店铺一行", 各目标平台解析出自己的
shopId 就往同一行填自己那几列: 淘宝腿先建行(id_taobao/name_taobao), 将来京东腿解析出
id 再合并进同一行(id_jd/name_jd)。trace_id 唯一约束天然就是这一行的合并键。

原 insert 是"撞 trace 就跳过", 第二条腿来上报会被丢掉, 撑不起渐进填充。改成 upsert:
- 不存在 → INSERT(同原行为)
- 已存在 → 填空合并(fill-the-blanks): 只写该行当前为 NULL 的列, 绝不覆盖已有非空值。
  保证后到平台只填自己的列、动不了先到平台的数据; 共享列(geo/source/溯源)先到先得。
- 并发撞唯一约束 → 回滚后转走同一填空合并路径(不依赖 ON CONFLICT, PG/SQLite 都安全)。

返回值 (created, row_id): created=1 新建 / 0 合并; 端点日志区分"新建/合并"并带上 jd 列。

注: 一行里 id_taobao 与 id_jd 共存只是 name-match 置信度(两腿搜同一源店名各自匹配到某店),
非已核实同一实体; 作 append-only 原始资产留存, 精确匹配下游做。

验证: TestClient 打真实 PG —— 淘宝建行(inserted=1) → 京东同 trace 合并(只 UPDATE id_jd/
name_jd, geohash 不进 SET) → 淘宝重报不同 id 不覆盖(无 UPDATE)。最终一行各列正确, 已清理。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
marco added 2 commits 2026-06-13 22:36:18 +08:00
美团接入店内搜索 deeplink 链路, 表加 4 列存美团侧店铺身份/原料:
- meituan_poi_id_str: 分享链 302 反查出的 poi_id_str(索引)
- meituan_share_url / meituan_resolved_url / meituan_deeplink: 与 taobao_* 平行

poi_id_str 不进 id_meituan: 调研实证它每次分享重新加密、非稳定主键(同店两条分享链
两个值, 见 docs §八), 单列当"可复跳的一次性票据"存; id_meituan 留给将来 CPS API
拿到的稳定数字 poi_id。4 列均纳入 upsert 填空合并集。

迁移 store_mapping_meituan_cols(down=store_mapping_table); 已 upgrade, 单 head,
模型零漂移, 合并不变式 26列=22合并+4排除 仍成立。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
京东秒送接入: 店铺身份是两个稳定数字 id —— storeId 进 id_jd(同 taobao shopId→id_taobao),
venderId(deeplink 还需、商家维度)单列存 jd_vender_id; 另加 jd_share_url/jd_resolved_url/
jd_deeplink 原料列(与 taobao_*/meituan_* 平行)。迁移 store_mapping_jd_cols 加这 4 列 +
jd_vender_id 索引。

顺手修一个潜在 bug: upsert 的首写(INSERT)原来硬编码只灌 taobao+通用列, 首写的若是美团/
京东腿, 它们的列不在构造器里会被丢。改成按 _MERGE_COLUMNS 逐列 setattr, 任意平台首写都全量
落库(美团也受益)。合并(填空不覆盖)逻辑不变。

验证: 迁移单 head、store_mapping 无新漂移; TestClient 打真 PG —— 京东首写 4 列全落 →
淘宝同 trace 合并填淘宝列、不覆盖京东列。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
marco added 1 commit 2026-06-14 00:45:13 +08:00
比价前 pricebot 调它反查各目标平台已沉淀的店铺 id, 命中则目标腿直接 deeplink 跳店内搜索。
- lookup_nearest: 按"源平台对应 name 列 == 源店名"匹配; 每个目标**分别**取"含该目标 id 的同名候选
  里最近一条"(haversine, 淘宝 id / 京东 id 可能在不同行); 排除源平台自己; 有入参 geo 取最近、否则
  取 created_at 最新。返 {target: {店铺id..., deeplink, row_id, dist_km}}。
- 端点日志打源/店名/geo/命中行/距离/deeplink, 与 pricebot 侧对账。
- 同次提交带上 upsert 首写改逐列灌 _MERGE_COLUMNS(任意平台首写不丢列)+ 美团/京东合并列。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
marco merged commit 8659a7ed2b into main 2026-06-14 01:24:30 +08:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: WonderableAI/shaguabijia-app-server#48