|
@@ -11,6 +11,7 @@ import com.ruoyi.app.flashdelivery.dto.FlashDeliveryCreateRequest;
|
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryProofRequest;
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryProofRequest;
|
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryPricingRequest;
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryPricingRequest;
|
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryQuoteRequest;
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryQuoteRequest;
|
|
|
|
|
+import com.ruoyi.app.flashdelivery.dto.FlashDeliveryReasonRequest;
|
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryDeliverRequest;
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryDeliverRequest;
|
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryRiderOrderPageView;
|
|
import com.ruoyi.app.flashdelivery.dto.FlashDeliveryRiderOrderPageView;
|
|
|
import com.ruoyi.app.flashdelivery.exception.FlashDeliveryQuoteChangedException;
|
|
import com.ruoyi.app.flashdelivery.exception.FlashDeliveryQuoteChangedException;
|
|
@@ -68,6 +69,7 @@ import static org.mockito.Mockito.when;
|
|
|
import static org.mockito.Mockito.doAnswer;
|
|
import static org.mockito.Mockito.doAnswer;
|
|
|
import static org.mockito.Mockito.inOrder;
|
|
import static org.mockito.Mockito.inOrder;
|
|
|
import static org.mockito.Mockito.doThrow;
|
|
import static org.mockito.Mockito.doThrow;
|
|
|
|
|
+import static org.mockito.Mockito.verifyNoInteractions;
|
|
|
|
|
|
|
|
class FlashDeliveryApplicationServiceTest {
|
|
class FlashDeliveryApplicationServiceTest {
|
|
|
private static ConfigurableListableBeanFactory originalBeanFactory;
|
|
private static ConfigurableListableBeanFactory originalBeanFactory;
|
|
@@ -691,6 +693,163 @@ class FlashDeliveryApplicationServiceTest {
|
|
|
verify(fixture.orderMapper, never()).accept(any(), any(), any());
|
|
verify(fixture.orderMapper, never()).accept(any(), any(), any());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void acceptSuccessNotifiesSenderOfOrder() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ rider(fixture, 8L);
|
|
|
|
|
+ FlashDeliveryOrder waiting = order(10L, "WAITING_ACCEPTANCE");
|
|
|
|
|
+ FlashDeliveryOrder accepted = order(10L, ACCEPTED);
|
|
|
|
|
+ accepted.setRiderId(8L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(waiting, accepted);
|
|
|
|
|
+ when(fixture.orderMapper.accept(eq(10L), eq(8L), any(Date.class))).thenReturn(1);
|
|
|
|
|
+
|
|
|
|
|
+ fixture.service.accept(8L, 10L);
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService).notifyAccepted(waiting);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void acceptFailureDoesNotTriggerNotification() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ rider(fixture, 8L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(order(10L, "WAITING_ACCEPTANCE"));
|
|
|
|
|
+ when(fixture.orderMapper.accept(eq(10L), eq(8L), any(Date.class))).thenReturn(0);
|
|
|
|
|
+
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> fixture.service.accept(8L, 10L));
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService, never()).notifyAccepted(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void pickupSuccessNotifiesSenderOfOrder() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ rider(fixture, 8L);
|
|
|
|
|
+ FlashDeliveryOrder accepted = order(10L, ACCEPTED);
|
|
|
|
|
+ accepted.setRiderId(8L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(accepted);
|
|
|
|
|
+ when(fixture.orderMapper.transitionByRider(eq(10L), eq(8L), eq("ACCEPTED"),
|
|
|
|
|
+ eq("PICKED_UP"), eq("picked_up_at"), any())).thenReturn(1);
|
|
|
|
|
+ FlashDeliveryProofRequest request = new FlashDeliveryProofRequest();
|
|
|
|
|
+ request.setImageUrls(List.of("https://example.com/pickup.jpg"));
|
|
|
|
|
+
|
|
|
|
|
+ fixture.service.pickup(8L, 10L, request);
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService).notifyPickedUp(accepted);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void deliverSuccessNotifiesSenderOfOrder() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ rider(fixture, 8L);
|
|
|
|
|
+ FlashDeliveryOrder pickedUp = order(10L, "PICKED_UP");
|
|
|
|
|
+ pickedUp.setRiderId(8L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(pickedUp);
|
|
|
|
|
+ when(fixture.orderMapper.transitionByRider(eq(10L), eq(8L), eq("PICKED_UP"),
|
|
|
|
|
+ eq("DELIVERED"), eq("delivered_at"), any())).thenReturn(1);
|
|
|
|
|
+ FlashDeliveryDeliverRequest request = new FlashDeliveryDeliverRequest();
|
|
|
|
|
+ request.setPinCode("4821");
|
|
|
|
|
+ request.setImageUrls(List.of("https://example.com/delivery.jpg"));
|
|
|
|
|
+
|
|
|
|
|
+ fixture.service.deliver(8L, 10L, request);
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService).notifyDelivered(pickedUp);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void proofTransitionFailureDoesNotTriggerNotification() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ rider(fixture, 8L);
|
|
|
|
|
+ FlashDeliveryOrder accepted = order(10L, ACCEPTED);
|
|
|
|
|
+ accepted.setRiderId(8L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(accepted);
|
|
|
|
|
+ when(fixture.orderMapper.transitionByRider(eq(10L), eq(8L), eq("ACCEPTED"),
|
|
|
|
|
+ eq("PICKED_UP"), eq("picked_up_at"), any())).thenReturn(0);
|
|
|
|
|
+ FlashDeliveryProofRequest request = new FlashDeliveryProofRequest();
|
|
|
|
|
+ request.setImageUrls(List.of("https://example.com/pickup.jpg"));
|
|
|
|
|
+
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> fixture.service.pickup(8L, 10L, request));
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService, never()).notifyPickedUp(any());
|
|
|
|
|
+ verify(fixture.notificationService, never()).notifyDelivered(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void userCancelSuccessNotifiesAssignedRider() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ FlashDeliveryOrder accepted = order(10L, ACCEPTED);
|
|
|
|
|
+ accepted.setRiderId(9L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(accepted);
|
|
|
|
|
+ when(fixture.orderMapper.cancel(eq(10L), eq("ACCEPTED"), eq("USER"), eq(7L),
|
|
|
|
|
+ anyString(), any(Date.class))).thenReturn(1);
|
|
|
|
|
+
|
|
|
|
|
+ fixture.service.userCancel(7L, 10L, reason("改行程"));
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService).notifyCancelled(accepted);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void adminCancelSuccessNotifiesAssignedRider() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ FlashDeliveryOrder pickedUp = order(10L, "PICKED_UP");
|
|
|
|
|
+ pickedUp.setRiderId(9L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(pickedUp);
|
|
|
|
|
+ when(fixture.orderMapper.cancel(eq(10L), eq("PICKED_UP"), eq("ADMIN"), eq(1L),
|
|
|
|
|
+ anyString(), any(Date.class))).thenReturn(1);
|
|
|
|
|
+
|
|
|
|
|
+ fixture.service.adminCancel(1L, 10L, reason("异常收口"));
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService).notifyCancelled(pickedUp);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void cancelFailureDoesNotTriggerNotification() {
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ FlashDeliveryOrder accepted = order(10L, ACCEPTED);
|
|
|
|
|
+ accepted.setRiderId(9L);
|
|
|
|
|
+ when(fixture.orderMapper.selectById(10L)).thenReturn(accepted);
|
|
|
|
|
+ when(fixture.orderMapper.cancel(eq(10L), eq("ACCEPTED"), eq("USER"), eq(7L),
|
|
|
|
|
+ anyString(), any(Date.class))).thenReturn(0);
|
|
|
|
|
+
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> fixture.service.userCancel(7L, 10L, reason("改行程")));
|
|
|
|
|
+
|
|
|
|
|
+ verify(fixture.notificationService, never()).notifyCancelled(any());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void excludedEventsNeverTriggerNotification() {
|
|
|
|
|
+ // FR-010:创建、加小费、完成、过期预约自动取消四类事件不产生任何推送。
|
|
|
|
|
+ Fixture fixture = new Fixture();
|
|
|
|
|
+ when(fixture.pricingMapper.selectAtTime(anyString(), anyInt())).thenReturn(List.of(pricing()));
|
|
|
|
|
+ when(fixture.routeService.calculate(any(), any(), anyInt())).thenReturn(new RouteDistance(3200, 600, "ROUTE"));
|
|
|
|
|
+ when(fixture.userMapper.selectOrdinaryUserIdsByNormalizedPhone("0922222222")).thenReturn(List.of());
|
|
|
|
|
+ doAnswer(invocation -> {
|
|
|
|
|
+ FlashDeliveryOrder order = invocation.getArgument(0);
|
|
|
|
|
+ order.setId(99L);
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }).when(fixture.orderMapper).insert(any(FlashDeliveryOrder.class));
|
|
|
|
|
+
|
|
|
|
|
+ fixture.service.create(7L, createRequest());
|
|
|
|
|
+
|
|
|
|
|
+ editableOrder(fixture);
|
|
|
|
|
+ when(fixture.orderMapper.updateWaitingTip(any(), eq(2))).thenReturn(1);
|
|
|
|
|
+ fixture.service.addTip(7L, 10L, tipRequest(20L, 2));
|
|
|
|
|
+
|
|
|
|
|
+ when(fixture.orderMapper.transitionByStatus(eq(10L), eq("DELIVERED"), eq("COMPLETED"),
|
|
|
|
|
+ eq("completed_at"), any(Date.class))).thenReturn(1);
|
|
|
|
|
+ assertTrue(fixture.service.complete(1L, "ADMIN", 10L));
|
|
|
|
|
+
|
|
|
|
|
+ when(fixture.orderMapper.cancelExpiredScheduled(eq(10L), any(Date.class))).thenReturn(1);
|
|
|
|
|
+ assertTrue(fixture.service.cancelExpiredScheduledOrder(10L, new Date()));
|
|
|
|
|
+
|
|
|
|
|
+ verifyNoInteractions(fixture.notificationService);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private FlashDeliveryReasonRequest reason(String text) {
|
|
|
|
|
+ FlashDeliveryReasonRequest request = new FlashDeliveryReasonRequest();
|
|
|
|
|
+ request.setReason(text);
|
|
|
|
|
+ return request;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Test
|
|
@Test
|
|
|
void oversizedProofUrlIsRejectedBeforeStateTransition() {
|
|
void oversizedProofUrlIsRejectedBeforeStateTransition() {
|
|
|
Fixture fixture = new Fixture();
|
|
Fixture fixture = new Fixture();
|
|
@@ -1205,6 +1364,7 @@ class FlashDeliveryApplicationServiceTest {
|
|
|
return image;
|
|
return image;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
@Test
|
|
@Test
|
|
|
void missingOrInvalidBodyOrderIdIsRejectedBeforeDatabaseAccess() {
|
|
void missingOrInvalidBodyOrderIdIsRejectedBeforeDatabaseAccess() {
|
|
|
Fixture fixture = new Fixture();
|
|
Fixture fixture = new Fixture();
|
|
@@ -1513,9 +1673,11 @@ class FlashDeliveryApplicationServiceTest {
|
|
|
final FlashDeliveryRouteService routeService = mock(FlashDeliveryRouteService.class);
|
|
final FlashDeliveryRouteService routeService = mock(FlashDeliveryRouteService.class);
|
|
|
final RiderDeliveryLockService lockService = mock(RiderDeliveryLockService.class);
|
|
final RiderDeliveryLockService lockService = mock(RiderDeliveryLockService.class);
|
|
|
final RiderDeliveryExclusivityService exclusivityService = mock(RiderDeliveryExclusivityService.class);
|
|
final RiderDeliveryExclusivityService exclusivityService = mock(RiderDeliveryExclusivityService.class);
|
|
|
|
|
+ final FlashDeliveryNotificationService notificationService = mock(FlashDeliveryNotificationService.class);
|
|
|
final FlashDeliveryApplicationService service = new FlashDeliveryApplicationService(
|
|
final FlashDeliveryApplicationService service = new FlashDeliveryApplicationService(
|
|
|
orderMapper, pricingMapper, imageMapper, logMapper, userMapper,
|
|
orderMapper, pricingMapper, imageMapper, logMapper, userMapper,
|
|
|
- routeService, new FlashDeliveryPricingCalculator(), lockService, exclusivityService);
|
|
|
|
|
|
|
+ routeService, new FlashDeliveryPricingCalculator(), lockService, exclusivityService,
|
|
|
|
|
+ notificationService);
|
|
|
|
|
|
|
|
Fixture() {
|
|
Fixture() {
|
|
|
when(lockService.withLock(any(), any())).thenAnswer(invocation -> {
|
|
when(lockService.withLock(any(), any())).thenAnswer(invocation -> {
|