package com.ruoyi.app.order; import com.alibaba.fastjson2.JSON; import com.ruoyi.app.order.dto.OrderCreateInput; import com.ruoyi.app.order.dto.OrderCreatItem; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.MessageUtils; import com.ruoyi.system.domain.*; import com.ruoyi.system.service.*; import com.ruoyi.system.utils.AuthContext; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.*; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; class MerchantCashOrderNotificationTest { private final PosOrderShOprateController controller = new PosOrderShOprateController(); private final IPosOrderService orders = mock(IPosOrderService.class); private final IOrderParentService parents = mock(IOrderParentService.class); private final MerchantNotificationRouter router = mock(MerchantNotificationRouter.class); private MockedStatic auth; private MockedStatic messages; @BeforeEach void setup() { auth = mockStatic(AuthContext.class); auth.when(AuthContext::requireUserId).thenReturn(101L); messages = mockStatic(MessageUtils.class); messages.when(() -> MessageUtils.message(anyString())).thenAnswer(i -> i.getArgument(0)); InfoUser merchant = new InfoUser(); merchant.setUserId(101L); merchant.setUserType("1"); IInfoUserService users = mock(IInfoUserService.class); when(users.getOne(any())).thenReturn(merchant); IPosStoreService stores = mock(IPosStoreService.class); PosStore store = new PosStore(); store.setId(9); store.setUserId(101L); when(stores.getById(9L)).thenReturn(store); when(stores.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class))).thenReturn(List.of(store)); OrderNumberReservationService reservations = mock(OrderNumberReservationService.class); when(reservations.reserveIfUnused(anyString(), anyString())).thenReturn(true); ReflectionTestUtils.setField(controller, "orderNumberReservations", reservations); ReflectionTestUtils.setField(controller, "infoUserService", users); ReflectionTestUtils.setField(controller, "posStoreService", stores); ReflectionTestUtils.setField(controller, "posOrderService", orders); ReflectionTestUtils.setField(controller, "orderParentService", parents); ReflectionTestUtils.setField(controller, "operatingHoursService", mock(IOperatingHoursService.class)); ReflectionTestUtils.setField(controller, "selfDeliveryService", mock(SelfDeliveryService.class)); ReflectionTestUtils.setField(controller, "stallQualificationGateService", mock(StallQualificationGateService.class)); ReflectionTestUtils.setField(controller, "merchantNotificationRouter", router); when(orders.save(any())).thenReturn(true); when(parents.save(any())).thenReturn(true); TransactionSynchronizationManager.initSynchronization(); } @AfterEach void cleanup() { TransactionSynchronizationManager.clearSynchronization(); auth.close(); messages.close(); } @ParameterizedTest @ValueSource(longs = {0, 1, 2}) void cashNotifiesOnlyAfterCommitWithFullOrderNumber(long type) { controller.createOrder("token", input("4", type)); verifyNoInteractions(router); commit(); ArgumentCaptor body = ArgumentCaptor.forClass(String.class); verify(router).sendStoreNewOrder(eq(9L), eq(101L), eq("no.message.push.message"), eq("no.message.push.new.order"), body.capture(), eq("DD-CASH-100"), eq(type)); assertEquals("DD-CASH-100", JSON.parseObject(body.getValue()).getString("ddId")); assertEquals(0, JSON.parseObject(body.getValue()).getIntValue("type")); assertEquals(1, JSON.parseObject(body.getValue()).getIntValue("pushType")); ArgumentCaptor saved = ArgumentCaptor.forClass(PosOrder.class); verify(orders).save(saved.capture()); assertEquals(0L, saved.getValue().getPayStatus()); verifyNoMoreInteractions(router); } @Test void rollbackDoesNotNotify() { controller.createOrder("token", input("4", 2)); TransactionSynchronizationManager.getSynchronizations().forEach( s -> s.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK)); verifyNoInteractions(router); } @Test void failedSaveDoesNotNotify() { when(orders.save(any())).thenReturn(false); assertThrows(ServiceException.class, () -> controller.createOrder("token", input("4", 2))); commit(); verifyNoInteractions(router); } @Test void notificationFailureDoesNotFailCommittedCreation() { doThrow(new IllegalStateException("push unavailable")).when(router).sendStoreNewOrder( anyLong(), anyLong(), anyString(), anyString(), anyString(), anyString(), anyLong()); controller.createOrder("token", input("4", 2)); assertDoesNotThrow(this::commit); verify(router).sendStoreNewOrder(anyLong(), anyLong(), anyString(), anyString(), anyString(), anyString(), anyLong()); } @ParameterizedTest @ValueSource(strings = {"1", "2", "3", "5", "6"}) void otherPaymentMethodsDoNotSendCashNotification(String method) { controller.createOrder("token", input(method, 2)); commit(); verifyNoInteractions(router); } private void commit() { TransactionSynchronizationManager.getSynchronizations().forEach(TransactionSynchronization::afterCommit); } private OrderCreateInput input(String method, long type) { OrderCreatItem item = new OrderCreatItem(); item.setMdId(9L); item.setShId(101L); item.setAmount(280); item.setType(type); OrderCreateInput input = new OrderCreateInput(); input.setDdId("DD-CASH-100"); input.setPaymentMethod(method); input.setItems(List.of(item)); return input; } }