| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- package com.ruoyi.app.pay;
- import com.baomidou.mybatisplus.core.conditions.Wrapper;
- import com.ruoyi.app.omgpay.OmgPaymentCreateOutcome;
- import com.ruoyi.app.omgpay.OmgPaymentCreateService;
- import com.ruoyi.app.omgpay.OmgPaymentRetryService;
- import com.ruoyi.app.omgpay.OmgPaymentMethod;
- import com.ruoyi.app.omgpay.dto.OmgCreatePaymentResponse;
- import com.ruoyi.app.pay.dto.PayMethodSwitchRequest;
- import com.ruoyi.common.core.domain.AjaxResult;
- import com.ruoyi.common.exception.ServiceException;
- import com.ruoyi.common.utils.spring.SpringUtils;
- import com.ruoyi.system.domain.InfoUser;
- import com.ruoyi.system.domain.PosOrder;
- import com.ruoyi.system.service.IInfoUserService;
- import com.ruoyi.system.service.IPosOrderService;
- import com.ruoyi.system.utils.OrderLogHelper;
- import org.junit.jupiter.api.AfterAll;
- import org.junit.jupiter.api.BeforeAll;
- import org.junit.jupiter.api.Test;
- import org.mockito.ArgumentCaptor;
- import org.mockito.InOrder;
- import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
- import org.springframework.context.support.StaticMessageSource;
- import org.springframework.test.util.ReflectionTestUtils;
- import java.util.Map;
- import static org.junit.jupiter.api.Assertions.assertEquals;
- import static org.junit.jupiter.api.Assertions.assertNotNull;
- import static org.junit.jupiter.api.Assertions.assertNull;
- 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.Mockito.inOrder;
- import static org.mockito.Mockito.mock;
- import static org.mockito.Mockito.never;
- import static org.mockito.Mockito.times;
- import static org.mockito.Mockito.verify;
- import static org.mockito.Mockito.when;
- /**
- * 待支付订单更换支付方式(#661)服务层测试。
- * 覆盖:线下方式回写与日志、外送单拒线下、跨渠道委派与先回写后拉起的顺序、
- * 同方式不重复回写、已支付/非法方式/非本人单/多门店子单拒绝。
- */
- class PayMethodSwitchServiceTest {
- private static ConfigurableListableBeanFactory originalBeanFactory;
- @BeforeAll
- static void initializeMessages() {
- originalBeanFactory = (ConfigurableListableBeanFactory)
- ReflectionTestUtils.getField(SpringUtils.class, "beanFactory");
- // MessageUtils 取 key 本身,断言直接比对 i18n key
- StaticMessageSource messageSource = new StaticMessageSource();
- messageSource.setUseCodeAsDefaultMessage(true);
- org.springframework.beans.factory.support.DefaultListableBeanFactory beanFactory =
- new org.springframework.beans.factory.support.DefaultListableBeanFactory();
- beanFactory.registerSingleton("messageSource", messageSource);
- new SpringUtils().postProcessBeanFactory(beanFactory);
- }
- @AfterAll
- static void restoreMessages() {
- new SpringUtils().postProcessBeanFactory(originalBeanFactory);
- }
- /** 待支付 LINE Pay 自取单:ddId=dd-1、userId=7、type=1、payType=3 */
- private static PosOrder baseOrder() {
- PosOrder order = new PosOrder();
- order.setDdId("dd-1");
- order.setParentDdId("dd-1");
- order.setUserId(7L);
- order.setState(0L);
- order.setPayStatus(0L);
- order.setAmount(90);
- order.setType(1L);
- order.setPayType("3");
- return order;
- }
- private static PayMethodSwitchRequest request(String orderId, String method) {
- PayMethodSwitchRequest request = new PayMethodSwitchRequest();
- request.setOrderId(orderId);
- request.setPaymentMethod(method);
- return request;
- }
- @Test
- void switchLinePayOrderToOfflineUpdatesPayTypeWritesLogAndSkipsPayment() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- OmgPaymentRetryService omgPaymentRetryService = mock(OmgPaymentRetryService.class);
- LinePayService linePayService = mock(LinePayService.class);
- OrderLogHelper orderLogHelper = mock(OrderLogHelper.class);
- IInfoUserService infoUserService = mock(IInfoUserService.class);
- InfoUser user = new InfoUser();
- user.setUserId(7L);
- user.setNickName("小明");
- when(infoUserService.getById(7L)).thenReturn(user);
- PosOrder order = baseOrder();
- when(orderService.getOne(any(Wrapper.class))).thenReturn(order);
- when(orderService.updateById(any(PosOrder.class))).thenReturn(true);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- omgPaymentRetryService, linePayService, orderLogHelper, infoUserService);
- AjaxResult result = service.switchMethod(7L, request("dd-1", "1"));
- ArgumentCaptor<PosOrder> saved = ArgumentCaptor.forClass(PosOrder.class);
- verify(orderService).updateById(saved.capture());
- assertEquals("1", saved.getValue().getPayType(), "应回写为目标方式货到付款");
- verify(orderLogHelper).log(eq("dd-1"), eq(4), eq(7L), eq("小明"), anyString());
- verify(linePayService, never()).create(any(), anyString());
- assertEquals("1", ((Map<?, ?>) result.get("data")).get("payType"));
- assertNull(((Map<?, ?>) result.get("data")).get("payParams"), "到付无支付环节,payParams 应为空");
- }
- @Test
- void switchDeliveryOrderToCashIsRejected() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- PosOrder order = baseOrder();
- order.setType(0L);
- when(orderService.getOne(any(Wrapper.class))).thenReturn(order);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- mock(OmgPaymentRetryService.class), mock(LinePayService.class),
- mock(OrderLogHelper.class), mock(IInfoUserService.class));
- ServiceException exception = assertThrows(ServiceException.class,
- () -> service.switchMethod(7L, request("dd-1", "4")));
- assertEquals("no.pay.switch.offline.restricted", exception.getMessage());
- verify(orderService, never()).updateById(any(PosOrder.class));
- }
- @Test
- @SuppressWarnings({"unchecked", "rawtypes"})
- void switchToCreditCardRewritesPayTypeBeforeOmgCreate() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- OmgPaymentRetryService omgPaymentRetryService = mock(OmgPaymentRetryService.class);
- when(omgPaymentRetryService.retry(eq(7L), eq("dd-1"), eq(OmgPaymentMethod.CREDIT)))
- .thenReturn(new OmgPaymentCreateOutcome(mock(OmgCreatePaymentResponse.class), 1L, "dd-1", 7L, 5L, 90, "abcde***fghi"));
- PosOrder order = baseOrder();
- when(orderService.getOne(any(Wrapper.class))).thenReturn(order);
- when(orderService.updateById(any(PosOrder.class))).thenReturn(true);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- omgPaymentRetryService, mock(LinePayService.class), mock(OrderLogHelper.class),
- mock(IInfoUserService.class));
- AjaxResult result = service.switchMethod(7L, request("dd-1", "2"));
- // 顺序约束:先回写 payType 再拉起渠道;走 retry 路径:旧 Apple Pay 尝试仍激活时
- // 替换重建而非 PAYMENT_ATTEMPT_EXISTS 死锁(组内 5->2 切换线上事故场景)
- InOrder inOrder = inOrder(orderService, omgPaymentRetryService);
- inOrder.verify(orderService).updateById(any(PosOrder.class));
- inOrder.verify(omgPaymentRetryService).retry(eq(7L), eq("dd-1"), eq(OmgPaymentMethod.CREDIT));
- Map<?, ?> data = (Map<?, ?>) result.get("data");
- assertEquals("2", data.get("payType"));
- assertNotNull(data.get("payParams"), "在线方式应带渠道支付参数");
- }
- @Test
- void sameMethodSkipsRewriteButStillDelegates() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- OmgPaymentRetryService omgPaymentRetryService = mock(OmgPaymentRetryService.class);
- when(omgPaymentRetryService.retry(any(), anyString(), any(OmgPaymentMethod.class)))
- .thenReturn(new OmgPaymentCreateOutcome(mock(OmgCreatePaymentResponse.class), 1L, "dd-1", 7L, 5L, 90, "abcde***fghi"));
- PosOrder order = baseOrder();
- order.setPayType("2");
- when(orderService.getOne(any(Wrapper.class))).thenReturn(order);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- omgPaymentRetryService, mock(LinePayService.class), mock(OrderLogHelper.class),
- mock(IInfoUserService.class));
- service.switchMethod(7L, request("dd-1", "2"));
- verify(orderService, never()).updateById(any(PosOrder.class));
- verify(omgPaymentRetryService, times(1)).retry(eq(7L), eq("dd-1"), eq(OmgPaymentMethod.CREDIT));
- }
- @Test
- void alreadyPaidOrderIsRejected() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- PosOrder order = baseOrder();
- order.setPayStatus(1L);
- when(orderService.getOne(any(Wrapper.class))).thenReturn(order);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- mock(OmgPaymentRetryService.class), mock(LinePayService.class),
- mock(OrderLogHelper.class), mock(IInfoUserService.class));
- ServiceException exception = assertThrows(ServiceException.class,
- () -> service.switchMethod(7L, request("dd-1", "2")));
- assertEquals("no.pay.switch.already.paid", exception.getMessage());
- }
- @Test
- void unknownMethodIsRejectedBeforeOrderLookup() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- mock(OmgPaymentRetryService.class), mock(LinePayService.class),
- mock(OrderLogHelper.class), mock(IInfoUserService.class));
- ServiceException exception = assertThrows(ServiceException.class,
- () -> service.switchMethod(7L, request("dd-1", "9")));
- assertEquals("no.pay.switch.method.invalid", exception.getMessage());
- verify(orderService, never()).getOne(any(Wrapper.class));
- }
- @Test
- void orderOfAnotherUserIsRejected() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- PosOrder order = baseOrder();
- order.setUserId(8L);
- when(orderService.getOne(any(Wrapper.class))).thenReturn(order);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- mock(OmgPaymentRetryService.class), mock(LinePayService.class),
- mock(OrderLogHelper.class), mock(IInfoUserService.class));
- ServiceException exception = assertThrows(ServiceException.class,
- () -> service.switchMethod(7L, request("dd-1", "2")));
- assertEquals("no.pay.switch.order.invalid", exception.getMessage());
- }
- @Test
- void multiStoreSubOrderIsRejected() {
- IPosOrderService orderService = mock(IPosOrderService.class);
- PosOrder order = baseOrder();
- order.setParentDdId("dd-parent");
- when(orderService.getOne(any(Wrapper.class))).thenReturn(order);
- PayMethodSwitchService service = new PayMethodSwitchService(orderService,
- mock(OmgPaymentRetryService.class), mock(LinePayService.class),
- mock(OrderLogHelper.class), mock(IInfoUserService.class));
- ServiceException exception = assertThrows(ServiceException.class,
- () -> service.switchMethod(7L, request("dd-1", "2")));
- assertEquals("no.pay.switch.order.invalid", exception.getMessage());
- }
- }
|