重构订单下单逻辑,引入策略模式和命令模式
- 为OrderTriggerSource枚举添加详细注释说明 - 将IOrderLifecycleService接口的initiateOrder方法重构为placeOrder - 新增OrderPlacementCommand、OrderPlacementResult、OrderAmountBreakdown等DTO - 实现订单下单策略模式,支持不同类型订单的差异化处理 - 优化金额计算逻辑,完善优惠券折扣计算 - 改进余额扣减逻辑,增强异常处理 - 更新相关控制器使用新的下单接口 - 完善单元测试覆盖,确保代码质量
This commit is contained in:
@@ -3,6 +3,8 @@ package com.starry.admin.modules.order.service.impl;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
@@ -10,6 +12,7 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.common.exception.ServiceException;
|
||||
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
|
||||
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
|
||||
@@ -33,6 +36,8 @@ import com.starry.admin.modules.order.module.constant.OrderConstant.YesNoFlag;
|
||||
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
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.OrderRefundContext;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
@@ -41,7 +46,10 @@ import com.starry.admin.modules.order.module.vo.ClerkEstimatedRevenueVo;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
||||
import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator;
|
||||
import com.starry.admin.modules.shop.module.constant.CouponUseState;
|
||||
import com.starry.admin.modules.shop.module.entity.PlayCouponDetailsEntity;
|
||||
import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponInfoService;
|
||||
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
||||
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||
@@ -49,7 +57,9 @@ import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -81,14 +91,65 @@ class OrderLifecycleServiceImplTest {
|
||||
@Mock
|
||||
private IPlayCouponDetailsService playCouponDetailsService;
|
||||
|
||||
@Mock
|
||||
private IPlayCouponInfoService playCouponInfoService;
|
||||
|
||||
@Mock
|
||||
private ClerkRevenueCalculator clerkRevenueCalculator;
|
||||
|
||||
@Mock
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
|
||||
@BeforeEach
|
||||
void initStrategies() {
|
||||
lifecycleService.initPlacementStrategies();
|
||||
}
|
||||
|
||||
@Test
|
||||
void initiateOrder_specifiedOrder_persistsAndUpdatesCoupon() {
|
||||
void placeOrder_throwsWhenCommandNull() {
|
||||
assertThrows(CustomException.class, () -> lifecycleService.placeOrder(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_throwsWhenContextNull() {
|
||||
OrderPlacementCommand command = mock(OrderPlacementCommand.class);
|
||||
when(command.getOrderContext()).thenReturn(null);
|
||||
|
||||
assertThrows(CustomException.class, () -> lifecycleService.placeOrder(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_throwsWhenPaymentInfoMissing() {
|
||||
OrderCreationContext context = OrderCreationContext.builder()
|
||||
.orderId("ctx-001")
|
||||
.orderNo("NO-001")
|
||||
.orderStatus(OrderStatus.PENDING)
|
||||
.orderType(OrderType.NORMAL)
|
||||
.placeType(PlaceType.OTHER)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(false)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity")
|
||||
.commodityType(CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.TEN)
|
||||
.serviceDuration("60")
|
||||
.commodityName("服务")
|
||||
.commodityNumber("1")
|
||||
.build())
|
||||
.paymentInfo(null)
|
||||
.purchaserBy("customer-1")
|
||||
.build();
|
||||
|
||||
OrderPlacementCommand command = OrderPlacementCommand.builder()
|
||||
.orderContext(context)
|
||||
.deductBalance(false)
|
||||
.build();
|
||||
|
||||
assertThrows(CustomException.class, () -> lifecycleService.placeOrder(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedOrder_persistsAndUpdatesCoupon() {
|
||||
OrderCreationContext request = OrderCreationContext.builder()
|
||||
.orderId("order-init-001")
|
||||
.orderNo("NO20241001")
|
||||
@@ -118,6 +179,18 @@ class OrderLifecycleServiceImplTest {
|
||||
.remark("备注")
|
||||
.build();
|
||||
|
||||
OrderPlacementCommand command = OrderPlacementCommand.builder()
|
||||
.orderContext(request)
|
||||
.deductBalance(false)
|
||||
.pricingInput(OrderPlacementCommand.PricingInput.builder()
|
||||
.unitPrice(BigDecimal.valueOf(199))
|
||||
.quantity(1)
|
||||
.couponIds(request.getPaymentInfo().getCouponIds())
|
||||
.commodityId("commodity-01")
|
||||
.placeType(PlaceType.SPECIFIED)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ClerkEstimatedRevenueVo revenueVo = new ClerkEstimatedRevenueVo();
|
||||
revenueVo.setRevenueAmount(BigDecimal.valueOf(89.5));
|
||||
revenueVo.setRevenueRatio(50);
|
||||
@@ -129,7 +202,23 @@ class OrderLifecycleServiceImplTest {
|
||||
doNothing().when(customUserInfoService).saveOrderInfo(any());
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), anyString());
|
||||
|
||||
PlayOrderInfoEntity created = lifecycleService.initiateOrder(request);
|
||||
PlayCouponDetailsEntity couponDetails = new PlayCouponDetailsEntity();
|
||||
couponDetails.setCouponId("coupon-master");
|
||||
when(playCouponDetailsService.getById("coupon-1")).thenReturn(couponDetails);
|
||||
|
||||
PlayCouponInfoEntity couponInfo = new PlayCouponInfoEntity();
|
||||
couponInfo.setDiscountType("0");
|
||||
couponInfo.setDiscountAmount(BigDecimal.valueOf(20));
|
||||
when(playCouponInfoService.selectPlayCouponInfoById("coupon-master")).thenReturn(couponInfo);
|
||||
when(playCouponInfoService.getCouponReasonForUnavailableUse(
|
||||
eq(couponInfo),
|
||||
eq(PlaceType.SPECIFIED.getCode()),
|
||||
eq("commodity-01"),
|
||||
eq(1),
|
||||
any())).thenReturn("");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command);
|
||||
PlayOrderInfoEntity created = result.getOrder();
|
||||
|
||||
verify(customUserInfoService).saveOrderInfo(created);
|
||||
verify(orderInfoMapper).insert(created);
|
||||
@@ -148,6 +237,740 @@ class OrderLifecycleServiceImplTest {
|
||||
assertEquals(PayMethod.WECHAT.getCode(), created.getPayMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_otherType_skipsPricingAndUsesExistingAmounts() {
|
||||
PaymentInfo payment = payment(BigDecimal.valueOf(50), BigDecimal.valueOf(50), BigDecimal.ZERO, Collections.emptyList());
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.OTHER,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment,
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(context, null, false, null, null));
|
||||
|
||||
assertMoneyEquals("50.00", result.getAmountBreakdown().getGrossAmount());
|
||||
assertMoneyEquals("0.00", result.getAmountBreakdown().getDiscountAmount());
|
||||
assertMoneyEquals("50.00", result.getAmountBreakdown().getNetAmount());
|
||||
verify(playCouponDetailsService, never()).updateCouponUseStateByIds(anyList(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_otherType_withCoupons_marksUsageButKeepsAmounts() {
|
||||
List<String> coupons = Collections.singletonList("coupon-1");
|
||||
PaymentInfo payment = payment(BigDecimal.valueOf(60), BigDecimal.valueOf(60), BigDecimal.ZERO, coupons);
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.OTHER,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment,
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
lifecycleService.placeOrder(command(context, null, false, null, null));
|
||||
|
||||
verify(playCouponDetailsService).updateCouponUseStateByIds(eq(coupons), eq(CouponUseState.USED.getCode()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedWithoutCoupons_calculatesTotallyFromPricing() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
"clerk-001");
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(20), 2, Collections.emptyList(), "commodity-x", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("40.00", context.getPaymentInfo().getOrderMoney());
|
||||
assertMoneyEquals("0.00", context.getPaymentInfo().getDiscountAmount());
|
||||
assertMoneyEquals("40.00", context.getPaymentInfo().getFinalAmount());
|
||||
assertMoneyEquals("40.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedWithFullReductionCouponReducesNetAmount() {
|
||||
List<String> coupons = Collections.singletonList("coupon-1");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-001");
|
||||
|
||||
stubDefaultPersistence();
|
||||
stubCoupon("coupon-1", "coupon-master", "0", BigDecimal.valueOf(15), "");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(30), 1, coupons, "commodity-123", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("15.00", result.getAmountBreakdown().getDiscountAmount());
|
||||
assertMoneyEquals("15.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedWithMultipleCouponsCapsNetAtZero() {
|
||||
List<String> coupons = Arrays.asList("coupon-1", "coupon-2");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-001");
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
PlayCouponDetailsEntity firstDetail = new PlayCouponDetailsEntity();
|
||||
firstDetail.setCouponId("master-1");
|
||||
PlayCouponDetailsEntity secondDetail = new PlayCouponDetailsEntity();
|
||||
secondDetail.setCouponId("master-2");
|
||||
when(playCouponDetailsService.getById("coupon-1")).thenReturn(firstDetail);
|
||||
when(playCouponDetailsService.getById("coupon-2")).thenReturn(secondDetail);
|
||||
|
||||
PlayCouponInfoEntity firstInfo = new PlayCouponInfoEntity();
|
||||
firstInfo.setDiscountType("0");
|
||||
firstInfo.setDiscountAmount(BigDecimal.valueOf(40));
|
||||
PlayCouponInfoEntity secondInfo = new PlayCouponInfoEntity();
|
||||
secondInfo.setDiscountType("0");
|
||||
secondInfo.setDiscountAmount(BigDecimal.valueOf(30));
|
||||
when(playCouponInfoService.selectPlayCouponInfoById("master-1")).thenReturn(firstInfo);
|
||||
when(playCouponInfoService.selectPlayCouponInfoById("master-2")).thenReturn(secondInfo);
|
||||
when(playCouponInfoService.getCouponReasonForUnavailableUse(any(), anyString(), anyString(), anyInt(), any()))
|
||||
.thenReturn("");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(60), 1, coupons, "commodity-max", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("0.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedWithPercentageFold_eightyPercentApplied() {
|
||||
List<String> coupons = Collections.singletonList("coupon-1");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-001");
|
||||
|
||||
stubDefaultPersistence();
|
||||
stubCoupon("coupon-1", "master-1", "1", BigDecimal.valueOf(8), "");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(50), 1, coupons, "commodity-perc", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("40.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedWithPercentageFoldGreaterThanTenTreatsAsFree() {
|
||||
List<String> coupons = Collections.singletonList("coupon-1");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-001");
|
||||
|
||||
stubDefaultPersistence();
|
||||
stubCoupon("coupon-1", "master-1", "1", BigDecimal.valueOf(20), "");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(40), 1, coupons, "commodity-perc", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("0.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedWithNonPositivePercentageIgnoresDiscount() {
|
||||
List<String> coupons = Collections.singletonList("coupon-1");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-001");
|
||||
|
||||
stubDefaultPersistence();
|
||||
stubCoupon("coupon-1", "master-1", "1", BigDecimal.ZERO, "");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(25), 1, coupons, "commodity-perc", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("25.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedThrowsWhenCouponDetailMissing() {
|
||||
List<String> coupons = Collections.singletonList("coupon-1");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
when(playCouponDetailsService.getById("coupon-1")).thenReturn(null);
|
||||
|
||||
OrderPlacementCommand command = command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(30), 1, coupons, "commodity-missing", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null);
|
||||
|
||||
assertThrows(CustomException.class, () -> lifecycleService.placeOrder(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedThrowsWhenCouponIneligible() {
|
||||
List<String> coupons = Collections.singletonList("coupon-1");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
stubCoupon("coupon-1", "master-1", "0", BigDecimal.valueOf(10), "订单类型不符合");
|
||||
|
||||
OrderPlacementCommand command = command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(30), 1, coupons, "commodity-invalid", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null);
|
||||
|
||||
assertThrows(CustomException.class, () -> lifecycleService.placeOrder(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_specifiedSkipsUnsupportedCouponTypeButSucceeds() {
|
||||
List<String> coupons = Collections.singletonList("coupon-unsupported");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-001");
|
||||
|
||||
stubDefaultPersistence();
|
||||
PlayCouponDetailsEntity detail = new PlayCouponDetailsEntity();
|
||||
detail.setCouponId("master-unsupported");
|
||||
when(playCouponDetailsService.getById("coupon-unsupported")).thenReturn(detail);
|
||||
PlayCouponInfoEntity info = new PlayCouponInfoEntity();
|
||||
info.setDiscountType("X");
|
||||
info.setDiscountAmount(BigDecimal.valueOf(999));
|
||||
when(playCouponInfoService.selectPlayCouponInfoById("master-unsupported")).thenReturn(info);
|
||||
when(playCouponInfoService.getCouponReasonForUnavailableUse(any(), anyString(), anyString(), anyInt(), any()))
|
||||
.thenReturn("");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(45), 1, coupons, "commodity-unsupported", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("45.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_randomWithValidRequirementsSucceeds() {
|
||||
RandomOrderRequirements requirements = RandomOrderRequirements.builder()
|
||||
.clerkGender(Gender.MALE)
|
||||
.clerkLevelId("level-1")
|
||||
.excludeHistory("0")
|
||||
.build();
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.RANDOM,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
requirements,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(35), 2, Collections.emptyList(), "commodity-rand", PlaceType.RANDOM),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("70.00", result.getAmountBreakdown().getGrossAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_randomWithoutRequirementsThrows() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.RANDOM,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
null);
|
||||
|
||||
OrderPlacementCommand command = command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(20), 1, Collections.emptyList(), "commodity-rand", PlaceType.RANDOM),
|
||||
false,
|
||||
null,
|
||||
null);
|
||||
|
||||
assertThrows(CustomException.class, () -> lifecycleService.placeOrder(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_rewardGiftWithoutAcceptByDoesNotAutoComplete() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.REWARD,
|
||||
RewardType.GIFT,
|
||||
payment(BigDecimal.valueOf(30), BigDecimal.valueOf(30), BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
lifecycleService.placeOrder(command(context, null, false, null, null));
|
||||
|
||||
verify(orderInfoMapper, never()).selectById(anyString());
|
||||
verify(wxCustomMpService, never()).sendOrderFinishMessageAsync(any());
|
||||
verify(earningsService, never()).createFromOrder(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_rewardGiftWithAcceptByTriggersAutoComplete() {
|
||||
PlayOrderInfoEntity inProgress = buildOrder("reward-order", OrderStatus.IN_PROGRESS.getCode());
|
||||
inProgress.setAcceptBy("clerk-1");
|
||||
PlayOrderInfoEntity completed = buildOrder("reward-order", OrderStatus.COMPLETED.getCode());
|
||||
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.REWARD,
|
||||
RewardType.GIFT,
|
||||
payment(BigDecimal.valueOf(30), BigDecimal.valueOf(30), BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
"clerk-1");
|
||||
|
||||
stubDefaultPersistence();
|
||||
when(orderInfoMapper.selectById(anyString())).thenReturn(inProgress, completed);
|
||||
when(orderInfoMapper.update(isNull(), any())).thenReturn(1);
|
||||
mockEarningsCounts(0L, 1L);
|
||||
doNothing().when(customUserInfoService).handleOrderCompletion(any());
|
||||
doNothing().when(earningsService).createFromOrder(any());
|
||||
|
||||
lifecycleService.placeOrder(command(context, null, false, null, null));
|
||||
|
||||
verify(orderInfoMapper, atLeastOnce()).selectById(anyString());
|
||||
verify(customUserInfoService).handleOrderCompletion(any());
|
||||
verify(earningsService).createFromOrder(completed);
|
||||
verify(wxCustomMpService).sendOrderFinishMessageAsync(completed);
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_rewardTipWithoutAcceptByDoesNotAutoComplete() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.REWARD,
|
||||
RewardType.BALANCE,
|
||||
payment(BigDecimal.valueOf(40), BigDecimal.valueOf(40), BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
lifecycleService.placeOrder(command(context, null, false, null, null));
|
||||
|
||||
verify(orderInfoMapper, never()).selectById(anyString());
|
||||
verify(wxCustomMpService, never()).sendOrderFinishMessageAsync(any());
|
||||
verify(earningsService, never()).createFromOrder(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_rewardTipWithAcceptByTriggersAutoComplete() {
|
||||
PlayOrderInfoEntity inProgress = buildOrder("tip-reward", OrderStatus.IN_PROGRESS.getCode());
|
||||
inProgress.setAcceptBy("clerk-3");
|
||||
PlayOrderInfoEntity completed = buildOrder("tip-reward", OrderStatus.COMPLETED.getCode());
|
||||
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.REWARD,
|
||||
RewardType.BALANCE,
|
||||
payment(BigDecimal.valueOf(40), BigDecimal.valueOf(40), BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
"clerk-3");
|
||||
|
||||
stubDefaultPersistence();
|
||||
when(orderInfoMapper.selectById(anyString())).thenReturn(inProgress, completed);
|
||||
when(orderInfoMapper.update(isNull(), any())).thenReturn(1);
|
||||
mockEarningsCounts(0L, 1L);
|
||||
doNothing().when(customUserInfoService).handleOrderCompletion(any());
|
||||
doNothing().when(earningsService).createFromOrder(any());
|
||||
|
||||
lifecycleService.placeOrder(command(context, null, false, null, null));
|
||||
|
||||
verify(orderInfoMapper, atLeastOnce()).selectById(anyString());
|
||||
verify(customUserInfoService).handleOrderCompletion(any());
|
||||
verify(earningsService).createFromOrder(completed);
|
||||
verify(wxCustomMpService).sendOrderFinishMessageAsync(completed);
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_withBalanceDeductionAndSufficientFundsUpdatesBalance() {
|
||||
List<String> coupons = Collections.emptyList();
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-balance");
|
||||
|
||||
PlayCustomUserInfoEntity customer = customer(context.getPurchaserBy(), BigDecimal.valueOf(100));
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(customer);
|
||||
doNothing().when(customUserInfoService).updateAccountBalanceById(anyString(), any(), any(), anyString(), anyString(), any(), any(), anyString());
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(20), 2, coupons, "commodity-balance", PlaceType.SPECIFIED),
|
||||
true,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("40.00", result.getAmountBreakdown().getNetAmount());
|
||||
verify(customUserInfoService).updateAccountBalanceById(
|
||||
eq(context.getPurchaserBy()),
|
||||
eq(customer.getAccountBalance()),
|
||||
eq(customer.getAccountBalance().subtract(new BigDecimal("40.00"))),
|
||||
eq(BalanceOperationType.CONSUME.getCode()),
|
||||
eq("下单"),
|
||||
eq(new BigDecimal("40.00")),
|
||||
eq(BigDecimal.ZERO),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_withBalanceDeductionUsesCustomAction() {
|
||||
List<String> coupons = Collections.emptyList();
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
null);
|
||||
|
||||
PlayCustomUserInfoEntity customer = customer(context.getPurchaserBy(), BigDecimal.valueOf(50));
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(customer);
|
||||
doNothing().when(customUserInfoService).updateAccountBalanceById(anyString(), any(), any(), anyString(), anyString(), any(), any(), anyString());
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(10), 2, coupons, "commodity-act", PlaceType.SPECIFIED),
|
||||
true,
|
||||
"测试扣款",
|
||||
null));
|
||||
|
||||
verify(customUserInfoService).updateAccountBalanceById(
|
||||
eq(context.getPurchaserBy()),
|
||||
any(),
|
||||
any(),
|
||||
eq(BalanceOperationType.CONSUME.getCode()),
|
||||
eq("测试扣款"),
|
||||
eq(new BigDecimal("20.00")),
|
||||
eq(BigDecimal.ZERO),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_withInsufficientBalanceThrows() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
null);
|
||||
|
||||
PlayCustomUserInfoEntity customer = customer(context.getPurchaserBy(), BigDecimal.valueOf(10));
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(customer);
|
||||
|
||||
stubDefaultPersistence();
|
||||
OrderPlacementCommand command = command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(20), 1, Collections.emptyList(), "commodity-insufficient", PlaceType.SPECIFIED),
|
||||
true,
|
||||
null,
|
||||
null);
|
||||
|
||||
assertThrows(ServiceException.class, () -> lifecycleService.placeOrder(command));
|
||||
verify(customUserInfoService, never()).updateAccountBalanceById(anyString(), any(), any(), anyString(), anyString(), any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_withMissingCustomerThrows() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.OTHER,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
null);
|
||||
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
OrderPlacementCommand command = command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(10), 1, Collections.emptyList(), "commodity-missing", PlaceType.SPECIFIED),
|
||||
true,
|
||||
null,
|
||||
null);
|
||||
|
||||
assertThrows(CustomException.class, () -> lifecycleService.placeOrder(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_noDeductionHandlesNullFinalAmount() {
|
||||
PaymentInfo payment = payment(BigDecimal.valueOf(15), null, BigDecimal.ZERO, Collections.emptyList());
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.OTHER,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment,
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(context, null, false, null, null));
|
||||
|
||||
assertMoneyEquals("0.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_deductFalseStillMarksCouponUsage() {
|
||||
List<String> coupons = Collections.singletonList("coupon-flag");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
null);
|
||||
|
||||
stubDefaultPersistence();
|
||||
stubCoupon("coupon-flag", "master-flag", "0", BigDecimal.valueOf(5), "");
|
||||
|
||||
lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(25), 1, coupons, "commodity-flag", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
verify(playCouponDetailsService).updateCouponUseStateByIds(eq(coupons), eq(CouponUseState.USED.getCode()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_randomWithBalanceDeductionSucceeds() {
|
||||
RandomOrderRequirements requirements = RandomOrderRequirements.builder()
|
||||
.clerkGender(Gender.FEMALE)
|
||||
.clerkLevelId("level-9")
|
||||
.excludeHistory("1")
|
||||
.build();
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.RANDOM,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
requirements,
|
||||
null);
|
||||
|
||||
PlayCustomUserInfoEntity customer = customer(context.getPurchaserBy(), BigDecimal.valueOf(300));
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(customer);
|
||||
doNothing().when(customUserInfoService).updateAccountBalanceById(anyString(), any(), any(), anyString(), anyString(), any(), any(), anyString());
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(60), 2, Collections.emptyList(), "commodity-random", PlaceType.RANDOM),
|
||||
true,
|
||||
"随机扣款",
|
||||
null));
|
||||
|
||||
assertMoneyEquals("120.00", result.getAmountBreakdown().getNetAmount());
|
||||
verify(customUserInfoService).updateAccountBalanceById(
|
||||
eq(context.getPurchaserBy()),
|
||||
any(),
|
||||
any(),
|
||||
eq(BalanceOperationType.CONSUME.getCode()),
|
||||
eq("随机扣款"),
|
||||
eq(new BigDecimal("120.00")),
|
||||
eq(BigDecimal.ZERO),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_randomWithDeductionThrowsWhenInsufficientBalance() {
|
||||
RandomOrderRequirements requirements = RandomOrderRequirements.builder()
|
||||
.clerkGender(Gender.UNKNOWN)
|
||||
.clerkLevelId("level-2")
|
||||
.excludeHistory("0")
|
||||
.build();
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.RANDOM,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
requirements,
|
||||
null);
|
||||
|
||||
PlayCustomUserInfoEntity customer = customer(context.getPurchaserBy(), BigDecimal.valueOf(10));
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(customer);
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
OrderPlacementCommand command = command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(30), 1, Collections.emptyList(), "commodity-rand", PlaceType.RANDOM),
|
||||
true,
|
||||
null,
|
||||
null);
|
||||
|
||||
assertThrows(ServiceException.class, () -> lifecycleService.placeOrder(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_withMixedCouponTypesAppliesValidOnes() {
|
||||
List<String> coupons = Arrays.asList("coupon-invalid", "coupon-valid");
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, coupons),
|
||||
null,
|
||||
"clerk-1");
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
PlayCouponDetailsEntity invalidDetail = new PlayCouponDetailsEntity();
|
||||
invalidDetail.setCouponId("master-invalid");
|
||||
PlayCouponDetailsEntity validDetail = new PlayCouponDetailsEntity();
|
||||
validDetail.setCouponId("master-valid");
|
||||
when(playCouponDetailsService.getById("coupon-invalid")).thenReturn(invalidDetail);
|
||||
when(playCouponDetailsService.getById("coupon-valid")).thenReturn(validDetail);
|
||||
|
||||
PlayCouponInfoEntity invalidInfo = new PlayCouponInfoEntity();
|
||||
invalidInfo.setDiscountType("X");
|
||||
invalidInfo.setDiscountAmount(BigDecimal.valueOf(100));
|
||||
PlayCouponInfoEntity validInfo = new PlayCouponInfoEntity();
|
||||
validInfo.setDiscountType("0");
|
||||
validInfo.setDiscountAmount(BigDecimal.valueOf(10));
|
||||
when(playCouponInfoService.selectPlayCouponInfoById("master-invalid")).thenReturn(invalidInfo);
|
||||
when(playCouponInfoService.selectPlayCouponInfoById("master-valid")).thenReturn(validInfo);
|
||||
when(playCouponInfoService.getCouponReasonForUnavailableUse(any(), anyString(), anyString(), anyInt(), any()))
|
||||
.thenReturn("");
|
||||
|
||||
OrderPlacementResult result = lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(35), 1, coupons, "commodity-mixed", PlaceType.SPECIFIED),
|
||||
false,
|
||||
null,
|
||||
null));
|
||||
|
||||
assertMoneyEquals("25.00", result.getAmountBreakdown().getNetAmount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_withDeductionFetchesCustomerBalance() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.SPECIFIED,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
null);
|
||||
|
||||
PlayCustomUserInfoEntity customer = customer(context.getPurchaserBy(), BigDecimal.valueOf(70));
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(customer);
|
||||
doNothing().when(customUserInfoService).updateAccountBalanceById(anyString(), any(), any(), anyString(), anyString(), any(), any(), anyString());
|
||||
stubDefaultPersistence();
|
||||
|
||||
lifecycleService.placeOrder(command(
|
||||
context,
|
||||
pricing(BigDecimal.valueOf(15), 2, Collections.emptyList(), "commodity-snap", PlaceType.SPECIFIED),
|
||||
true,
|
||||
null,
|
||||
null));
|
||||
|
||||
verify(customUserInfoService).selectById(context.getPurchaserBy());
|
||||
verify(customUserInfoService).updateAccountBalanceById(
|
||||
eq(customer.getId()),
|
||||
eq(customer.getAccountBalance()),
|
||||
eq(customer.getAccountBalance().subtract(new BigDecimal("30.00"))),
|
||||
eq(BalanceOperationType.CONSUME.getCode()),
|
||||
eq("下单"),
|
||||
eq(new BigDecimal("30.00")),
|
||||
eq(BigDecimal.ZERO),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void placeOrder_withZeroFinalAmountStillUpdatesLedger() {
|
||||
OrderCreationContext context = baseContext(
|
||||
PlaceType.OTHER,
|
||||
RewardType.NOT_APPLICABLE,
|
||||
payment(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, Collections.emptyList()),
|
||||
null,
|
||||
null);
|
||||
|
||||
PlayCustomUserInfoEntity customer = customer(context.getPurchaserBy(), BigDecimal.valueOf(10));
|
||||
when(customUserInfoService.selectById(context.getPurchaserBy())).thenReturn(customer);
|
||||
doNothing().when(customUserInfoService).updateAccountBalanceById(anyString(), any(), any(), anyString(), anyString(), any(), any(), anyString());
|
||||
|
||||
stubDefaultPersistence();
|
||||
|
||||
lifecycleService.placeOrder(command(
|
||||
context,
|
||||
null,
|
||||
true,
|
||||
null,
|
||||
null));
|
||||
|
||||
verify(customUserInfoService).updateAccountBalanceById(
|
||||
eq(context.getPurchaserBy()),
|
||||
eq(customer.getAccountBalance()),
|
||||
eq(customer.getAccountBalance()),
|
||||
eq(BalanceOperationType.CONSUME.getCode()),
|
||||
eq("下单"),
|
||||
argThat(amount -> amount.compareTo(BigDecimal.ZERO) == 0),
|
||||
argThat(amount -> amount.compareTo(BigDecimal.ZERO) == 0),
|
||||
anyString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void completeOrder_inProgress_createsEarningsAndNotifies() {
|
||||
String orderId = UUID.randomUUID().toString();
|
||||
@@ -329,6 +1152,103 @@ class OrderLifecycleServiceImplTest {
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void stubDefaultPersistence() {
|
||||
lenient().when(orderInfoMapper.selectCount(any())).thenReturn(0L);
|
||||
lenient().when(orderInfoMapper.insert(any())).thenReturn(1);
|
||||
lenient().doNothing().when(customUserInfoService).saveOrderInfo(any());
|
||||
lenient().doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), anyString());
|
||||
ClerkEstimatedRevenueVo revenueVo = new ClerkEstimatedRevenueVo();
|
||||
revenueVo.setRevenueAmount(BigDecimal.ZERO);
|
||||
revenueVo.setRevenueRatio(0);
|
||||
lenient().when(clerkRevenueCalculator.calculateEstimatedRevenue(
|
||||
anyString(), anyList(), anyString(), anyString(), any())).thenReturn(revenueVo);
|
||||
}
|
||||
|
||||
private PaymentInfo payment(BigDecimal gross, BigDecimal net, BigDecimal discount, List<String> couponIds) {
|
||||
return PaymentInfo.builder()
|
||||
.orderMoney(gross)
|
||||
.finalAmount(net)
|
||||
.discountAmount(discount)
|
||||
.couponIds(couponIds)
|
||||
.build();
|
||||
}
|
||||
|
||||
private OrderCreationContext baseContext(PlaceType placeType, RewardType rewardType, PaymentInfo paymentInfo,
|
||||
RandomOrderRequirements randomRequirements, String acceptBy) {
|
||||
return OrderCreationContext.builder()
|
||||
.orderId(UUID.randomUUID().toString())
|
||||
.orderNo("NO-" + System.nanoTime())
|
||||
.orderStatus(OrderStatus.PENDING)
|
||||
.orderType(OrderType.NORMAL)
|
||||
.placeType(placeType)
|
||||
.rewardType(rewardType)
|
||||
.isFirstOrder(true)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity-" + placeType.getCode())
|
||||
.commodityType(CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.TEN)
|
||||
.serviceDuration("60")
|
||||
.commodityName("服务")
|
||||
.commodityNumber("1")
|
||||
.build())
|
||||
.paymentInfo(paymentInfo)
|
||||
.purchaserBy("customer-" + placeType.getCode())
|
||||
.acceptBy(acceptBy)
|
||||
.randomOrderRequirements(randomRequirements)
|
||||
.build();
|
||||
}
|
||||
|
||||
private OrderPlacementCommand.PricingInput pricing(BigDecimal unitPrice, int quantity, List<String> couponIds,
|
||||
String commodityId, PlaceType placeType) {
|
||||
return OrderPlacementCommand.PricingInput.builder()
|
||||
.unitPrice(unitPrice)
|
||||
.quantity(quantity)
|
||||
.couponIds(couponIds)
|
||||
.commodityId(commodityId)
|
||||
.placeType(placeType)
|
||||
.build();
|
||||
}
|
||||
|
||||
private OrderPlacementCommand command(OrderCreationContext context,
|
||||
OrderPlacementCommand.PricingInput pricing,
|
||||
boolean deduct,
|
||||
String action,
|
||||
PlayCustomUserInfoEntity snapshot) {
|
||||
OrderPlacementCommand.OrderPlacementCommandBuilder builder = OrderPlacementCommand.builder()
|
||||
.orderContext(context)
|
||||
.deductBalance(deduct)
|
||||
.balanceOperationAction(action);
|
||||
if (pricing != null) {
|
||||
builder.pricingInput(pricing);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void stubCoupon(String detailId, String masterId, String discountType, BigDecimal discountAmount,
|
||||
String reason) {
|
||||
PlayCouponDetailsEntity detail = new PlayCouponDetailsEntity();
|
||||
detail.setCouponId(masterId);
|
||||
when(playCouponDetailsService.getById(detailId)).thenReturn(detail);
|
||||
|
||||
PlayCouponInfoEntity info = new PlayCouponInfoEntity();
|
||||
info.setDiscountType(discountType);
|
||||
info.setDiscountAmount(discountAmount);
|
||||
when(playCouponInfoService.selectPlayCouponInfoById(masterId)).thenReturn(info);
|
||||
when(playCouponInfoService.getCouponReasonForUnavailableUse(
|
||||
eq(info), anyString(), anyString(), anyInt(), any())).thenReturn(reason);
|
||||
}
|
||||
|
||||
private PlayCustomUserInfoEntity customer(String id, BigDecimal balance) {
|
||||
PlayCustomUserInfoEntity entity = new PlayCustomUserInfoEntity();
|
||||
entity.setId(id);
|
||||
entity.setAccountBalance(balance.setScale(2));
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void assertMoneyEquals(String expected, BigDecimal actual) {
|
||||
assertEquals(0, actual.compareTo(new BigDecimal(expected)));
|
||||
}
|
||||
|
||||
private void mockEarningsCounts(long... counts) {
|
||||
LambdaQueryChainWrapper<EarningsLineEntity> chain = Mockito.mock(LambdaQueryChainWrapper.class);
|
||||
when(chain.eq(any(), any())).thenReturn(chain);
|
||||
|
||||
Reference in New Issue
Block a user