package com.tourguide.modules.finance.service;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.tourguide.modules.finance.dto.GuideWithdrawalCreateRequest;
|
import com.tourguide.modules.finance.dto.WithdrawalResponse;
|
import com.tourguide.modules.finance.entity.GuideWithdrawal;
|
import com.tourguide.modules.finance.entity.GuideWithdrawalAccount;
|
import com.tourguide.modules.finance.mapper.GuideWithdrawalMapper;
|
import com.tourguide.modules.user.entity.GuideUser;
|
import com.tourguide.modules.user.entity.GuideTeam;
|
import com.tourguide.modules.user.mapper.GuideTeamMapper;
|
import com.tourguide.modules.user.mapper.GuideUserMapper;
|
import com.tourguide.modules.user.service.GuideUserService;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
|
import java.math.BigDecimal;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.when;
|
|
class GuideWithdrawalServiceTest {
|
private GuideWithdrawalMapper withdrawalMapper;
|
private GuideSettlementService settlementService;
|
private GuideUserService guideUserService;
|
private GuideWithdrawableBalanceService balanceService;
|
private GuideWithdrawalStateService stateService;
|
private GuideWithdrawalPayoutService payoutService;
|
private GuideTeamMapper teamMapper;
|
private GuideUserMapper guideUserMapper;
|
private GuideWithdrawalService service;
|
|
@BeforeEach
|
void setUp() {
|
withdrawalMapper = mock(GuideWithdrawalMapper.class);
|
settlementService = mock(GuideSettlementService.class);
|
guideUserService = mock(GuideUserService.class);
|
balanceService = mock(GuideWithdrawableBalanceService.class);
|
stateService = mock(GuideWithdrawalStateService.class);
|
payoutService = mock(GuideWithdrawalPayoutService.class);
|
teamMapper = mock(GuideTeamMapper.class);
|
guideUserMapper = mock(GuideUserMapper.class);
|
service = new GuideWithdrawalService(withdrawalMapper, settlementService,
|
guideUserService, balanceService, stateService, payoutService, teamMapper, guideUserMapper);
|
}
|
|
@Test
|
void createShouldReserveAvailableBalanceAndPersistCardSnapshot() throws Exception {
|
GuideUser guide = new GuideUser();
|
guide.setId(8L);
|
guide.setRealName("张三");
|
guide.setMobile("13800138000");
|
guide.setStatus(1);
|
GuideWithdrawalAccount account = new GuideWithdrawalAccount();
|
account.setId(10);
|
account.setHuifuId("HF100");
|
account.setBankName("招商银行");
|
account.setBankCardCode("6225888888885678");
|
when(guideUserService.findEnabledById(8L)).thenReturn(guide);
|
when(guideUserMapper.selectForUpdate(8L)).thenReturn(guide);
|
when(settlementService.requireApprovedCardForUpdate(8L, 10)).thenReturn(account);
|
when(balanceService.debit(8L, new BigDecimal("200.00"))).thenReturn(true);
|
doAnswer(invocation -> {
|
GuideWithdrawal withdrawal = invocation.getArgument(0);
|
withdrawal.setId(20L);
|
return 1;
|
}).when(withdrawalMapper).insert(any(GuideWithdrawal.class));
|
GuideWithdrawalCreateRequest request = new GuideWithdrawalCreateRequest();
|
request.setAmount(new BigDecimal("200.00"));
|
request.setBankCardId(10);
|
|
WithdrawalResponse response = service.create(8L, request);
|
|
assertThat(response.getAmount()).isEqualByComparingTo("200.00");
|
assertThat(response.getBankName()).isEqualTo("招商银行");
|
assertThat(response.getCardMasked()).isEqualTo("****5678");
|
assertThat(response.getStatus()).isEqualTo("PENDING");
|
verify(balanceService).debit(8L, new BigDecimal("200.00"));
|
}
|
|
@Test
|
void rejectShouldReturnReservedBalance() {
|
GuideWithdrawal withdrawal = new GuideWithdrawal();
|
withdrawal.setId(20L);
|
withdrawal.setGuideId(8L);
|
withdrawal.setAmount(new BigDecimal("200.00"));
|
withdrawal.setStatus("REJECTED");
|
when(stateService.reject(20L, "资料不完整", "admin"))
|
.thenReturn(new WithdrawalResponse(withdrawal));
|
|
WithdrawalResponse response = service.process(20L, "REJECTED", "资料不完整", "admin");
|
|
assertThat(response.getStatus()).isEqualTo("REJECTED");
|
verify(stateService).reject(20L, "资料不完整", "admin");
|
}
|
|
@Test
|
void failedCashCallbackShouldReturnToTransferredWithoutRefundingLocalBalance() {
|
GuideWithdrawal withdrawal = new GuideWithdrawal();
|
withdrawal.setId(20L);
|
withdrawal.setGuideId(8L);
|
withdrawal.setAmount(new BigDecimal("200.00"));
|
withdrawal.setCashReqSeqId("CW001");
|
withdrawal.setCashReqDate("20260817");
|
withdrawal.setStatus("PROCESSING");
|
GuideWithdrawalAccount account = new GuideWithdrawalAccount();
|
account.setHuifuId("HF100");
|
when(withdrawalMapper.selectByPayoutSequenceForUpdate("CW001")).thenReturn(withdrawal);
|
when(settlementService.findCurrent(8L)).thenReturn(account);
|
when(withdrawalMapper.resetCashForRetry(any(), any(), any(), any())).thenReturn(1);
|
JSONObject body = new JSONObject();
|
body.put("req_seq_id", "CW001");
|
body.put("huifu_id", "HF100");
|
body.put("cash_amt", "200.00");
|
body.put("trans_stat", "F");
|
body.put("resp_desc", "银行处理中断");
|
|
assertThat(service.handleCallback(body)).isTrue();
|
|
assertThat(withdrawal.getStatus()).isEqualTo("TRANSFERRED");
|
assertThat(withdrawal.getCashReqSeqId()).isNull();
|
verify(balanceService, org.mockito.Mockito.never()).credit(8L, new BigDecimal("200.00"));
|
}
|
|
@Test
|
void createShouldReserveCollectionTeamBalance() {
|
GuideUser guide = new GuideUser();
|
guide.setId(8L);
|
guide.setTeamId(6L);
|
guide.setRealName("张三");
|
guide.setMobile("13800138000");
|
guide.setStatus(1);
|
GuideTeam team = new GuideTeam();
|
team.setId(6L);
|
team.setCollectionGuideId(8L);
|
GuideWithdrawalAccount account = new GuideWithdrawalAccount();
|
account.setId(10);
|
account.setBankName("招商银行");
|
account.setBankCardCode("6225888888885678");
|
when(guideUserService.findEnabledById(8L)).thenReturn(guide);
|
when(guideUserMapper.selectForUpdate(8L)).thenReturn(guide);
|
when(teamMapper.selectById(6L)).thenReturn(team);
|
when(settlementService.requireApprovedCardForUpdate(8L, 10)).thenReturn(account);
|
when(balanceService.debitTeam(6L, new BigDecimal("200.00"))).thenReturn(true);
|
GuideWithdrawalCreateRequest request = new GuideWithdrawalCreateRequest();
|
request.setAmount(new BigDecimal("200.00"));
|
request.setBankCardId(10);
|
|
WithdrawalResponse response = service.create(8L, request);
|
|
assertThat(response.getTeamId()).isEqualTo(6L);
|
verify(balanceService).debitTeam(6L, new BigDecimal("200.00"));
|
verify(balanceService, never()).debit(8L, new BigDecimal("200.00"));
|
}
|
|
@Test
|
void nonTerminalCashCallbackShouldRemainProcessing() {
|
GuideWithdrawal withdrawal = processingWithdrawal();
|
GuideWithdrawalAccount account = new GuideWithdrawalAccount();
|
account.setHuifuId("HF100");
|
when(withdrawalMapper.selectByPayoutSequenceForUpdate("CW001")).thenReturn(withdrawal);
|
when(settlementService.findCurrent(8L)).thenReturn(account);
|
when(withdrawalMapper.updateById(withdrawal)).thenReturn(1);
|
JSONObject body = new JSONObject();
|
body.put("req_seq_id", "CW001");
|
body.put("huifu_id", "HF100");
|
body.put("cash_amt", "200.00");
|
body.put("hf_seq_id", "HF-CASH-1");
|
|
assertThat(service.handleCallback(body)).isTrue();
|
|
assertThat(withdrawal.getStatus()).isEqualTo("PROCESSING");
|
assertThat(withdrawal.getCashReqSeqId()).isEqualTo("CW001");
|
assertThat(withdrawal.getCashHfSeqId()).isEqualTo("HF-CASH-1");
|
verify(withdrawalMapper, never()).resetCashForRetry(any(), any(), any(), any());
|
}
|
|
@Test
|
void successCallbackShouldRecoverTransferredRecordWithSameCashSequence() {
|
GuideWithdrawal withdrawal = processingWithdrawal();
|
withdrawal.setStatus("TRANSFERRED");
|
GuideWithdrawalAccount account = new GuideWithdrawalAccount();
|
account.setHuifuId("HF100");
|
when(withdrawalMapper.selectByPayoutSequenceForUpdate("CW001")).thenReturn(withdrawal);
|
when(settlementService.findCurrent(8L)).thenReturn(account);
|
when(withdrawalMapper.updateById(withdrawal)).thenReturn(1);
|
JSONObject body = new JSONObject();
|
body.put("req_seq_id", "CW001");
|
body.put("huifu_id", "HF100");
|
body.put("cash_amt", "200.00");
|
body.put("trans_stat", "S");
|
|
assertThat(service.handleCallback(body)).isTrue();
|
|
assertThat(withdrawal.getStatus()).isEqualTo("APPROVED");
|
assertThat(withdrawal.getPayoutMessage()).isEqualTo("取现到账成功");
|
}
|
|
private GuideWithdrawal processingWithdrawal() {
|
GuideWithdrawal withdrawal = new GuideWithdrawal();
|
withdrawal.setId(20L);
|
withdrawal.setGuideId(8L);
|
withdrawal.setAmount(new BigDecimal("200.00"));
|
withdrawal.setCashReqSeqId("CW001");
|
withdrawal.setCashReqDate("20260817");
|
withdrawal.setStatus("PROCESSING");
|
return withdrawal;
|
}
|
}
|