军利易购的积分模块是 2019 年上的,当时补过一个幂等键:order_id 加 change_type。七年里够用,因为积分只有下单加、退货扣两种,都挂在订单上。
今年加了三种新来源:每日签到、商品评价、客服手动补偿。问题就来了,签到没有订单号,补偿有时候是运营一笔单补两次不同原因。老键里那半截 order_id 填不进去,有人图省事塞了个固定值,结果不同业务的流水互相撞车。
新键三段
重构成 source 加 biz_id 加 change_type:
- source 标明来源,order、signin、review、manual 四种。
- biz_id 是各自业务里的唯一号,订单用订单号,签到用日期加用户,评价用评价 id,补偿用补偿单号。
- change_type 还是加或扣。
三段拼起来建唯一索引。这样同一笔签到重复点、同一张订单的加分和补偿,各归各的,不会互相挡也不会漏挡。
迁移那 2 小时
40 多万行历史流水要按下新键回填。没停机,写了个脚本分批跑,每批 2000 行,按 id 区间切,跑完一批校对一批的条数。
最麻烦的是那批塞了固定值的脏数据,一共 300 多行,得靠流水时间和备注人工判来源。跑了 2 小时,其中 40 分钟花在这 300 行上。
新键上线前,老键的索引留了 7 天没删,万一要回滚。7 天后确认没有旧格式写入,才 drop 掉。
顺手改的
- 补偿入口加了操作人和原因,必填。以前只有一个金额框,出问题查不到谁补的。
- 后台加了个「按 biz_id 查流水」的框,客服问「我这分怎么来的」,直接贴号进去看,不用翻三张表。
- 重复请求还是返回第一次的结果,不返回错误。这条从 2019 年到现在没变过。
7 月 10 号
切完当天盯了半天错误日志,唯一索引冲突 11 次,都是同一个用户连点签到,被正常挡下,没有一笔写重。
运营那边后来提了个需求,想看「哪种来源发的分最多」。这个不用改代码,按 source 分组一查就有。老结构里查不了,因为 source 那列压根不存在。

