| | |
| | | package com.tourguide.modules.finance.service; |
| | | |
| | | import com.tourguide.common.exception.BusinessException; |
| | | import com.tourguide.modules.order.entity.TourOrder; |
| | | import com.tourguide.modules.order.mapper.TourOrderMapper; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | |
| | | import java.time.LocalDateTime; |
| | | |
| | | import static org.assertj.core.api.Assertions.assertThat; |
| | | import static org.assertj.core.api.Assertions.assertThatThrownBy; |
| | | import static org.mockito.Mockito.mock; |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.verify; |
| | |
| | | } |
| | | |
| | | @Test |
| | | void shouldSettleRemainingEarningForPartialRefund() { |
| | | LocalDateTime now = LocalDateTime.of(2026, 9, 8, 12, 0); |
| | | TourOrder order = dueOrder(now.minusMinutes(1)); |
| | | order.setAmount(new BigDecimal("100.00")); |
| | | order.setPaymentAmount(new BigDecimal("100.00")); |
| | | order.setRefundStatus("PARTIAL"); |
| | | order.setRefundedAmount(new BigDecimal("50.00")); |
| | | when(orderMapper.selectForUpdate(10L)).thenReturn(order); |
| | | when(orderMapper.updateById(order)).thenReturn(1); |
| | | |
| | | assertThat(service.settle(10L, now)).isTrue(); |
| | | |
| | | verify(balanceService).credit(8L, new BigDecimal("45.00")); |
| | | assertThat(order.getSettledAt()).isEqualTo(now); |
| | | } |
| | | |
| | | @Test |
| | | void shouldReverseSettledGuideEarningForRefund() { |
| | | TourOrder order = dueOrder(LocalDateTime.now()); |
| | | order.setSettledAt(LocalDateTime.now()); |
| | | order.setAmount(new BigDecimal("100.00")); |
| | | order.setPaymentAmount(new BigDecimal("100.00")); |
| | | when(balanceService.debitForRefund(8L, new BigDecimal("90.00"))).thenReturn(true); |
| | | |
| | | service.reverseRefund(order, BigDecimal.ZERO, new BigDecimal("100.00")); |
| | | |
| | |
| | | order.setSettledAt(LocalDateTime.now()); |
| | | order.setAmount(new BigDecimal("100.00")); |
| | | order.setPaymentAmount(new BigDecimal("100.00")); |
| | | when(balanceService.debitForRefund(8L, new BigDecimal("27.00"))).thenReturn(true); |
| | | |
| | | service.reverseRefund(order, new BigDecimal("20.00"), new BigDecimal("50.00")); |
| | | |
| | |
| | | order.setCommissionTeamId(6L); |
| | | order.setAmount(new BigDecimal("100.00")); |
| | | order.setPaymentAmount(new BigDecimal("100.00")); |
| | | when(balanceService.debitTeamForRefund(6L, new BigDecimal("90.00"))).thenReturn(true); |
| | | |
| | | service.reverseRefund(order, BigDecimal.ZERO, new BigDecimal("100.00")); |
| | | |
| | | verify(balanceService).debitTeamForRefund(6L, new BigDecimal("90.00")); |
| | | } |
| | | |
| | | @Test |
| | | void shouldNotSettleOrderWithRefundInProgress() { |
| | | LocalDateTime now = LocalDateTime.of(2026, 8, 17, 12, 0); |
| | | TourOrder order = dueOrder(now.minusMinutes(1)); |
| | | order.setRefundStatus("REFUNDING"); |
| | | when(orderMapper.selectForUpdate(10L)).thenReturn(order); |
| | | |
| | | assertThat(service.settle(10L, now)).isFalse(); |
| | | |
| | | verify(balanceService, never()).credit(8L, new BigDecimal("90.00")); |
| | | verify(orderMapper, never()).updateById(order); |
| | | } |
| | | |
| | | @Test |
| | | void shouldFailRefundWhenBalanceAdjustmentFails() { |
| | | TourOrder order = dueOrder(LocalDateTime.now()); |
| | | order.setSettledAt(LocalDateTime.now()); |
| | | order.setAmount(new BigDecimal("100.00")); |
| | | order.setPaymentAmount(new BigDecimal("100.00")); |
| | | |
| | | assertThatThrownBy(() -> service.reverseRefund(order, BigDecimal.ZERO, |
| | | new BigDecimal("100.00"))) |
| | | .isInstanceOf(BusinessException.class) |
| | | .hasMessage("退款后导游收益冲正失败"); |
| | | } |
| | | |
| | | private TourOrder dueOrder(LocalDateTime dueAt) { |
| | | TourOrder order = new TourOrder(); |
| | | order.setId(10L); |