2026-06-25-rating-datasync-trend-design.md 5.6 KB

数仓同步 + 卡片趋势接口 + 推荐增强 设计文档(Design Spec v2)

项目:RatingRecommend | 日期:2026-06-25 前置:本文是 2026-06-24-rating-efficiency-recommend-design.md 的扩展 状态:已评审,待放行写库后实施


1. 目标

  1. 数仓同步:数仓每日 ETL 完成后,通过 HTTP 主动推送卡片规则数据(含 POP report、价值、价格历史)到本服务并落库。
  2. 卡片趋势/详情查询接口:给前端提供对齐图片 UI 的卡片详情(身份 + POP + 价值 + 价格走势曲线)。
  3. 推荐响应增强:推荐接口命中后,透传 POP/价值数据。

2. 关键决策(本轮新增确认)

# 决策 说明
E1 POP/价值/趋势对推荐仅展示透传 recommend_efficiency 仍由数仓直接给,推荐降级匹配逻辑不变
E2 卡片唯一键 = 数仓 card_id 去重根基
E3 POP report 按三机构各一套(PSA/BGS/Nexphostis) 用明细行存表
E4 数仓主动推(HTTP)+ T+1 每日批量 本服务被动接收
E5 规则/POP/当前价:全量批次替换(batch_no + 指针切换,查询零中断) 当前快照
E6 价格历史:增量追加(每日追加当天价,永久累积,不被全量覆盖) 趋势数据源
E7 数据量小(万级),单次全量推送 + 单事务切换

3. 表结构

3.1 t_rating_recommend_rule(规则主表,加字段)

新增:card_idcard_nocurrent_valuevalue_change_pctvalue_minvalue_maxbatch_no 唯一约束:(batch_no, card_id);查询索引:(batch_no, series, card_set, year)

3.2 t_rating_card_pop(POP 明细,新表)

id, card_id, agency, grade_all, grade_10, grade_9, grade_8, grade_7, batch_no 索引:(batch_no, card_id)

3.3 t_rating_card_value_history(价格历史,新表,增量累积)

id, card_id, price, record_date, create_time 唯一约束:(card_id, record_date)(同卡同日只一条,幂等)

3.4 t_rating_recommend_active_batch(生效批次指针,新表,单行)

id, active_batch_no, update_time

4. 数仓同步接口

POST /api/recommend/rule/sync(内部鉴权:Token/签名/IP 白名单)

请求体:

{
  "batchNo": "2026062501",
  "cards": [
    { "cardId":"...", "cardNo":"066/080", "player":"AIPOM", "year":"2026",
      "series":"pokemon.M2.JNP", "cardSet":"BASE", "recommendEfficiency":2,
      "currentValue":15, "valueChangePct":7.05, "valueMin":10, "valueMax":45,
      "recordDate":"2026-06-25", "todayPrice":15,
      "pops":[ {"agency":"PSA","all":660,"g10":25,"g9":152,"g8":220,"g7":458}, ... ] }
  ]
}

处理(单事务):

  1. 校验:batchNo 合法、cards 非空(空拒绝,防全表清空)、batchNo 必须比当前生效批次新(防回灌)
  2. 按 cardId 去重(同批次重复 cardId 取最新/报错)
  3. 全量替换部分:DELETE 该 batchNo 残留(规则表 + POP 表)→ 批量 INSERT 规则表 + POP 表
  4. 增量追加部分:价格历史表按 (card_id, record_date) UPSERT 当天价(同日幂等)
  5. UPDATE 指针表 active_batch_no = batchNo
  6. 提交事务(查询原子切到新批次;历史表已追加)
  7. 事务后异步 DELETE 更早批次(规则表 + POP 表保留最近 1~2 批)

5. 卡片趋势/详情查询接口

GET /api/recommend/card/detail?cardId={cardId}&period={7d|30d|90d|all}(period 默认 30d)

响应 data = CardDetailVO

{
  "cardId":"...", "cardNo":"066/080", "name":"AIPOM", "year":"2026",
  "series":"pokemon.M2.JNP", "cardSet":"BASE", "imageUrl":"...",
  "pops":[ {"agency":"PSA","all":660,"g10":25,"g9":152,"g8":220,"g7":458}, ... ],
  "value":{"current":15,"changePct":7.05,"min":10,"max":45},
  "trend":[ {"date":"2026-06-01","price":12.0}, {"date":"2026-06-25","price":15.0} ]
}
  • 身份 + value:查规则表当前生效批次(按 card_id)
  • pops:查 POP 明细表(card_id + 生效批次)
  • trend:查价格历史表(card_id + period 时间窗,按日期升序) > 注:imageUrl 若数仓未提供则为 null(图片字段本期可选)

6. 推荐响应增强

  • RecommendCardVO 增字段:cardId、cardNo、currentValue、valueChangePct、valueMin、valueMax、pops[]
  • RecommendServiceImpl:三层降级命中规则后,带出 card_id/价值,并按 card_id 补查 POP 明细放入 VO
  • 规则查询统一加 batch_no = 当前生效批次 过滤(读指针表,可缓存 30s)

7. 去重 & 历史

  • 唯一键 card_id;同批次按 card_id 去重;规则/POP 跨批次全量替换 → 无脏历史
  • 价格历史按 (card_id, record_date) 幂等累积,永久保留

8. ⚠️ 写库清单(CLAUDE.md 红线,需大侠放行)

写操作 位置
4 张表 DDL(规则表加列 + POP/历史/指针 新表) 由大侠手动执行
规则表/POP 表 DELETE + INSERT(全量替换) 同步 Service/Mapper
价格历史表 UPSERT(增量) 同步 Service/Mapper
指针表 UPDATE(切批次) 同步 Service/Mapper
旧批次异步 DELETE 同步 Service/Mapper

推荐查询、趋势查询、POP 查询均为只读 SELECT,不在红线内。

9. 测试策略

  • 同步去重/防回灌/空保护:Service 单测(Mockito)
  • 趋势 period 时间窗过滤:Mapper/Service 验证
  • 推荐增强:扩展现有 RecommendServiceImplTest
  • 全链路:编译 + 单测 + 手动接口(需 DDL 执行 + 数据)

10. 跨团队对接事项

数仓侧需对齐:cardId 稳定性、batchNo 生成规则、pops 三机构口径、每日推送的 recordDate/todayPrice 字段、鉴权方式。