package com.tourguide.modules.finance.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.tourguide.modules.finance.dto.FinanceLedgerPageResponse; import com.tourguide.modules.finance.dto.FinanceLedgerResponse; import com.tourguide.modules.finance.mapper.GuideWithdrawalMapper; import com.tourguide.modules.operation.dto.PlatformSettingsResponse; import com.tourguide.modules.operation.service.PlatformSettingsService; import com.tourguide.modules.order.entity.TourOrder; import com.tourguide.modules.order.entity.TourOrderRefund; import com.tourguide.modules.order.mapper.TourOrderMapper; import com.tourguide.modules.order.mapper.TourOrderRefundMapper; import com.tourguide.modules.user.entity.GuideUser; import com.tourguide.modules.user.mapper.GuideTeamMapper; import com.tourguide.modules.user.mapper.GuideUserMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Arrays; import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class AdminFinanceServiceTest { private TourOrderMapper orderMapper; private GuideUserMapper guideUserMapper; private PlatformSettingsService settingsService; private TourOrderRefundMapper refundMapper; private AdminFinanceService service; @BeforeEach void setUp() { orderMapper = mock(TourOrderMapper.class); guideUserMapper = mock(GuideUserMapper.class); settingsService = mock(PlatformSettingsService.class); refundMapper = mock(TourOrderRefundMapper.class); service = new AdminFinanceService( orderMapper, mock(GuideWithdrawalMapper.class), guideUserMapper, mock(GuideTeamMapper.class), settingsService, mock(GuideWithdrawalService.class), refundMapper); PlatformSettingsResponse settings = new PlatformSettingsResponse(); settings.setCommissionRate(new BigDecimal("15.00")); when(settingsService.getSettings()).thenReturn(settings); } @Test void ledgerShouldUseGuideAccountWhenOrderGuideSnapshotIsMissing() { TourOrder order = paidOrder(); order.setGuideId(8L); Page page = pageOf(order); when(orderMapper.selectPage(any(Page.class), any())).thenReturn(page); when(orderMapper.selectList(any())).thenReturn(Collections.singletonList(order)); GuideUser guide = new GuideUser(); guide.setId(8L); guide.setRealName("张三"); guide.setMobile("13800000000"); when(guideUserMapper.selectBatchIds(any())).thenReturn(Collections.singletonList(guide)); FinanceLedgerPageResponse response = service.ledger(1, 10, "INCOME", null, null, null, null); FinanceLedgerResponse ledger = response.getPage().getRecords().get(0); assertThat(ledger.getGuideName()).isEqualTo("张三"); assertThat(ledger.getGuideMobile()).isEqualTo("13800000000"); assertThat(ledger.getGuideTeam()).isEqualTo("个人导游"); } @Test void ledgerShouldPreferOrderGuideSnapshotOverCurrentGuideAccount() { TourOrder order = paidOrder(); order.setGuideId(8L); order.setGuideName("历史姓名"); order.setGuideMobile("13900000000"); Page page = pageOf(order); when(orderMapper.selectPage(any(Page.class), any())).thenReturn(page); when(orderMapper.selectList(any())).thenReturn(Collections.singletonList(order)); GuideUser guide = new GuideUser(); guide.setId(8L); guide.setRealName("当前姓名"); guide.setMobile("13800000000"); when(guideUserMapper.selectBatchIds(any())).thenReturn(Collections.singletonList(guide)); FinanceLedgerResponse ledger = service.ledger(1, 10, "INCOME", null, null, null, null) .getPage().getRecords().get(0); assertThat(ledger.getGuideName()).isEqualTo("历史姓名"); assertThat(ledger.getGuideMobile()).isEqualTo("13900000000"); } @Test void refundLedgerShouldExposeEveryRefundRecord() { TourOrder order = paidOrder(); order.setRefundAmount(new BigDecimal("40.00")); order.setRefundedAmount(new BigDecimal("100.00")); when(orderMapper.selectList(any())).thenReturn(Collections.singletonList(order)); TourOrderRefund first = refundRecord(1L, order.getId(), "RF001", "40.00", LocalDateTime.of(2026, 9, 4, 15, 0)); TourOrderRefund second = refundRecord(2L, order.getId(), "RF002", "60.00", LocalDateTime.of(2026, 9, 4, 16, 0)); when(refundMapper.selectByOrderIds(any())).thenReturn(Arrays.asList(first, second)); FinanceLedgerPageResponse response = service.ledger(1, 10, "REFUND", null, null, null, null); assertThat(response.getPage().getTotal()).isEqualTo(2); assertThat(response.getPage().getRecords()).extracting(FinanceLedgerResponse::getFlowNo) .containsExactly("RF002", "RF001"); assertThat(response.getPage().getRecords()).extracting(FinanceLedgerResponse::getFlowAmount) .containsExactly(new BigDecimal("-60.00"), new BigDecimal("-40.00")); assertThat(response.getTotalAmount()).isEqualByComparingTo("-100.00"); } private Page pageOf(TourOrder order) { Page page = new Page(1, 10); page.setRecords(Collections.singletonList(order)); page.setTotal(1); return page; } private TourOrder paidOrder() { TourOrder order = new TourOrder(); order.setId(99L); order.setOrderNo("202609040001"); order.setAmount(new BigDecimal("100.00")); order.setPaymentAmount(new BigDecimal("100.00")); order.setPaymentMethod("WECHAT"); order.setPaymentOrderNo("PAY001"); order.setPaymentTime(LocalDateTime.of(2026, 9, 4, 14, 19)); return order; } private TourOrderRefund refundRecord(Long id, Long orderId, String refundOrderNo, String amount, LocalDateTime refundTime) { TourOrderRefund refund = new TourOrderRefund(); refund.setId(id); refund.setOrderId(orderId); refund.setRefundOrderNo(refundOrderNo); refund.setRefundAmount(new BigDecimal(amount)); refund.setRefundStatus("REFUNDED"); refund.setRefundMethod("WECHAT"); refund.setRefundTime(refundTime); return refund; } }