首先无力吐槽到了新公司的遭遇,来了俩月改了俩月bug,真是生活不息bug不止~
遇到的问题:
1.在做组合支付成功后,微信回调成功会触发给用户发送两条付款消息;结果每次都会余额支付一次,微信付款会连发两条一样的消息,让用户和我们都很慌好不好~
排查问题:
1.开始认为是不是回调失败重试机制引起的?但是看了下做了幂等了,不会出现重复消费和发送;
2.是不是微信抽了,然后观察是有规律的,只要组合支付涉及余额加微信,就重复发消息;
解决问题:
1.首先直接治标不治本的方式,排查到了发送人名单,在第二次发送微信支付时存在重复openid,所以做了去重,简单粗暴
criteria.setToUser(criteria.getToUser() == null ? null : criteria.getToUser().parallelStream().filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList()));
2.但是为啥会出现问题呢?还原前同事的神代码:
//组合支付则循环两遍
for (InsPayLogEntity entity : list) {
//幂等性验证略...
//构建发送的消息
PayMsgCriteria payMsgCriteria = this.builtPayMsg(entity.getStoreCode(),
entity.getPayType(),
entity.getPayTarget(),
entity.getChangeAmount(),
entity.getReceiptTime(), entity.getPaySn());
// 发送支付通知
pushWeChatMpMsgService.asyncSendPayMsg(payMsgCriteria);
}
//构建信息方法
private PayMsgCriteria builtPayMsg(String storeCode, Integer payType, Integer payTarget, BigDecimal changeAmount, Date receiptTime, String paySn) {
PayMsgCriteria criteria = new PayMsgCriteria();
// 构建信息接收人
//问题就在这里
List<String> result = this.getSalesOpenIdList(storeCode);
List<String> managerOpenIdList = this.getManagerOpenIdList();
result.addAll(managerOpenIdList);
criteria.setToUser(result);
return criteria;
}
//获取门店用户openid
private List<String> getSalesOpenIdList(String storeCode) {
List<String> phoneList = userSalesService.selectPhoneList(storeCode);
if (CollectionUtils.isEmpty(phoneList)) {
return new ArrayList<>();
}
List<String> list = doorUserService.selectMpOpenId(phoneList);
return list;
}
debug发现,第二次循环后,result的数量从2变为29了~
正常来说应该每次查询数据库都是2才对,因为入参没变sql查询没变,这个29是addAll后面的集合后得到的;
3.查看日志找到问题了:
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@63c163e1] from current transaction
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@63c163e1]
第一次循环查询了数据库,第二次则直接用的缓存,找到问题就好办了...
@原因就是我外面使用了@Transactional,开启事务后查询数据库会用同一个session,如果重复查询不变,会出现后面使用到一级缓存的情况~当然涉及到事务隔离级别,我的是Read committed,.所以出现更新列时缓存会失效,这里不多说~
4.修改代码:
List<String> result = this.getSalesOpenIdList(storeCode);
List<String> managerOpenIdList = this.getManagerOpenIdList();
List<String> add = new ArrayList<>();
add.addAll(result);
add.addAll(managerOpenIdList);
这样第二次循环后,虽然用缓存,但是不会出现叠加重复的情况了~
5.避免缓存问题
-
在mybatis配置文件中
localCacheScope=STATEMENT
。 -
在mapper配置文件中,给select设置
flushCache=true
。需要注意的是,这样会将local cache和cache都清空掉。