最近在做一个订单审核类需求,类似这样
创建订单, 如果库里有那么直接对用户进行支付。
1.拆创建订单
2.进行订单分润预留
3.进行订单审核
我的整体开发流程大概如上面三个,其中第三步是使用restTempLate远程调用后台管理系统接口。
在调远程接口的时候我对传过来的订单进行校验,如果不存在那么返回给app端一个审核的结果。
。整个过程如下:
@Override
@Transactional(rollbackFor = Exception.class)
public RestResult add(MagicAddParam magicAddParam, Integer merchantId) {
String redisKey = key + merchantId;
if (redisUtil.incr(redisKey, 1) == 1) {
Optional<MerchantAccount> merchantAccount = merchantAccountDao.selectById(merchantId);
redisUtil.expire(redisKey, 5);
if (magicAddParam.getId() != null) {
//更新
Optional<MagicBlockStarOrder> magicBlockStarOrderOptional = magicBlockStarOrderDao.selectById(magicAddParam.getId());
if (magicBlockStarOrderOptional.isPresent()) {
MagicBlockStarOrder magicBlockStarOrder1 = magicBlockStarOrderOptional.get();
//增加更新校验
MagicBlockStarOrder check = magicBlockStarOrderDao.getMagicInfo(magicAddParam.getMagicId(), magicBlockStarOrder1.getId());
if (check != null) {
throw new RestException("该魔筷ID已经被使用");
}
MagicBlockStarOrder magicBlockStarOrder = magicBlockStarOrderOptional.get();
magicBlockStarOrder.setMotify(merchantAccount.orElseGet(MerchantAccount::new).getName());
magicBlockStarOrder.setGmtMotify(LocalDateTime.now());
magicBlockStarOrder.setShopPhone(magicAddParam.getPhone());
magicBlockStarOrder.setMagicId(magicAddParam.getMagicId());
magicBlockStarOrder.setMagicNickName(magicAddParam.getMagicNickName());
magicBlockStarOrder.setStatus(EnumMagicBlockStar.MagicBlockStar.INIT.getCode());
magicBlockStarOrderDao.updateById(magicBlockStarOrder);
this.auditOrder(magicBlockStarOrder.getMagicId());
} else {
throw new RestException("修改的订单不存在");
}
} else {
//添加 校验魔筷id是否存在
MagicBlockStarOrder magicIdCheck = magicBlockStarOrderDao.getMagicBlockOrderByMagicId(magicAddParam.getMagicId());
if (magicIdCheck != null) {
throw new RestException("该魔筷id已经被占用");
}
MagicBlockStarOrder magicBlockStarOrder = new MagicBlockStarOrder();
magicBlockStarOrder.setMagicId(magicAddParam.getMagicId());
magicBlockStarOrder.setMagicNickName(magicAddParam.getMagicNickName());
magicBlockStarOrder.setMerchantId(merchantId);
magicBlockStarOrder.setShopPhone(magicAddParam.getPhone());
magicBlockStarOrder.setStatus(EnumMagicBlockStar.MagicBlockStar.INIT.getCode());
magicBlockStarOrder.setOrderNo(idGeneratorUtil.generateOrderNo("MAGICBLOCKSTAR", EnumsMerchantProject.EnumProjectTag.MAGIC_BLOCK_STAR.getId()));
magicBlockStarOrder.setGmtCreate(LocalDateTime.now());
magicBlockStarOrder.setGmtMotify(LocalDateTime.now());
magicBlockStarOrder.setIsPay(EnumMagicBlockStar.PayStatus.OFF.getCode());
magicBlockStarOrder.setMotify(merchantAccount.orElseGet(MerchantAccount::new).getName());
magicBlockStarOrder.setCreate(merchantAccount.orElseGet(MerchantAccount::new).getName());
Integer orderId = magicBlockStarOrderDao.insert(magicBlockStarOrder);
//分润预留
merchantLv3IncomesService.saveLv3IncomesByAuditOrder(orderId,
EnumsMerchantProject.EnumProjectTag.MAGIC_BLOCK_STAR.getId(),
magicBlockStarOrder.getMerchantId(), EnumsMerchant.EnumsLv3.EnumIncomesType.DOUYIN_KUAISHOU, EnumAppTag.LEKE);
//进行订单的审核 如果魔筷库里有进行结算 如果结算成功则状态改为已经结算。
this.auditOrder(magicBlockStarOrder.getMagicId());
}
} else {
throw new RestException("请不要频繁提交");
}
@Async
public void auditOrder( String magicId) {
Map map = new HashMap();
map.put("Authorization", "Bearer " + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCURmFEA0HVA8Oh/dlN3+38jD1+D98gD24TgC6i");
RestResult restResult = restTemplateUtil.postForJson(managerBaseUrl + "/magic/block/audit/magic/order", RestResult.class, new MagicOriginConvertInfo(magicId, null), map);
log.warn("当前异步请求魔筷星选订单审审核,调用后台结果是:{}", restResult);
}
然后我发现库里一直有订单数据,但是无论我怎么提交订单都是处于待审核状态而并没有进行订单的结算。对这个问题,我很奇怪,看了半天也没看出来,最后断点调试的时候发现订单创建已经走完了,远程调用的时候库里并没有这个订单数据。
突然想到我把他们写到同一个事务里面,虽然前面的添加操作先完成,但是我的事务注解是家在这个方法里的
然后我对创建订单和创建分润预留做一个事务处理。远程调用不能写在这个事务里面。
最后代码调整如下:
事务在同一个类中不会生效,如果对事务进行抽取的话,所以我新建了一个类,在这个类里对创建订单,分润预留进行操作,而远程调用却在这个事务完成以后,这样我远程调用的时候就不会出现查询插入的订单,订单不存在的情况。阶段1已经插入之后才会进行下面的远程操作。这样就解决了这个事务问题。特此记录。哎,写bug真简单,修复bug却需要半天。因为一个注解导致耽误了很长时间,好在解决了,也学到了东西。
还有一点需要记录的,我们的订单每天晚上会进行批量审核。我写了一个定时器。由于线上是两台服务器。两台服务器同事触发,项目上线后,第二天发现库里很多重复的数据。然后慌得一批。经过大佬指点,发现是定时器的问题。最后在审核的接口使用redis锁进行业务锁定。使其只有一个机器在跑。
代码如下:
先到的机器先执行,后到的机器直接抛异常,保证只有一个机器在跑。
这样就解决了这个重复定时审核的问题。写代码需谨慎,一不留神钱就发错了。今天碰到的问题也是自工作以来难忘的一天。细心,谨慎,严谨,以后一定注意低级bug杜绝不必要的时间浪费。加油。