From 83112b406a799e1c382e735ff3c90a835e731aef Mon Sep 17 00:00:00 2001 From: irving Date: Mon, 3 Nov 2025 10:02:03 -0500 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=A2=E5=8D=95=E4=B8=8B?= =?UTF-8?q?=E5=8D=95=E9=94=99=E8=AF=AF=E5=92=8C=E4=BD=99=E9=A2=9D=E6=89=A3?= =?UTF-8?q?=E6=AC=BE=E6=A0=A1=E9=AA=8C=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/PlayClerkUserInfoServiceImpl.java | 11 ++- .../impl/OrderLifecycleServiceImpl.java | 22 ++++++ .../support/ClerkRevenueCalculator.java | 61 ++++++++++----- .../module/enums/BalanceDetailsUserType.java | 18 +++++ .../IPlayBalanceDetailsInfoService.java | 11 +++ .../PlayBalanceDetailsInfoServiceImpl.java | 21 ++++- .../com/starry/admin/api/AbstractApiTest.java | 2 + .../admin/api/WxBlindBoxOrderApiTest.java | 76 +++++++++++++++++++ .../admin/api/WxCustomGiftOrderApiTest.java | 37 +++++++++ .../api/WxCustomOrderApiTestSupport.java | 51 +++++++++++++ .../admin/api/WxCustomRandomOrderApiTest.java | 46 +++++++++++ .../admin/api/WxCustomRewardOrderApiTest.java | 42 ++++++++++ .../api/WxCustomSpecifiedOrderApiTest.java | 49 ++++++++++++ .../impl/OrderLifecycleServiceImplTest.java | 32 +++++++- 14 files changed, 456 insertions(+), 23 deletions(-) create mode 100644 play-admin/src/main/java/com/starry/admin/modules/personnel/module/enums/BalanceDetailsUserType.java create mode 100644 play-admin/src/test/java/com/starry/admin/api/WxBlindBoxOrderApiTest.java diff --git a/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java index 6152dbe..ce4ac52 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/clerk/service/impl/PlayClerkUserInfoServiceImpl.java @@ -134,7 +134,16 @@ public class PlayClerkUserInfoServiceImpl extends ServiceImpl placementStrategies; @PostConstruct @@ -515,6 +520,10 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService { throw new CustomException("每个订单只能退款一次~"); } + if (isBalancePaidOrder(order) && !playBalanceDetailsInfoService.existsCustomerConsumeRecord(order.getPurchaserBy(), order.getId())) { + throw new CustomException("订单未发生余额扣款,无法退款"); + } + UpdateWrapper refundUpdate = new UpdateWrapper<>(); refundUpdate.eq("id", order.getId()) .eq("refund_type", OrderRefundFlag.NOT_REFUNDED.getCode()) @@ -581,6 +590,19 @@ public class OrderLifecycleServiceImpl implements IOrderLifecycleService { refundOperationType); } + private boolean isBalancePaidOrder(PlayOrderInfoEntity order) { + String sourceCode = order.getPaymentSource(); + if (StrUtil.isBlank(sourceCode)) { + return true; + } + try { + return PaymentSource.fromCode(sourceCode) == PaymentSource.BALANCE; + } catch (IllegalArgumentException ex) { + log.warn("Unknown payment source {}, defaulting to balance for refund guard", sourceCode); + return true; + } + } + private void validateOrderCreationRequest(OrderCreationContext context) { if (context == null) { throw new CustomException("订单创建请求不能为空"); diff --git a/play-admin/src/main/java/com/starry/admin/modules/order/service/support/ClerkRevenueCalculator.java b/play-admin/src/main/java/com/starry/admin/modules/order/service/support/ClerkRevenueCalculator.java index 997371a..2b17829 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/order/service/support/ClerkRevenueCalculator.java +++ b/play-admin/src/main/java/com/starry/admin/modules/order/service/support/ClerkRevenueCalculator.java @@ -37,6 +37,13 @@ public class ClerkRevenueCalculator { BigDecimal baseAmount = orderAmount == null ? BigDecimal.ZERO : orderAmount; ClerkEstimatedRevenueVo estimatedRevenueVo = new ClerkEstimatedRevenueVo(); + if (levelInfo == null) { + log.warn("店员{}缺少等级提成配置,预计收益按0处理", clerkId); + estimatedRevenueVo.setRevenueRatio(0); + estimatedRevenueVo.setRevenueAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)); + return estimatedRevenueVo; + } + boolean fallbackToOther = false; OrderConstant.PlaceType placeTypeEnum; try { @@ -49,13 +56,13 @@ public class ClerkRevenueCalculator { switch (placeTypeEnum) { case SPECIFIED: // 指定单 - fillRegularOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo); + fillRegularOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo); break; case RANDOM: // 随机单 - fillRandomOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo); + fillRandomOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo); break; case REWARD: // 打赏单 - fillRewardOrderRevenue(firstOrder, baseAmount, levelInfo, estimatedRevenueVo); + fillRewardOrderRevenue(clerkId, firstOrder, baseAmount, levelInfo, estimatedRevenueVo); break; case OTHER: default: @@ -71,42 +78,56 @@ public class ClerkRevenueCalculator { return estimatedRevenueVo; } - private void fillRegularOrderRevenue(String firstOrder, BigDecimal orderAmount, + private void fillRegularOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount, PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) { if ("1".equals(firstOrder)) { - vo.setRevenueRatio(levelInfo.getFirstRegularRatio()); - vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRegularRatio())); + int ratio = safeRatio(levelInfo.getFirstRegularRatio(), "firstRegularRatio", clerkId); + vo.setRevenueRatio(ratio); + vo.setRevenueAmount(scaleAmount(orderAmount, ratio)); } else { - vo.setRevenueRatio(levelInfo.getNotFirstRegularRatio()); - vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRegularRatio())); + int ratio = safeRatio(levelInfo.getNotFirstRegularRatio(), "notFirstRegularRatio", clerkId); + vo.setRevenueRatio(ratio); + vo.setRevenueAmount(scaleAmount(orderAmount, ratio)); } } - private void fillRandomOrderRevenue(String firstOrder, BigDecimal orderAmount, + private void fillRandomOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount, PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) { if ("1".equals(firstOrder)) { - vo.setRevenueRatio(levelInfo.getFirstRandomRadio()); - vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRandomRadio())); + int ratio = safeRatio(levelInfo.getFirstRandomRadio(), "firstRandomRatio", clerkId); + vo.setRevenueRatio(ratio); + vo.setRevenueAmount(scaleAmount(orderAmount, ratio)); } else { - vo.setRevenueRatio(levelInfo.getNotFirstRandomRadio()); - vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRandomRadio())); + int ratio = safeRatio(levelInfo.getNotFirstRandomRadio(), "notFirstRandomRatio", clerkId); + vo.setRevenueRatio(ratio); + vo.setRevenueAmount(scaleAmount(orderAmount, ratio)); } } - private void fillRewardOrderRevenue(String firstOrder, BigDecimal orderAmount, + private void fillRewardOrderRevenue(String clerkId, String firstOrder, BigDecimal orderAmount, PlayClerkLevelInfoEntity levelInfo, ClerkEstimatedRevenueVo vo) { if ("1".equals(firstOrder)) { - vo.setRevenueRatio(levelInfo.getFirstRewardRatio()); - vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getFirstRewardRatio())); + int ratio = safeRatio(levelInfo.getFirstRewardRatio(), "firstRewardRatio", clerkId); + vo.setRevenueRatio(ratio); + vo.setRevenueAmount(scaleAmount(orderAmount, ratio)); } else { - vo.setRevenueRatio(levelInfo.getNotFirstRewardRatio()); - vo.setRevenueAmount(scaleAmount(orderAmount, levelInfo.getNotFirstRewardRatio())); + int ratio = safeRatio(levelInfo.getNotFirstRewardRatio(), "notFirstRewardRatio", clerkId); + vo.setRevenueRatio(ratio); + vo.setRevenueAmount(scaleAmount(orderAmount, ratio)); } } - private BigDecimal scaleAmount(BigDecimal baseAmount, Integer ratio) { + private int safeRatio(Integer ratio, String ratioField, String clerkId) { + if (ratio == null) { + log.warn("店员{}的等级配置字段{}缺失,已按0%处理", clerkId, ratioField); + return 0; + } + return ratio; + } + + private BigDecimal scaleAmount(BigDecimal baseAmount, int ratio) { return baseAmount - .multiply(new BigDecimal(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP)) + .multiply(BigDecimal.valueOf(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP)) .setScale(2, RoundingMode.HALF_UP); } diff --git a/play-admin/src/main/java/com/starry/admin/modules/personnel/module/enums/BalanceDetailsUserType.java b/play-admin/src/main/java/com/starry/admin/modules/personnel/module/enums/BalanceDetailsUserType.java new file mode 100644 index 0000000..20e3926 --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/personnel/module/enums/BalanceDetailsUserType.java @@ -0,0 +1,18 @@ +package com.starry.admin.modules.personnel.module.enums; + +import lombok.Getter; + +/** + * 用户类型枚举(0:陪聊;1:顾客) + */ +@Getter +public enum BalanceDetailsUserType { + CLERK("0"), + CUSTOMER("1"); + + private final String code; + + BalanceDetailsUserType(String code) { + this.code = code; + } +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/personnel/service/IPlayBalanceDetailsInfoService.java b/play-admin/src/main/java/com/starry/admin/modules/personnel/service/IPlayBalanceDetailsInfoService.java index f0ed76f..82c62da 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/personnel/service/IPlayBalanceDetailsInfoService.java +++ b/play-admin/src/main/java/com/starry/admin/modules/personnel/service/IPlayBalanceDetailsInfoService.java @@ -69,6 +69,17 @@ public interface IPlayBalanceDetailsInfoService extends IService lifecycleService.refundOrder(context)); + verify(orderInfoMapper, never()).update(isNull(), any()); + verify(customUserInfoService, never()).updateAccountBalanceById(any(), any(), any(), any(), any(), any(), any(), any()); + verify(orderRefundInfoService, never()).add(anyString(), anyString(), anyString(), anyString(), anyString(), any(), anyString(), anyString(), anyString(), anyString(), anyString()); + } + private PlayOrderInfoEntity buildOrder(String orderId, String status) { PlayOrderInfoEntity entity = new PlayOrderInfoEntity(); entity.setId(orderId);