描述
服务日志发现用户购买课程时提示”订单已支付,无需重复支付“类似提示,且 trade-center 发起微信预支付也报错”订单已支付“。
查数据时发现部分订单支付状态为 PAID 但课程订单表显示订单还是未支付的
查具体日志发现最开始的支付通知,接口处理时已经提示更新订单状态 NEW -> PAYING 乐观锁更新失败
思路
- 其他进程运行但没有关联到日志文件,且执行过程MQ实际发失败了(发的操作成功,但实际回调结果是失败的)
结论:确定当前运行的服务进程都是正常的,且MQ发送失败会有告警,故排除
- 乐观锁更新失败前有线程成功执行 PAYING -> PAID
同时异步线程池内发生异常,日志来不及打
结论:状态不应该被变更为 PAID,且在前面return 之前就应该有打印WxPay日志 - 其他地方更新,且没有相关日志(NEW -> PAID/NEW -> CANCEL(其他) -> PAID)
结论:异步线程执行的方法是一个事务方法,订单状态成功更新一定会发送相关日志,会有MQ的发送及消费日志
- 被其他 orderNo 更新了状态
例如,数据库字段类型隐式转换,导致仅匹配 7 - 8 位订单号去批量更新数据
通过比对在相同时间段内同样环境其他相同入参性质的 SQL 语句(findByOrderNoForUpdate),发现不存在该问题
复制问题数据及其周边数据记录,在开发环境创建 payment_order_test 表,简化代码逻辑,配置打印 SQL 操作日志,虚构支付通知参数,调用接口,发现同一个订单第三次更新时做了批量数据更新,定位到 SQL 问题
原因
- 更新订单状态的语句
eq
错写为ge
导致批量更新
解决
- 去掉重复的订单更新语句,修改错误更新操作
结论
- 写代码时需要细心检查逻辑,需要熟悉上下文逻辑
- 临时或紧急处理问题时注意保存现场环境(数据、日志等)
建议
- 熟悉调试工具如 arthas,在不重新部署的情况下打开某些日志的开关,此处指打开 mybatisPlus 的 SQL 执行日志开关