From 81630339e406380b1f7d57492fe97cbf436f6830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A5=8E=E5=85=B4?= <2220574228@qq.com> Date: Mon, 14 Sep 2026 16:13:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=B9=E9=87=8F=E6=94=B6=E6=AC=BE=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E4=BA=8B=E5=8A=A1;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReceiptManageApplicationService.java | 26 +++++++ .../facadeApi/ReceiptManageApi.java | 72 ++++++++----------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/mhd_bms/src/main/java/com/mhd/bms/application/server/receiptManage/ReceiptManageApplicationService.java b/mhd_bms/src/main/java/com/mhd/bms/application/server/receiptManage/ReceiptManageApplicationService.java index b098b3443..d970e985f 100644 --- a/mhd_bms/src/main/java/com/mhd/bms/application/server/receiptManage/ReceiptManageApplicationService.java +++ b/mhd_bms/src/main/java/com/mhd/bms/application/server/receiptManage/ReceiptManageApplicationService.java @@ -147,6 +147,32 @@ public class ReceiptManageApplicationService { } + /** + * 排队批量收款 + * 接收集合按顺序逐笔处理,任意一笔收款失败则整体回滚 + */ + @Transactional(rollbackFor = Exception.class) + public Boolean queueBatchCollection(List 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; + } + + /** * 撤销收款 */ diff --git a/mhd_bms/src/main/java/com/mhd/bms/interfaces/facadeApi/ReceiptManageApi.java b/mhd_bms/src/main/java/com/mhd/bms/interfaces/facadeApi/ReceiptManageApi.java index 8f6b3b896..9b4fe8635 100644 --- a/mhd_bms/src/main/java/com/mhd/bms/interfaces/facadeApi/ReceiptManageApi.java +++ b/mhd_bms/src/main/java/com/mhd/bms/interfaces/facadeApi/ReceiptManageApi.java @@ -1,11 +1,10 @@ package com.mhd.bms.interfaces.facadeApi; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -124,8 +123,9 @@ public class ReceiptManageApi extends BaseController{ /** * 排队批量收款 - * 接收多个批量收款请求参数集合,按顺序排队依次处理; - * 同一账单分多次收款时前一笔处理完成后才处理下一笔,避免redis锁冲突导致收款失败 + * 接收多个批量收款请求参数集合,整体加锁后按顺序排队依次处理; + * 同一账单分多次收款时前一笔处理完成后才处理下一笔,避免redis锁冲突导致收款失败; + * 整个集合在同一事务内处理,任意一笔收款失败则全部回滚 */ @ApiOperation("排队批量收款") @RepeatSubmit @@ -135,58 +135,42 @@ public class ReceiptManageApi extends BaseController{ if(StringUtils.isEmpty(receiptManageDTOList)){ throw new ServiceException("收款账单不能为空"); } - List> collectionResultList = new ArrayList<>(); + //汇总所有收款请求涉及的账单id并排序,整体加锁,保证事务期间锁不释放且加锁顺序一致避免死锁 + Set lockKeySet = new TreeSet<>(); 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 handleQueueBatchCollection(ReceiptManageDTO receiptManageDTO) { - Map result = new LinkedHashMap<>(); - result.put("billManageIds", receiptManageDTO.getBillManageIds()); - RedissonMultiLock redissonMultiLock = null; - boolean payInfoDetailLock = false; - try { if(!StringUtils.isNotEmpty(receiptManageDTO.getBillManageIds())){ throw new ServiceException("收款账单不能为空"); } - RedisLockTypeEnum redisLockTypeEnum = RedisLockTypeEnum.BMS; - List locks = new ArrayList<>(); - Set billingSet = Stream.of(receiptManageDTO.getBillManageIds().split(",")).collect(Collectors.toSet()); - billingSet.forEach(x -> { - //获取唯一key值 - String key = redisLockTypeEnum.getUniqueKey(UniqueKeyUtil.getBillingKey(x.toString())); - locks.add(redisLock.getRLock(key)); - }); - redissonMultiLock = new RedissonMultiLock(locks.toArray(new RLock[billingSet.size()])); + lockKeySet.addAll(Stream.of(receiptManageDTO.getBillManageIds().split(",")).filter(StringUtils::isNotEmpty).map(String::trim).collect(Collectors.toSet())); + } + RedisLockTypeEnum redisLockTypeEnum = RedisLockTypeEnum.BMS; + List locks = new ArrayList<>(); + lockKeySet.forEach(x -> { + //获取唯一key值 + String key = redisLockTypeEnum.getUniqueKey(UniqueKeyUtil.getBillingKey(x)); + locks.add(redisLock.getRLock(key)); + }); + RedissonMultiLock redissonMultiLock = new RedissonMultiLock(locks.toArray(new RLock[lockKeySet.size()])); + boolean payInfoDetailLock = false; + try { //尝试获取锁 等待指定时间内排队获取 持有锁10分钟 payInfoDetailLock = redissonMultiLock.tryLock(QUEUE_COLLECTION_LOCK_WAIT_SECONDS, 10, TimeUnit.MINUTES); if (!payInfoDetailLock) { redissonMultiLock = null; - result.put("success", false); - result.put("message", "账单正在处理,请稍后再试"); - return result; + return AjaxResult.error("账单正在处理,请稍后再试"); } log.info("排队批量收款-生成账单加锁成功"); //转换实体 - ReceiptManageDO receiptManageDO = new ReceiptManageDO(); - BeanUtils.copyProperties(receiptManageDTO,receiptManageDO); - Boolean flag = receiptManageApplicationService.batchCollection(receiptManageDO); - result.put("success", Boolean.TRUE.equals(flag)); - result.put("message", Boolean.TRUE.equals(flag) ? "收款成功" : "收款失败"); - return result; + List receiptManageDOList = new ArrayList<>(); + for (ReceiptManageDTO receiptManageDTO : receiptManageDTOList) { + ReceiptManageDO receiptManageDO = new ReceiptManageDO(); + BeanUtils.copyProperties(receiptManageDTO,receiptManageDO); + receiptManageDOList.add(receiptManageDO); + } + //事务内排队逐笔收款,任意一笔失败全部回滚 + return toAjax(receiptManageApplicationService.queueBatchCollection(receiptManageDOList)); }catch (Exception e){ - result.put("success", false); - result.put("message", "收款失败" + e.getMessage()); - return result; + return AjaxResult.error("收款失败:" + e.getMessage()); }finally { if (redissonMultiLock != null && payInfoDetailLock) { redissonMultiLock.unlock();