From c463179e838b4fad0f5566c9ed34cb2856fbfde6 Mon Sep 17 00:00:00 2001 From: irving Date: Sun, 2 Nov 2025 16:03:59 -0500 Subject: [PATCH] =?UTF-8?q?fix(order):=20=E5=89=8D=E7=BD=AE=E4=BD=99?= =?UTF-8?q?=E9=A2=9D=E6=89=A3=E5=87=8F=E5=B9=B6=E7=BB=9F=E4=B8=80=E9=87=91?= =?UTF-8?q?=E9=A2=9D=E7=B2=BE=E5=BA=A6=E5=A4=84=E7=90=86=EF=BC=8C=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E4=BD=99=E9=A2=9D=E6=A0=A1=E9=AA=8C=E4=B8=8E=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抽取 validateSufficientBalance,统一使用 normalizeMoney 校验与比较,提升健壮性\n- AbstractOrderPlacementStrategy:在创建订单前根据 shouldDeduct 进行余额校验与扣减,使用上下文 orderId 记录流水,避免不一致\n- deductCustomerBalance:使用 amountToDeduct 变量并先归一化后运算,修正可能的精度问题\n- 调整/补充测试用例:扣减失败不插入订单、不保存用户信息;更新 selectById 调用次数校验 --- .../impl/AbstractOrderPlacementStrategy.java | 14 ++++++++----- .../impl/OrderLifecycleServiceImpl.java | 20 ++++++++++++++++--- .../impl/OrderLifecycleServiceImplTest.java | 6 +++++- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/AbstractOrderPlacementStrategy.java b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/AbstractOrderPlacementStrategy.java index 8143397..ebe2e53 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/AbstractOrderPlacementStrategy.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/AbstractOrderPlacementStrategy.java @@ -7,6 +7,7 @@ import com.starry.admin.modules.order.module.dto.OrderPlacementCommand; import com.starry.admin.modules.order.module.dto.OrderPlacementResult; import com.starry.admin.modules.order.module.dto.PaymentInfo; import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity; +import java.math.BigDecimal; abstract class AbstractOrderPlacementStrategy implements OrderPlacementStrategy { @@ -26,16 +27,19 @@ abstract class AbstractOrderPlacementStrategy implements OrderPlacementStrategy throw new CustomException("支付信息不能为空"); } - PlayOrderInfoEntity order = service.createOrderRecord(context); - - if (command.isDeductBalance() && service.shouldDeductBalance(context)) { + BigDecimal netAmount = service.normalizeMoney(paymentInfo.getFinalAmount()); + boolean shouldDeduct = command.isDeductBalance() && service.shouldDeductBalance(context); + if (shouldDeduct) { + service.validateSufficientBalance(context.getPurchaserBy(), netAmount); service.deductCustomerBalance( context.getPurchaserBy(), - service.normalizeMoney(paymentInfo.getFinalAmount()), + netAmount, command.getBalanceOperationAction(), - order.getId()); + context.getOrderId()); } + PlayOrderInfoEntity order = service.createOrderRecord(context); + OrderAmountBreakdown amountBreakdown = breakdown != null ? breakdown : service.fallbackBreakdown(paymentInfo); return OrderPlacementResult.of(order, amountBreakdown); diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java index 6847ad2..dc3c41f 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImpl.java @@ -162,6 +162,7 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService { validateCouponUsage(context); + OrderConstant.RewardType rewardType = context.getRewardType() != null ? context.getRewardType() : OrderConstant.RewardType.NOT_APPLICABLE; @@ -278,6 +279,18 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService { return OrderAmountBreakdown.of(grossAmount, discountAmount, netAmount); } + void validateSufficientBalance(String customerId, BigDecimal requiredAmount) { + PlayCustomUserInfoEntity customer = customUserInfoService.selectById(customerId); + if (customer == null) { + throw new CustomException("顾客不存在"); + } + BigDecimal before = normalizeMoney(customer.getAccountBalance()); + BigDecimal required = normalizeMoney(requiredAmount); + if (required.compareTo(before) > 0) { + throw new ServiceException("余额不足", 998); + } + } + void deductCustomerBalance( String customerId, BigDecimal netAmount, @@ -288,10 +301,11 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService { throw new CustomException("顾客不存在"); } BigDecimal before = normalizeMoney(customer.getAccountBalance()); - if (netAmount.compareTo(before) > 0) { + BigDecimal amountToDeduct = normalizeMoney(netAmount); + if (amountToDeduct.compareTo(before) > 0) { throw new ServiceException("余额不足", 998); } - BigDecimal after = normalizeMoney(before.subtract(netAmount)); + BigDecimal after = normalizeMoney(before.subtract(amountToDeduct)); String action = StrUtil.isNotBlank(operationAction) ? operationAction : "下单"; customUserInfoService.updateAccountBalanceById( customerId, @@ -299,7 +313,7 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService { after, BalanceOperationType.CONSUME.getCode(), action, - netAmount, + amountToDeduct, BigDecimal.ZERO, orderId); } diff --git a/play-admin/src/test/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImplTest.java b/play-admin/src/test/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImplTest.java index edce129..f59da95 100644 --- a/play-admin/src/test/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImplTest.java +++ b/play-admin/src/test/java/com/starry/admin/modules/order/service/impl/OrderLifecycleServiceImplTest.java @@ -733,6 +733,8 @@ private PlayOrderLogInfoMapper orderLogInfoMapper; assertThrows(ServiceException.class, () -> lifecycleService.placeOrder(command)); verify(customUserInfoService, never()).updateAccountBalanceById(anyString(), any(), any(), anyString(), anyString(), any(), any(), anyString()); + verify(orderInfoMapper, never()).insert(any()); + verify(customUserInfoService, never()).saveOrderInfo(any()); } @Test @@ -863,6 +865,8 @@ private PlayOrderLogInfoMapper orderLogInfoMapper; null); assertThrows(ServiceException.class, () -> lifecycleService.placeOrder(command)); + verify(orderInfoMapper, never()).insert(any()); + verify(customUserInfoService, never()).saveOrderInfo(any()); } @Test @@ -962,7 +966,7 @@ private PlayOrderLogInfoMapper orderLogInfoMapper; null, null)); - verify(customUserInfoService).selectById(context.getPurchaserBy()); + verify(customUserInfoService, times(2)).selectById(context.getPurchaserBy()); verify(customUserInfoService).updateAccountBalanceById( eq(customer.getId()), eq(customer.getAccountBalance()),