package com.tourguide.modules.order.service; import com.tourguide.common.exception.BusinessException; import com.tourguide.modules.order.domain.OrderStatus; import com.tourguide.modules.order.dto.GuideOrderResponse; import com.tourguide.modules.order.entity.TourOrder; import com.tourguide.modules.order.mapper.TourOrderMapper; import com.tourguide.modules.operation.entity.ScenicMeetingPoint; import com.tourguide.modules.operation.mapper.ScenicMeetingPointMapper; import com.tourguide.modules.operation.entity.ScenicPricing; import com.tourguide.modules.operation.mapper.ScenicPricingMapper; import com.tourguide.modules.user.entity.GuideReceivingSession; import com.tourguide.modules.user.mapper.GuideRealtimeStatusMapper; import com.tourguide.modules.user.mapper.GuideReceivingSessionMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class GuideTourOrderServiceTest { private TourOrderMapper orderMapper; private GuideReceivingSessionMapper sessionMapper; private GuideRealtimeStatusMapper realtimeStatusMapper; private ScenicPricingMapper pricingMapper; private ScenicMeetingPointMapper meetingPointMapper; private GuideTourOrderService service; @BeforeEach void setUp() { orderMapper = mock(TourOrderMapper.class); sessionMapper = mock(GuideReceivingSessionMapper.class); realtimeStatusMapper = mock(GuideRealtimeStatusMapper.class); pricingMapper = mock(ScenicPricingMapper.class); meetingPointMapper = mock(ScenicMeetingPointMapper.class); service = new GuideTourOrderService(orderMapper, sessionMapper, realtimeStatusMapper, pricingMapper, meetingPointMapper); } @Test void shouldStartAllOrdersInReceivingSession() { TourOrder order = order(OrderStatus.PENDING_MEETING); GuideReceivingSession session = session("ASSIGNED"); when(orderMapper.selectOne(any())).thenReturn(order); when(sessionMapper.selectForUpdate(20L, 8L)).thenReturn(session); when(sessionMapper.startSession(20L, 8L)).thenReturn(1); when(orderMapper.startSessionOrders(eq(20L), any())).thenReturn(2); GuideOrderResponse response = service.start(8L, 10L); assertThat(response.getStatus()).isEqualTo("serving"); assertThat(response.getLectureStartTime()).isNotNull(); assertThat(order.getArrivedAt()).isNotNull(); verify(realtimeStatusMapper).disableReceiving(8L); } @Test void shouldTreatRepeatedStartAsSuccess() { TourOrder order = order(OrderStatus.IN_SERVICE); when(orderMapper.selectOne(any())).thenReturn(order); GuideOrderResponse response = service.start(8L, 10L); assertThat(response.getStatus()).isEqualTo("serving"); verify(sessionMapper, never()).selectForUpdate(any(), any()); } @Test void shouldExposePaymentAmountSeparatelyFromGuideEarningForHistoryOrder() { TourOrder order = order(OrderStatus.COMPLETED); order.setAmount(new BigDecimal("300.00")); order.setPaymentAmount(new BigDecimal("300.00")); order.setGuideEarningAmount(new BigDecimal("256.00")); when(orderMapper.selectOne(any())).thenReturn(order); GuideOrderResponse response = service.detail(8L, 10L); assertThat(response.getPaymentAmount()).isEqualByComparingTo("300.00"); assertThat(response.getEarningAmount()).isEqualByComparingTo("256.00"); } @Test void shouldUseConfiguredDurationForOrder() { TourOrder order = order(OrderStatus.COMPLETED); order.setServicePackageId(30L); ScenicPricing pricing = new ScenicPricing(); pricing.setId(30L); pricing.setDurationMinutes(2); when(orderMapper.selectOne(any())).thenReturn(order); when(pricingMapper.selectById(30L)).thenReturn(pricing); GuideOrderResponse response = service.detail(8L, 10L); assertThat(response.getDurationMinutes()).isEqualTo(2); } @Test void shouldExposeMeetingPointCoordinatesForOrder() { TourOrder order = order(OrderStatus.COMPLETED); order.setScenicSpotId(11L); order.setMeetingPointId(12L); order.setMeetingPointName("景区正门"); ScenicMeetingPoint point = new ScenicMeetingPoint(); point.setId(12L); point.setScenicSpotId(11L); point.setPointName("景区正门"); point.setAddress("景区东门"); point.setLongitude(new BigDecimal("103.648278")); point.setLatitude(new BigDecimal("31.001472")); when(orderMapper.selectOne(any())).thenReturn(order); when(meetingPointMapper.selectBatchIds(any())).thenReturn(Collections.singletonList(point)); GuideOrderResponse response = service.detail(8L, 10L); assertThat(response.getMeetingPointId()).isEqualTo(12L); assertThat(response.getMeetingPointAddress()).isEqualTo("景区东门"); assertThat(response.getMeetingPointLongitude()).isEqualByComparingTo("103.648278"); assertThat(response.getMeetingPointLatitude()).isEqualByComparingTo("31.001472"); } @Test void shouldRejectOrderOutsidePendingMeetingState() { TourOrder order = order(OrderStatus.COMPLETED); when(orderMapper.selectOne(any())).thenReturn(order); assertThatThrownBy(() -> service.start(8L, 10L)) .isInstanceOf(BusinessException.class) .hasMessage("当前订单不能开始讲解"); } private TourOrder order(OrderStatus status) { TourOrder order = new TourOrder(); order.setId(10L); order.setGuideId(8L); order.setReceivingSessionId(20L); order.setStatus(status.getCode()); order.setPeopleCount(2); return order; } private GuideReceivingSession session(String status) { GuideReceivingSession session = new GuideReceivingSession(); session.setId(20L); session.setGuideId(8L); session.setStatus(status); return session; } }