批量收款接口事务;
This commit is contained in:
+26
@@ -147,6 +147,32 @@ public class ReceiptManageApplicationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排队批量收款
|
||||||
|
* 接收集合按顺序逐笔处理,任意一笔收款失败则整体回滚
|
||||||
|
*/
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Boolean queueBatchCollection(List<ReceiptManageDO> receiptManageDOList) {
|
||||||
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
|
if (ObjectUtil.isNull(loginUser)) {
|
||||||
|
throw new DigitalLogisticsException(UserError.TIMEOUT);
|
||||||
|
}
|
||||||
|
int index = 1;
|
||||||
|
for (ReceiptManageDO receiptManageDO : receiptManageDOList) {
|
||||||
|
try {
|
||||||
|
Boolean flag = batchCollection(receiptManageDO);
|
||||||
|
if (!Boolean.TRUE.equals(flag)) {
|
||||||
|
throw new ServiceException("收款数据处理失败");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException("第" + index + "笔收款失败:" + e.getMessage() + ",本次收款已全部回滚");
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 撤销收款
|
* 撤销收款
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package com.mhd.bms.interfaces.facadeApi;
|
package com.mhd.bms.interfaces.facadeApi;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -124,8 +123,9 @@ public class ReceiptManageApi extends BaseController{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 排队批量收款
|
* 排队批量收款
|
||||||
* 接收多个批量收款请求参数集合,按顺序排队依次处理;
|
* 接收多个批量收款请求参数集合,整体加锁后按顺序排队依次处理;
|
||||||
* 同一账单分多次收款时前一笔处理完成后才处理下一笔,避免redis锁冲突导致收款失败
|
* 同一账单分多次收款时前一笔处理完成后才处理下一笔,避免redis锁冲突导致收款失败;
|
||||||
|
* 整个集合在同一事务内处理,任意一笔收款失败则全部回滚
|
||||||
*/
|
*/
|
||||||
@ApiOperation("排队批量收款")
|
@ApiOperation("排队批量收款")
|
||||||
@RepeatSubmit
|
@RepeatSubmit
|
||||||
@@ -135,58 +135,42 @@ public class ReceiptManageApi extends BaseController{
|
|||||||
if(StringUtils.isEmpty(receiptManageDTOList)){
|
if(StringUtils.isEmpty(receiptManageDTOList)){
|
||||||
throw new ServiceException("收款账单不能为空");
|
throw new ServiceException("收款账单不能为空");
|
||||||
}
|
}
|
||||||
List<Map<String, Object>> collectionResultList = new ArrayList<>();
|
//汇总所有收款请求涉及的账单id并排序,整体加锁,保证事务期间锁不释放且加锁顺序一致避免死锁
|
||||||
|
Set<String> lockKeySet = new TreeSet<>();
|
||||||
for (ReceiptManageDTO receiptManageDTO : receiptManageDTOList) {
|
for (ReceiptManageDTO receiptManageDTO : receiptManageDTOList) {
|
||||||
collectionResultList.add(handleQueueBatchCollection(receiptManageDTO));
|
|
||||||
}
|
|
||||||
boolean allSuccess = collectionResultList.stream().allMatch(item -> Boolean.TRUE.equals(item.get("success")));
|
|
||||||
if (allSuccess) {
|
|
||||||
return AjaxResult.success("收款成功", collectionResultList);
|
|
||||||
}
|
|
||||||
return AjaxResult.error("部分收款失败,请查看收款结果明细", collectionResultList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排队批量收款-处理单个收款请求(排队获取账单锁,每笔收款独立处理,单笔失败不影响后续请求)
|
|
||||||
*/
|
|
||||||
private Map<String, Object> handleQueueBatchCollection(ReceiptManageDTO receiptManageDTO) {
|
|
||||||
Map<String, Object> result = new LinkedHashMap<>();
|
|
||||||
result.put("billManageIds", receiptManageDTO.getBillManageIds());
|
|
||||||
RedissonMultiLock redissonMultiLock = null;
|
|
||||||
boolean payInfoDetailLock = false;
|
|
||||||
try {
|
|
||||||
if(!StringUtils.isNotEmpty(receiptManageDTO.getBillManageIds())){
|
if(!StringUtils.isNotEmpty(receiptManageDTO.getBillManageIds())){
|
||||||
throw new ServiceException("收款账单不能为空");
|
throw new ServiceException("收款账单不能为空");
|
||||||
}
|
}
|
||||||
RedisLockTypeEnum redisLockTypeEnum = RedisLockTypeEnum.BMS;
|
lockKeySet.addAll(Stream.of(receiptManageDTO.getBillManageIds().split(",")).filter(StringUtils::isNotEmpty).map(String::trim).collect(Collectors.toSet()));
|
||||||
List<RLock> locks = new ArrayList<>();
|
}
|
||||||
Set<String> billingSet = Stream.of(receiptManageDTO.getBillManageIds().split(",")).collect(Collectors.toSet());
|
RedisLockTypeEnum redisLockTypeEnum = RedisLockTypeEnum.BMS;
|
||||||
billingSet.forEach(x -> {
|
List<RLock> locks = new ArrayList<>();
|
||||||
//获取唯一key值
|
lockKeySet.forEach(x -> {
|
||||||
String key = redisLockTypeEnum.getUniqueKey(UniqueKeyUtil.getBillingKey(x.toString()));
|
//获取唯一key值
|
||||||
locks.add(redisLock.getRLock(key));
|
String key = redisLockTypeEnum.getUniqueKey(UniqueKeyUtil.getBillingKey(x));
|
||||||
});
|
locks.add(redisLock.getRLock(key));
|
||||||
redissonMultiLock = new RedissonMultiLock(locks.toArray(new RLock[billingSet.size()]));
|
});
|
||||||
|
RedissonMultiLock redissonMultiLock = new RedissonMultiLock(locks.toArray(new RLock[lockKeySet.size()]));
|
||||||
|
boolean payInfoDetailLock = false;
|
||||||
|
try {
|
||||||
//尝试获取锁 等待指定时间内排队获取 持有锁10分钟
|
//尝试获取锁 等待指定时间内排队获取 持有锁10分钟
|
||||||
payInfoDetailLock = redissonMultiLock.tryLock(QUEUE_COLLECTION_LOCK_WAIT_SECONDS, 10, TimeUnit.MINUTES);
|
payInfoDetailLock = redissonMultiLock.tryLock(QUEUE_COLLECTION_LOCK_WAIT_SECONDS, 10, TimeUnit.MINUTES);
|
||||||
if (!payInfoDetailLock) {
|
if (!payInfoDetailLock) {
|
||||||
redissonMultiLock = null;
|
redissonMultiLock = null;
|
||||||
result.put("success", false);
|
return AjaxResult.error("账单正在处理,请稍后再试");
|
||||||
result.put("message", "账单正在处理,请稍后再试");
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
log.info("排队批量收款-生成账单加锁成功");
|
log.info("排队批量收款-生成账单加锁成功");
|
||||||
//转换实体
|
//转换实体
|
||||||
ReceiptManageDO receiptManageDO = new ReceiptManageDO();
|
List<ReceiptManageDO> receiptManageDOList = new ArrayList<>();
|
||||||
BeanUtils.copyProperties(receiptManageDTO,receiptManageDO);
|
for (ReceiptManageDTO receiptManageDTO : receiptManageDTOList) {
|
||||||
Boolean flag = receiptManageApplicationService.batchCollection(receiptManageDO);
|
ReceiptManageDO receiptManageDO = new ReceiptManageDO();
|
||||||
result.put("success", Boolean.TRUE.equals(flag));
|
BeanUtils.copyProperties(receiptManageDTO,receiptManageDO);
|
||||||
result.put("message", Boolean.TRUE.equals(flag) ? "收款成功" : "收款失败");
|
receiptManageDOList.add(receiptManageDO);
|
||||||
return result;
|
}
|
||||||
|
//事务内排队逐笔收款,任意一笔失败全部回滚
|
||||||
|
return toAjax(receiptManageApplicationService.queueBatchCollection(receiptManageDOList));
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
result.put("success", false);
|
return AjaxResult.error("收款失败:" + e.getMessage());
|
||||||
result.put("message", "收款失败" + e.getMessage());
|
|
||||||
return result;
|
|
||||||
}finally {
|
}finally {
|
||||||
if (redissonMultiLock != null && payInfoDetailLock) {
|
if (redissonMultiLock != null && payInfoDetailLock) {
|
||||||
redissonMultiLock.unlock();
|
redissonMultiLock.unlock();
|
||||||
|
|||||||
Reference in New Issue
Block a user