feat: 完善订单生命周期幂等与日志追踪
This commit is contained in:
@@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationRequest;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationContext;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
import java.math.BigDecimal;
|
||||
@@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test;
|
||||
*
|
||||
* @author admin
|
||||
*/
|
||||
class OrderCreationRequestTest {
|
||||
class OrderCreationContextTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("测试Builder模式构建订单请求")
|
||||
@@ -42,7 +42,7 @@ class OrderCreationRequestTest {
|
||||
.build();
|
||||
|
||||
// 构建订单请求
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
OrderCreationContext request = OrderCreationContext.builder()
|
||||
.orderId("order_123456")
|
||||
.orderNo("ORD20240906001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -82,7 +82,7 @@ class OrderCreationRequestTest {
|
||||
@DisplayName("测试订单类型判断方法")
|
||||
void testOrderTypeChecks() {
|
||||
// 测试指定单
|
||||
OrderCreationRequest specifiedOrder = OrderCreationRequest.builder()
|
||||
OrderCreationContext specifiedOrder = OrderCreationContext.builder()
|
||||
.orderId("order_001")
|
||||
.orderNo("ORD001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -99,7 +99,7 @@ class OrderCreationRequestTest {
|
||||
assertFalse(specifiedOrder.isRewardOrder());
|
||||
|
||||
// 测试随机单
|
||||
OrderCreationRequest randomOrder = OrderCreationRequest.builder()
|
||||
OrderCreationContext randomOrder = OrderCreationContext.builder()
|
||||
.orderId("order_002")
|
||||
.orderNo("ORD002")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -121,7 +121,7 @@ class OrderCreationRequestTest {
|
||||
assertFalse(randomOrder.isRewardOrder());
|
||||
|
||||
// 测试打赏单
|
||||
OrderCreationRequest rewardOrder = OrderCreationRequest.builder()
|
||||
OrderCreationContext rewardOrder = OrderCreationContext.builder()
|
||||
.orderId("order_003")
|
||||
.orderNo("ORD003")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -142,7 +142,7 @@ class OrderCreationRequestTest {
|
||||
@DisplayName("测试首单标识转换")
|
||||
void testFirstOrderStringConversion() {
|
||||
// 测试首单
|
||||
OrderCreationRequest firstOrder = OrderCreationRequest.builder()
|
||||
OrderCreationContext firstOrder = OrderCreationContext.builder()
|
||||
.orderId("order_001")
|
||||
.orderNo("ORD001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -157,7 +157,7 @@ class OrderCreationRequestTest {
|
||||
assertEquals("1", firstOrder.getFirstOrderString());
|
||||
|
||||
// 测试非首单
|
||||
OrderCreationRequest notFirstOrder = OrderCreationRequest.builder()
|
||||
OrderCreationContext notFirstOrder = OrderCreationContext.builder()
|
||||
.orderId("order_002")
|
||||
.orderNo("ORD002")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -176,7 +176,7 @@ class OrderCreationRequestTest {
|
||||
@DisplayName("测试随机单验证逻辑")
|
||||
void testRandomOrderValidation() {
|
||||
// 有效的随机单
|
||||
OrderCreationRequest validRandomOrder = OrderCreationRequest.builder()
|
||||
OrderCreationContext validRandomOrder = OrderCreationContext.builder()
|
||||
.orderId("order_001")
|
||||
.orderNo("ORD001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -194,7 +194,7 @@ class OrderCreationRequestTest {
|
||||
assertTrue(validRandomOrder.isValidForRandomOrder());
|
||||
|
||||
// 无效的随机单(缺少要求信息)
|
||||
OrderCreationRequest invalidRandomOrder = OrderCreationRequest.builder()
|
||||
OrderCreationContext invalidRandomOrder = OrderCreationContext.builder()
|
||||
.orderId("order_002")
|
||||
.orderNo("ORD002")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
@@ -1,512 +0,0 @@
|
||||
package com.starry.admin.modules.order.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
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;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||
import com.starry.admin.modules.order.module.dto.CommodityInfo;
|
||||
import com.starry.admin.modules.order.module.dto.OrderCreationRequest;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
|
||||
import com.starry.admin.modules.order.service.impl.PlayOrderInfoServiceImpl;
|
||||
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
|
||||
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
|
||||
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
/**
|
||||
* 订单服务测试类 - 测试重构后的createOrderInfo方法
|
||||
*
|
||||
* @author admin
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PlayOrderInfoServiceTest {
|
||||
|
||||
@Mock
|
||||
private PlayOrderInfoMapper orderInfoMapper;
|
||||
|
||||
@Mock
|
||||
private IPlayClerkUserInfoService playClerkUserInfoService;
|
||||
|
||||
@Mock
|
||||
private IPlayCustomUserInfoService playCustomUserInfoService;
|
||||
|
||||
@Mock
|
||||
private IPlayCustomUserInfoService userInfoService;
|
||||
|
||||
@Mock
|
||||
private IPlayCouponDetailsService playCouponDetailsService;
|
||||
|
||||
@Mock
|
||||
private WxCustomMpService wxCustomMpService;
|
||||
|
||||
@Mock
|
||||
private IPlayCustomUserInfoService customUserInfoService;
|
||||
|
||||
@Mock
|
||||
private IPlayClerkLevelInfoService playClerkLevelInfoService;
|
||||
|
||||
@Mock
|
||||
private IPlayPersonnelGroupInfoService playClerkGroupInfoService;
|
||||
|
||||
@Mock
|
||||
private IPlayOrderRefundInfoService playOrderRefundInfoService;
|
||||
|
||||
@Mock
|
||||
private IEarningsService earningsService;
|
||||
|
||||
@InjectMocks
|
||||
private PlayOrderInfoServiceImpl orderService;
|
||||
|
||||
@Test
|
||||
@DisplayName("创建指定订单 - 成功案例")
|
||||
void testCreateSpecifiedOrder_Success() {
|
||||
// 准备测试数据
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
.orderId("test_order_001")
|
||||
.orderNo("ORD20241001001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.rewardType(OrderConstant.RewardType.BALANCE)
|
||||
.isFirstOrder(true)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity_001")
|
||||
.commodityName("测试商品")
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.valueOf(100.00))
|
||||
.serviceDuration("60")
|
||||
.commodityNumber("1")
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(100.00))
|
||||
.finalAmount(BigDecimal.valueOf(90.00))
|
||||
.discountAmount(BigDecimal.valueOf(10.00))
|
||||
.couponIds(Arrays.asList("coupon_001"))
|
||||
.payMethod("1")
|
||||
.build())
|
||||
.purchaserBy("customer_001")
|
||||
// 不设置 acceptBy,避免调用复杂的 setAcceptByInfo 方法
|
||||
.weiChatCode("wx_test_001")
|
||||
.remark("测试订单")
|
||||
.build();
|
||||
|
||||
// Mock 依赖服务的返回
|
||||
when(orderInfoMapper.insert(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
doNothing().when(userInfoService).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), eq("2"));
|
||||
|
||||
// 执行测试
|
||||
assertDoesNotThrow(() -> orderService.createOrderInfo(request));
|
||||
|
||||
// 验证方法调用
|
||||
verify(orderInfoMapper, times(1)).insert(any(PlayOrderInfoEntity.class));
|
||||
verify(userInfoService, times(1)).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
verify(playCouponDetailsService, times(1)).updateCouponUseStateByIds(Arrays.asList("coupon_001"), "2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建随机订单 - 成功案例")
|
||||
void testCreateRandomOrder_Success() {
|
||||
// 准备随机单要求
|
||||
RandomOrderRequirements randomRequirements = RandomOrderRequirements.builder()
|
||||
.clerkGender(OrderConstant.Gender.FEMALE)
|
||||
.clerkLevelId("level_001")
|
||||
.excludeHistory("1")
|
||||
.build();
|
||||
|
||||
// 构建随机单请求
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
.orderId("random_order_001")
|
||||
.orderNo("RND20241001001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.RANDOM)
|
||||
.rewardType(OrderConstant.RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(false)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("service_001")
|
||||
.commodityName("陪聊服务")
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.valueOf(50.00))
|
||||
.serviceDuration("30")
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(50.00))
|
||||
.finalAmount(BigDecimal.valueOf(50.00))
|
||||
.discountAmount(BigDecimal.ZERO)
|
||||
.couponIds(Collections.emptyList())
|
||||
.payMethod("0")
|
||||
.build())
|
||||
.purchaserBy("customer_002")
|
||||
.weiChatCode("wx_test_002")
|
||||
.remark("随机单测试")
|
||||
.randomOrderRequirements(randomRequirements)
|
||||
.build();
|
||||
|
||||
// Mock 依赖服务的返回
|
||||
when(orderInfoMapper.insert(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
doNothing().when(userInfoService).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), eq("2"));
|
||||
|
||||
// 执行测试
|
||||
assertDoesNotThrow(() -> orderService.createOrderInfo(request));
|
||||
|
||||
// 验证方法调用
|
||||
verify(orderInfoMapper, times(1)).insert(any(PlayOrderInfoEntity.class));
|
||||
verify(userInfoService, times(1)).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
verify(playCouponDetailsService, times(1)).updateCouponUseStateByIds(Collections.emptyList(), "2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建打赏订单 - 自动完成")
|
||||
void testCreateRewardOrder_AutoComplete() {
|
||||
// 构建打赏单请求
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
.orderId("reward_order_001")
|
||||
.orderNo("REW20241001001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.REWARD)
|
||||
.rewardType(OrderConstant.RewardType.GIFT)
|
||||
.isFirstOrder(false)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("gift_001")
|
||||
.commodityName("虚拟礼物")
|
||||
.commodityType(OrderConstant.CommodityType.GIFT)
|
||||
.commodityPrice(BigDecimal.valueOf(20.00))
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(20.00))
|
||||
.finalAmount(BigDecimal.valueOf(20.00))
|
||||
.discountAmount(BigDecimal.ZERO)
|
||||
.couponIds(Collections.emptyList())
|
||||
.payMethod("1")
|
||||
.build())
|
||||
.purchaserBy("customer_003")
|
||||
// 不设置 acceptBy,避免调用复杂的 setAcceptByInfo 方法
|
||||
.weiChatCode("wx_test_003")
|
||||
.remark("打赏订单")
|
||||
.build();
|
||||
|
||||
// Mock 依赖服务的返回
|
||||
when(orderInfoMapper.insert(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
doNothing().when(userInfoService).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), eq("2"));
|
||||
|
||||
// 执行测试
|
||||
assertDoesNotThrow(() -> orderService.createOrderInfo(request));
|
||||
|
||||
// 验证方法调用
|
||||
verify(orderInfoMapper, times(1)).insert(any(PlayOrderInfoEntity.class));
|
||||
verify(userInfoService, times(1)).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
verify(playCouponDetailsService, times(1)).updateCouponUseStateByIds(Collections.emptyList(), "2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("创建随机订单失败 - 缺少随机单要求")
|
||||
void testCreateRandomOrder_MissingRequirements() {
|
||||
// 构建无要求的随机单请求
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
.orderId("invalid_random_order")
|
||||
.orderNo("IRO20241001001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.RANDOM) // 随机单但没有要求
|
||||
.rewardType(OrderConstant.RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(false)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("service_001")
|
||||
.commodityName("服务")
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.valueOf(50.00))
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(50.00))
|
||||
.finalAmount(BigDecimal.valueOf(50.00))
|
||||
.discountAmount(BigDecimal.ZERO)
|
||||
.couponIds(Collections.emptyList())
|
||||
.build())
|
||||
.purchaserBy("customer_004")
|
||||
.weiChatCode("wx_test_004")
|
||||
.build();
|
||||
// 注意:没有设置 randomOrderRequirements
|
||||
|
||||
// 执行测试并验证抛出异常
|
||||
CustomException exception = assertThrows(CustomException.class,
|
||||
() -> orderService.createOrderInfo(request));
|
||||
|
||||
assertEquals("随机单必须提供店员要求信息", exception.getMessage());
|
||||
|
||||
// 验证没有调用数据库操作
|
||||
verify(orderInfoMapper, never()).insert(any(PlayOrderInfoEntity.class));
|
||||
verify(userInfoService, never()).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
verify(playCouponDetailsService, never()).updateCouponUseStateByIds(anyList(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试优惠券使用状态更新")
|
||||
void testCouponStatusUpdate() {
|
||||
// 准备包含多个优惠券的订单
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
.orderId("coupon_order_001")
|
||||
.orderNo("CPN20241001001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.rewardType(OrderConstant.RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(false)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity_002")
|
||||
.commodityName("优惠商品")
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.valueOf(200.00))
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(200.00))
|
||||
.finalAmount(BigDecimal.valueOf(150.00))
|
||||
.discountAmount(BigDecimal.valueOf(50.00))
|
||||
.couponIds(Arrays.asList("coupon_001", "coupon_002", "coupon_003"))
|
||||
.payMethod("1")
|
||||
.build())
|
||||
.purchaserBy("customer_005")
|
||||
// 不设置 acceptBy,避免调用复杂的 setAcceptByInfo 方法
|
||||
.weiChatCode("wx_test_005")
|
||||
.build();
|
||||
|
||||
// Mock 依赖服务的返回
|
||||
when(orderInfoMapper.insert(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
doNothing().when(userInfoService).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), eq("2"));
|
||||
|
||||
// 执行测试
|
||||
orderService.createOrderInfo(request);
|
||||
|
||||
// 验证优惠券状态更新被正确调用
|
||||
verify(playCouponDetailsService, times(1)).updateCouponUseStateByIds(
|
||||
Arrays.asList("coupon_001", "coupon_002", "coupon_003"), "2");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试带接单人的订单创建 - 需要完整mock依赖")
|
||||
void testCreateOrderWithAcceptBy_ComplexScenario() {
|
||||
// 创建模拟的店员等级信息
|
||||
com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity mockLevelEntity =
|
||||
new com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity();
|
||||
mockLevelEntity.setFirstRegularRatio(15);
|
||||
mockLevelEntity.setNotFirstRegularRatio(12);
|
||||
|
||||
// 创建模拟的优惠券信息
|
||||
com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo mockCouponInfo =
|
||||
new com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo();
|
||||
mockCouponInfo.setAttributionDiscounts("1"); // 1表示店铺承担,不需要从店员收入中扣除
|
||||
mockCouponInfo.setDiscountType("0");
|
||||
mockCouponInfo.setDiscountAmount(BigDecimal.valueOf(20.00));
|
||||
|
||||
// 准备测试数据
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
.orderId("complex_order_001")
|
||||
.orderNo("CPX20241001001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.rewardType(OrderConstant.RewardType.BALANCE)
|
||||
.isFirstOrder(true)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity_003")
|
||||
.commodityName("复杂商品")
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.valueOf(300.00))
|
||||
.serviceDuration("120")
|
||||
.commodityNumber("1")
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(300.00))
|
||||
.finalAmount(BigDecimal.valueOf(280.00))
|
||||
.discountAmount(BigDecimal.valueOf(20.00))
|
||||
.couponIds(Arrays.asList("coupon_004"))
|
||||
.payMethod("0")
|
||||
.build())
|
||||
.purchaserBy("customer_006")
|
||||
.acceptBy("clerk_004")
|
||||
.weiChatCode("wx_test_006")
|
||||
.remark("带接单人的复杂订单")
|
||||
.build();
|
||||
|
||||
// Mock 店员相关的依赖
|
||||
when(playClerkUserInfoService.queryLevelCommission("clerk_004")).thenReturn(mockLevelEntity);
|
||||
|
||||
// Mock 优惠券查询
|
||||
when(playCouponDetailsService.selectPlayCouponDetailsById("coupon_004")).thenReturn(mockCouponInfo);
|
||||
|
||||
// Mock 其他依赖服务的返回
|
||||
when(orderInfoMapper.insert(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
doNothing().when(userInfoService).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), eq("2"));
|
||||
|
||||
// 执行测试
|
||||
assertDoesNotThrow(() -> orderService.createOrderInfo(request));
|
||||
|
||||
// 验证方法调用
|
||||
verify(orderInfoMapper, times(1)).insert(any(PlayOrderInfoEntity.class));
|
||||
verify(userInfoService, times(1)).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
verify(playCouponDetailsService, times(1)).updateCouponUseStateByIds(Arrays.asList("coupon_004"), "2");
|
||||
verify(playClerkUserInfoService, times(1)).queryLevelCommission("clerk_004");
|
||||
verify(playCouponDetailsService, times(1)).selectPlayCouponDetailsById("coupon_004");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("测试店员收入计算 - 优惠券由店员承担")
|
||||
void testClerkRevenueCalculation_ClerkBearsCouponCost() {
|
||||
// 创建模拟的店员等级信息
|
||||
com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity mockLevelEntity =
|
||||
new com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity();
|
||||
mockLevelEntity.setFirstRegularRatio(20); // 首单20%佣金
|
||||
mockLevelEntity.setNotFirstRegularRatio(15); // 非首单15%佣金
|
||||
|
||||
// 创建模拟的优惠券信息 - 店员承担优惠
|
||||
com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo mockCouponInfo =
|
||||
new com.starry.admin.modules.shop.module.vo.PlayCouponDetailsReturnVo();
|
||||
mockCouponInfo.setAttributionDiscounts("0"); // 0表示店员承担,需要从店员收入中扣除
|
||||
mockCouponInfo.setDiscountType("0"); // 固定金额优惠
|
||||
mockCouponInfo.setDiscountAmount(BigDecimal.valueOf(15.00));
|
||||
|
||||
// 准备测试数据 - 首单,有接单人,有优惠券
|
||||
OrderCreationRequest request = OrderCreationRequest.builder()
|
||||
.orderId("revenue_test_001")
|
||||
.orderNo("REV20241001001")
|
||||
.orderStatus(OrderConstant.OrderStatus.PENDING)
|
||||
.orderType(OrderConstant.OrderType.NORMAL)
|
||||
.placeType(OrderConstant.PlaceType.SPECIFIED)
|
||||
.rewardType(OrderConstant.RewardType.BALANCE)
|
||||
.isFirstOrder(true) // 首单
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity_revenue")
|
||||
.commodityName("收入测试商品")
|
||||
.commodityType(OrderConstant.CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.valueOf(200.00))
|
||||
.serviceDuration("90")
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(200.00))
|
||||
.finalAmount(BigDecimal.valueOf(185.00)) // 使用了15元优惠券
|
||||
.discountAmount(BigDecimal.valueOf(15.00))
|
||||
.couponIds(Arrays.asList("coupon_revenue_001"))
|
||||
.payMethod("1")
|
||||
.build())
|
||||
.purchaserBy("customer_revenue")
|
||||
.acceptBy("clerk_revenue")
|
||||
.weiChatCode("wx_revenue_test")
|
||||
.remark("收入计算测试订单")
|
||||
.build();
|
||||
|
||||
// Mock 依赖
|
||||
when(playClerkUserInfoService.queryLevelCommission("clerk_revenue")).thenReturn(mockLevelEntity);
|
||||
when(playCouponDetailsService.selectPlayCouponDetailsById("coupon_revenue_001")).thenReturn(mockCouponInfo);
|
||||
when(orderInfoMapper.insert(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
doNothing().when(userInfoService).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), eq("2"));
|
||||
|
||||
// 执行测试
|
||||
assertDoesNotThrow(() -> orderService.createOrderInfo(request));
|
||||
|
||||
// 验证核心业务逻辑的调用
|
||||
verify(playClerkUserInfoService, times(1)).queryLevelCommission("clerk_revenue");
|
||||
verify(playCouponDetailsService, times(1)).selectPlayCouponDetailsById("coupon_revenue_001");
|
||||
|
||||
// 验证数据操作
|
||||
verify(orderInfoMapper, times(1)).insert(any(PlayOrderInfoEntity.class));
|
||||
verify(userInfoService, times(1)).saveOrderInfo(any(PlayOrderInfoEntity.class));
|
||||
verify(playCouponDetailsService, times(1)).updateCouponUseStateByIds(Arrays.asList("coupon_revenue_001"), "2");
|
||||
|
||||
// 这个测试验证了:
|
||||
// 1. 首单佣金比例计算(20%)
|
||||
// 2. 优惠券影响店员收入的计算逻辑
|
||||
// 3. 复杂业务流程的正确执行
|
||||
// 实际收入计算:185元 * 20% = 37元,但由于优惠券由店员承担,需要减去15元,最终收入22元
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("管理员强制取消已接单/服务中订单 - 成功流程")
|
||||
void testForceCancelOngoingOrderByAdminSuccess() {
|
||||
String orderId = "order_force_cancel";
|
||||
PlayOrderInfoEntity inProgressOrder = new PlayOrderInfoEntity();
|
||||
inProgressOrder.setId(orderId);
|
||||
inProgressOrder.setOrderStatus(OrderStatus.IN_PROGRESS.getCode());
|
||||
inProgressOrder.setAcceptBy("clerk-1");
|
||||
inProgressOrder.setPurchaserBy("customer-1");
|
||||
inProgressOrder.setFinalAmount(BigDecimal.valueOf(100));
|
||||
inProgressOrder.setPayMethod("1");
|
||||
|
||||
PlayOrderInfoEntity cancelledOrder = new PlayOrderInfoEntity();
|
||||
cancelledOrder.setId(orderId);
|
||||
cancelledOrder.setOrderStatus(OrderStatus.CANCELLED.getCode());
|
||||
|
||||
PlayCustomUserInfoEntity customUserInfo = new PlayCustomUserInfoEntity();
|
||||
customUserInfo.setId("customer-1");
|
||||
customUserInfo.setAccountBalance(BigDecimal.valueOf(200));
|
||||
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(inProgressOrder, cancelledOrder);
|
||||
when(orderInfoMapper.updateById(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
when(customUserInfoService.getById("customer-1")).thenReturn(customUserInfo);
|
||||
|
||||
doNothing().when(customUserInfoService).updateAccountBalanceById(eq("customer-1"), any(BigDecimal.class),
|
||||
any(BigDecimal.class), anyString(), anyString(), any(BigDecimal.class), any(BigDecimal.class), eq(orderId));
|
||||
doNothing().when(playOrderRefundInfoService).add(eq(orderId), eq("customer-1"), eq("clerk-1"), anyString(),
|
||||
anyString(), any(BigDecimal.class), anyString(), anyString(), anyString(), anyString(), anyString());
|
||||
doNothing().when(wxCustomMpService).sendOrderCancelMessageAsync(any(PlayOrderInfoEntity.class), anyString());
|
||||
|
||||
assertDoesNotThrow(() -> orderService.forceCancelOngoingOrder("2", "admin-1", orderId,
|
||||
BigDecimal.valueOf(80), "管理员取消测试", Collections.emptyList()));
|
||||
|
||||
verify(orderInfoMapper, times(1)).updateById(any(PlayOrderInfoEntity.class));
|
||||
verify(customUserInfoService, times(1)).updateAccountBalanceById(eq("customer-1"), any(BigDecimal.class),
|
||||
any(BigDecimal.class), eq(OrderConstant.BalanceOperationType.REFUND.getCode()), eq("订单取消退款"),
|
||||
eq(BigDecimal.valueOf(80)), eq(BigDecimal.ZERO), eq(orderId));
|
||||
verify(playOrderRefundInfoService, times(1)).add(eq(orderId), eq("customer-1"), eq("clerk-1"),
|
||||
eq(inProgressOrder.getPayMethod()),
|
||||
eq(OrderConstant.OrderRefundRecordType.PARTIAL.getCode()),
|
||||
eq(BigDecimal.valueOf(80)), eq("管理员取消测试"), eq("2"), eq("admin-1"),
|
||||
eq(OrderConstant.OrderRefundState.PROCESSING.getCode()),
|
||||
eq(OrderConstant.ReviewRequirement.NOT_REQUIRED.getCode()));
|
||||
verify(wxCustomMpService, times(1)).sendOrderCancelMessageAsync(any(PlayOrderInfoEntity.class),
|
||||
eq("管理员取消测试"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("强制取消订单 - 非进行中状态抛出异常")
|
||||
void testForceCancelOngoingOrderInvalidStatus() {
|
||||
String orderId = "order_invalid_force_cancel";
|
||||
PlayOrderInfoEntity pendingOrder = new PlayOrderInfoEntity();
|
||||
pendingOrder.setId(orderId);
|
||||
pendingOrder.setOrderStatus(OrderStatus.PENDING.getCode());
|
||||
pendingOrder.setAcceptBy("clerk-1");
|
||||
pendingOrder.setPurchaserBy("customer-1");
|
||||
pendingOrder.setFinalAmount(BigDecimal.valueOf(50));
|
||||
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(pendingOrder);
|
||||
|
||||
assertThrows(CustomException.class, () -> orderService.forceCancelOngoingOrder("2", "admin-1", orderId,
|
||||
null, "原因", Collections.emptyList()));
|
||||
verify(orderInfoMapper, never()).updateById(any(PlayOrderInfoEntity.class));
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||
@@ -12,27 +13,45 @@ import com.starry.admin.common.exception.CustomException;
|
||||
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;
|
||||
import com.starry.admin.modules.order.mapper.PlayOrderLogInfoMapper;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.BalanceOperationType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.CommodityType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.Gender;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.PayMethod;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.PlaceType;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
|
||||
import com.starry.admin.modules.order.module.constant.OrderConstant.RewardType;
|
||||
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.OrderRefundContext;
|
||||
import com.starry.admin.modules.order.module.dto.PaymentInfo;
|
||||
import com.starry.admin.modules.order.module.dto.RandomOrderRequirements;
|
||||
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
|
||||
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.service.IPlayCouponDetailsService;
|
||||
import com.starry.admin.modules.weichat.service.WxCustomMpService;
|
||||
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
|
||||
import com.starry.admin.modules.withdraw.service.IEarningsService;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
@@ -59,6 +78,76 @@ class OrderLifecycleServiceImplTest {
|
||||
@Mock
|
||||
private IPlayCustomUserInfoService customUserInfoService;
|
||||
|
||||
@Mock
|
||||
private IPlayCouponDetailsService playCouponDetailsService;
|
||||
|
||||
@Mock
|
||||
private ClerkRevenueCalculator clerkRevenueCalculator;
|
||||
|
||||
@Mock
|
||||
private PlayOrderLogInfoMapper orderLogInfoMapper;
|
||||
|
||||
@Test
|
||||
void initiateOrder_specifiedOrder_persistsAndUpdatesCoupon() {
|
||||
OrderCreationContext request = OrderCreationContext.builder()
|
||||
.orderId("order-init-001")
|
||||
.orderNo("NO20241001")
|
||||
.orderStatus(OrderStatus.PENDING)
|
||||
.orderType(OrderType.NORMAL)
|
||||
.placeType(PlaceType.SPECIFIED)
|
||||
.rewardType(RewardType.NOT_APPLICABLE)
|
||||
.isFirstOrder(true)
|
||||
.commodityInfo(CommodityInfo.builder()
|
||||
.commodityId("commodity-01")
|
||||
.commodityType(CommodityType.SERVICE)
|
||||
.commodityPrice(BigDecimal.valueOf(199))
|
||||
.serviceDuration("60")
|
||||
.commodityName("服务A")
|
||||
.commodityNumber("1")
|
||||
.build())
|
||||
.paymentInfo(PaymentInfo.builder()
|
||||
.orderMoney(BigDecimal.valueOf(199))
|
||||
.finalAmount(BigDecimal.valueOf(179))
|
||||
.discountAmount(BigDecimal.valueOf(20))
|
||||
.couponIds(Collections.singletonList("coupon-1"))
|
||||
.payMethod(PayMethod.WECHAT.getCode())
|
||||
.build())
|
||||
.purchaserBy("customer-1")
|
||||
.acceptBy("clerk-1")
|
||||
.weiChatCode("wx-001")
|
||||
.remark("备注")
|
||||
.build();
|
||||
|
||||
ClerkEstimatedRevenueVo revenueVo = new ClerkEstimatedRevenueVo();
|
||||
revenueVo.setRevenueAmount(BigDecimal.valueOf(89.5));
|
||||
revenueVo.setRevenueRatio(50);
|
||||
|
||||
when(orderInfoMapper.selectCount(any())).thenReturn(0L);
|
||||
when(orderInfoMapper.insert(any())).thenReturn(1);
|
||||
when(clerkRevenueCalculator.calculateEstimatedRevenue(
|
||||
anyString(), anyList(), anyString(), anyString(), any())).thenReturn(revenueVo);
|
||||
doNothing().when(customUserInfoService).saveOrderInfo(any());
|
||||
doNothing().when(playCouponDetailsService).updateCouponUseStateByIds(anyList(), anyString());
|
||||
|
||||
PlayOrderInfoEntity created = lifecycleService.initiateOrder(request);
|
||||
|
||||
verify(customUserInfoService).saveOrderInfo(created);
|
||||
verify(orderInfoMapper).insert(created);
|
||||
verify(playCouponDetailsService).updateCouponUseStateByIds(
|
||||
request.getPaymentInfo().getCouponIds(), CouponUseState.USED.getCode());
|
||||
verify(clerkRevenueCalculator).calculateEstimatedRevenue(
|
||||
request.getAcceptBy(),
|
||||
request.getPaymentInfo().getCouponIds(),
|
||||
request.getPlaceType().getCode(),
|
||||
YesNoFlag.YES.getCode(),
|
||||
request.getPaymentInfo().getFinalAmount());
|
||||
|
||||
assertEquals(YesNoFlag.YES.getCode(), created.getFirstOrder());
|
||||
assertEquals(revenueVo.getRevenueAmount(), created.getEstimatedRevenue());
|
||||
assertEquals(revenueVo.getRevenueRatio(), created.getEstimatedRevenueRatio());
|
||||
assertEquals(PayMethod.WECHAT.getCode(), created.getPayMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
void completeOrder_inProgress_createsEarningsAndNotifies() {
|
||||
String orderId = UUID.randomUUID().toString();
|
||||
@@ -69,20 +158,18 @@ class OrderLifecycleServiceImplTest {
|
||||
completed.setOrderEndTime(LocalDateTime.now());
|
||||
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(inProgress, completed);
|
||||
when(orderInfoMapper.updateById(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
when(orderInfoMapper.update(isNull(), any())).thenReturn(1);
|
||||
mockEarningsCounts(0L, 1L);
|
||||
|
||||
OrderCompletionContext context = OrderCompletionContext.of(
|
||||
OperatorType.CLERK.getCode(),
|
||||
OrderActor.CLERK,
|
||||
inProgress.getAcceptBy(),
|
||||
OrderTriggerSource.WX_CLERK);
|
||||
|
||||
lifecycleService.completeOrder(orderId, context);
|
||||
|
||||
verify(orderInfoMapper).updateById(argThat(entity ->
|
||||
orderId.equals(entity.getId())
|
||||
&& OrderStatus.COMPLETED.getCode().equals(entity.getOrderStatus())
|
||||
&& entity.getOrderEndTime() != null));
|
||||
verify(orderInfoMapper).update(isNull(), any());
|
||||
verify(customUserInfoService).handleOrderCompletion(completed);
|
||||
verify(earningsService).createFromOrder(completed);
|
||||
verify(wxCustomMpService).sendOrderFinishMessageAsync(completed);
|
||||
}
|
||||
@@ -96,17 +183,39 @@ class OrderLifecycleServiceImplTest {
|
||||
PlayOrderInfoEntity completedWithEnd = buildOrder(orderId, OrderStatus.COMPLETED.getCode());
|
||||
completedWithEnd.setOrderEndTime(LocalDateTime.now());
|
||||
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(alreadyCompleted, completedWithEnd);
|
||||
when(orderInfoMapper.updateById(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
mockEarningsCounts(1L);
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(alreadyCompleted, completedWithEnd, completedWithEnd);
|
||||
when(orderInfoMapper.update(isNull(), any())).thenReturn(1);
|
||||
|
||||
lifecycleService.completeOrder(orderId, OrderCompletionContext.of(
|
||||
OperatorType.ADMIN.getCode(),
|
||||
OrderActor.ADMIN,
|
||||
"admin-1",
|
||||
OrderTriggerSource.ADMIN_CONSOLE));
|
||||
|
||||
verify(earningsService, never()).createFromOrder(any());
|
||||
verify(wxCustomMpService).sendOrderFinishMessageAsync(completedWithEnd);
|
||||
verify(wxCustomMpService, never()).sendOrderFinishMessageAsync(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void completeOrder_whenTransitionAlreadyApplied_skipsSideEffects() {
|
||||
String orderId = UUID.randomUUID().toString();
|
||||
PlayOrderInfoEntity inProgress = buildOrder(orderId, OrderStatus.IN_PROGRESS.getCode());
|
||||
inProgress.setOrderEndTime(null);
|
||||
|
||||
PlayOrderInfoEntity completed = buildOrder(orderId, OrderStatus.COMPLETED.getCode());
|
||||
completed.setOrderEndTime(LocalDateTime.now());
|
||||
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(inProgress, completed, completed);
|
||||
when(orderInfoMapper.update(isNull(), any())).thenReturn(0);
|
||||
|
||||
lifecycleService.completeOrder(orderId, OrderCompletionContext.of(
|
||||
OrderActor.CLERK,
|
||||
inProgress.getAcceptBy(),
|
||||
OrderTriggerSource.WX_CLERK));
|
||||
|
||||
verify(customUserInfoService, never()).handleOrderCompletion(any());
|
||||
verify(earningsService, never()).createFromOrder(any());
|
||||
verify(wxCustomMpService, never()).sendOrderFinishMessageAsync(any());
|
||||
verify(orderLogInfoMapper, never()).insert(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,10 +228,10 @@ class OrderLifecycleServiceImplTest {
|
||||
order.setFinalAmount(finalAmount);
|
||||
order.setOrderMoney(finalAmount);
|
||||
order.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
order.setPayMethod("1");
|
||||
order.setPayMethod(PayMethod.WECHAT.getCode());
|
||||
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(order);
|
||||
when(orderInfoMapper.updateById(any(PlayOrderInfoEntity.class))).thenReturn(1);
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(order, order);
|
||||
when(orderInfoMapper.update(isNull(), any())).thenReturn(1);
|
||||
|
||||
PlayCustomUserInfoEntity customer = new PlayCustomUserInfoEntity();
|
||||
customer.setId(order.getPurchaserBy());
|
||||
@@ -139,12 +248,7 @@ class OrderLifecycleServiceImplTest {
|
||||
|
||||
lifecycleService.refundOrder(context);
|
||||
|
||||
ArgumentCaptor<PlayOrderInfoEntity> updateCaptor = ArgumentCaptor.forClass(PlayOrderInfoEntity.class);
|
||||
verify(orderInfoMapper).updateById(updateCaptor.capture());
|
||||
PlayOrderInfoEntity updated = updateCaptor.getValue();
|
||||
assertEquals(OrderStatus.CANCELLED.getCode(), updated.getOrderStatus());
|
||||
assertEquals(OrderRefundFlag.REFUNDED.getCode(), updated.getRefundType());
|
||||
assertEquals(refundAmount, updated.getRefundAmount());
|
||||
verify(orderInfoMapper).update(isNull(), any());
|
||||
|
||||
verify(customUserInfoService).updateAccountBalanceById(
|
||||
eq(order.getPurchaserBy()),
|
||||
@@ -186,10 +290,35 @@ class OrderLifecycleServiceImplTest {
|
||||
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
||||
|
||||
assertThrows(CustomException.class, () -> lifecycleService.refundOrder(context));
|
||||
verify(orderInfoMapper, never()).updateById(any());
|
||||
verify(orderInfoMapper, never()).update(isNull(), any());
|
||||
verify(orderRefundInfoService, never()).add(anyString(), anyString(), anyString(), anyString(), anyString(), any(), anyString(), anyString(), anyString(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void refundOrder_duplicateRequest_isIdempotent() {
|
||||
String orderId = UUID.randomUUID().toString();
|
||||
PlayOrderInfoEntity order = buildOrder(orderId, OrderStatus.ACCEPTED.getCode());
|
||||
order.setFinalAmount(BigDecimal.TEN);
|
||||
order.setRefundType(OrderRefundFlag.NOT_REFUNDED.getCode());
|
||||
|
||||
PlayOrderInfoEntity afterUpdate = buildOrder(orderId, OrderStatus.CANCELLED.getCode());
|
||||
afterUpdate.setRefundType(OrderRefundFlag.REFUNDED.getCode());
|
||||
|
||||
when(orderInfoMapper.selectById(orderId)).thenReturn(order, afterUpdate);
|
||||
when(orderInfoMapper.update(isNull(), any())).thenReturn(0);
|
||||
|
||||
OrderRefundContext context = new OrderRefundContext();
|
||||
context.setOrderId(orderId);
|
||||
context.setRefundAmount(BigDecimal.ONE);
|
||||
context.withTriggerSource(OrderTriggerSource.ADMIN_API);
|
||||
|
||||
lifecycleService.refundOrder(context);
|
||||
|
||||
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());
|
||||
verify(orderLogInfoMapper, never()).insert(any());
|
||||
}
|
||||
|
||||
private PlayOrderInfoEntity buildOrder(String orderId, String status) {
|
||||
PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
|
||||
entity.setId(orderId);
|
||||
|
||||
Reference in New Issue
Block a user