6月份的时候开发了一个类似消费攒积分的活动。用户在当月消费达到门槛,在下月可以领取相应门槛的礼物。
当时采取的方案有两个关键点。1. 用户只有打开小程序时才会领取活动。假如用户一直不打开小程序而一直使用现金支付,就无法参与活动了。 2. 监听订单的变更消息统计总金额存在数据库中。这种方式用户查询活动参与信息时很快,但是如果统计出现问题就无法处理了。
这个项目在没有上线前就交接给其他团队负责了。最近线上出现了一个问题,一个大姐反馈上个月消费满足了门槛(200元)但是给她计算的是一百九十多块。原因是新团队接受项目后,想要将用户领取任务的触发时间从打开小程序变成第一次下单。我觉得这个改动是对的,除了能够解决一直现金支付无法参与活动的问题,还能大大减少数据库中维护的任务数。只有真正下单过的用户才会创建任务。
新团队监听了订单的mq消息,接受到订单消息时判断用户是否有任务,如果用户此时没有任务,就为这个用户创建一个任务。问题出在了创建任务时复用的历史的创建任务方法(我写的那一版),历史版本在用户首次进入小程序时才会创建任务,所以用户可能在进入小程序前有过消费记录,代码里通过时间查询当月用户所有的订单信息然后累加在一起初始化用户任务的初始金额。当监听订单mq消息后创建任务时,立即调用时间范围查询,因为主从延迟所以反查不到订单的消息导致没有统计到第一笔订单。
因为使用的是根据时间范围查找所有订单的接口,这个接口查询不到订单信息在业务上是正常的。如果指定订单id查询没有查询到可以直接报错通过mq反复重试。
我检查了一下我任务创建后监听订单mq消息后计算金额的实现,发现我在监听下单的mq时如果查询不到订单信息是会直接抛出异常,但是退款的订单信息没有。所以实现上还是有问题的。
吸取到的经验
- 指定id查询信息,如果技术上这个id应该查到(这个id本身就是对方服务提供的),那么查询不到不应该忽略而应该抛出异常
- 因为主从延迟的存在,即使是调用其他服务的接口也可能有时查询不到数据,需要有重试机制
补充: 新团队还在做实时计算用户的累积金额的工作,这是最准确的,即使有主从延迟,用户重试也能统计到正确数据。但是我们的场景,查询到订单数据后需要解析所有的商品,并且过滤掉这些商品中的特殊商品,过滤条件有商品的属性,所以需要调用商品的接口查询所有商品的信息,同时还需要统计退款金额。这样在一次请求里操作耗时会很长。目前的实现只有一个库查询(直接获取金额)。
我觉得实时计算的开发可以写一个定时任务,巡检程序再加一个修复接口。每晚在店停止下单后巡检任务计算,如果发生不一致可以修复。