package com.tourguide.modules.finance.service; import com.tourguide.common.huifu.biz.DouGongBasicDataBiz; import com.tourguide.modules.finance.dto.GuideBankCardRequest; import com.tourguide.modules.finance.dto.GuideBankCardResponse; import com.tourguide.modules.finance.dto.GuideSettlementOnboardingRequest; import com.tourguide.modules.finance.dto.GuideSettlementStatusResponse; import com.tourguide.modules.finance.entity.GuideBankBranch; import com.tourguide.modules.finance.entity.GuideWithdrawalAccount; import com.tourguide.modules.finance.mapper.GuideWithdrawalAccountMapper; import com.tourguide.modules.finance.mapper.GuideBankBranchMapper; import com.tourguide.modules.user.entity.GuideUser; import com.tourguide.modules.user.service.GuideUserService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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.anyInt; import static org.mockito.Mockito.doAnswer; 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 GuideSettlementServiceTest { private GuideWithdrawalAccountMapper accountMapper; private GuideUserService guideUserService; private DouGongBasicDataBiz douGongBasicDataBiz; private GuideBankBranchMapper bankBranchMapper; private GuideSettlementService service; @BeforeEach void setUp() { accountMapper = mock(GuideWithdrawalAccountMapper.class); guideUserService = mock(GuideUserService.class); douGongBasicDataBiz = mock(DouGongBasicDataBiz.class); bankBranchMapper = mock(GuideBankBranchMapper.class); service = new GuideSettlementService(accountMapper, guideUserService, douGongBasicDataBiz, bankBranchMapper); GuideUser guide = new GuideUser(); guide.setId(8L); guide.setMobile("13800138000"); when(guideUserService.findEnabledById(8L)).thenReturn(guide); doAnswer(invocation -> { GuideWithdrawalAccount account = invocation.getArgument(0); account.setId(10); return 1; }).when(accountMapper).insert(any(GuideWithdrawalAccount.class)); } @Test void submitShouldRouteEnterpriseToEnterpriseOnboarding() throws Exception { when(accountMapper.selectOne(any())).thenReturn(null); when(douGongBasicDataBiz.userBasicDataEnt(any())).thenReturn(success()); GuideSettlementStatusResponse response = service.submit(8L, enterpriseRequest()); assertThat(response.getStatus()).isEqualTo("pending"); assertThat(response.getSubjectType()).isEqualTo("enterprise"); verify(douGongBasicDataBiz).userBasicDataEnt(any(GuideWithdrawalAccount.class)); } @Test void submitShouldRouteIndividualToIndividualOnboarding() throws Exception { when(accountMapper.selectOne(any())).thenReturn(null); when(douGongBasicDataBiz.userBasicDataIndv(any())).thenReturn(individualSuccess()); GuideSettlementStatusResponse response = service.submit(8L, individualRequest()); assertThat(response.getStatus()).isEqualTo("approved"); assertThat(response.getSubjectType()).isEqualTo("individual"); verify(douGongBasicDataBiz).userBasicDataIndv(any(GuideWithdrawalAccount.class)); } @Test void statusShouldPromoteLegacyIndividualWithoutApplicationNo() throws Exception { GuideWithdrawalAccount account = new GuideWithdrawalAccount(); account.setId(10); account.setSubjectType("individual"); account.setHuifuId("HF100"); account.setIsAudit(1); account.setApplicationStatus("submitted"); when(accountMapper.selectOne(any())).thenReturn(account); GuideSettlementStatusResponse response = service.status(8L); assertThat(response.getStatus()).isEqualTo("approved"); assertThat(account.getApplicationStatus()).isEqualTo("succeeded"); verify(douGongBasicDataBiz, never()).userApplyQuery(any(), any()); } @Test void bindBankCardShouldOnlySaveCanonicalBranchDataLocally() throws Exception { GuideWithdrawalAccount account = new GuideWithdrawalAccount(); account.setId(10); account.setIsAudit(2); account.setHuifuId("HF100"); account.setCertificateName("张三"); account.setCertificateCode("510000199001010000"); account.setCertificateExpireTime("2020-01-01,长期"); when(accountMapper.selectCurrentForUpdate(anyInt())).thenReturn(account); GuideBankBranch branch = new GuideBankBranch(); branch.setBankName("数据库标准银行"); branch.setBranchName("数据库标准支行"); branch.setBranchCode("105510000001"); branch.setCityCode("510100"); when(bankBranchMapper.selectOne(any())).thenReturn(branch); GuideBankCardRequest request = bankCardRequest(); request.setBankName("前端伪造银行"); request.setBranchName("前端伪造支行"); GuideBankCardResponse response = service.bindBankCard(8L, request); assertThat(account.getBankName()).isEqualTo("数据库标准银行"); assertThat(account.getBankBranchName()).isEqualTo("数据库标准支行"); assertThat(account.getBankBranchCode()).isEqualTo("105510000001"); assertThat(account.getBankCardCityCode()).isEqualTo("510100"); assertThat(account.getBankCardStatus()).isEqualTo("local"); assertThat(account.getTokenNo()).isNull(); assertThat(response.getStatus()).isEqualTo("local"); verify(douGongBasicDataBiz, never()).userBusinessOpen(any()); } @Test void openBankCardBusinessShouldCallHuifuWhenCardIsSelected() throws Exception { GuideWithdrawalAccount account = approvedAccountWithLocalCard(); when(accountMapper.selectCurrentForUpdate(anyInt())).thenReturn(account); Map result = success(); result.put("token_no", "CARD_TOKEN_100"); when(douGongBasicDataBiz.userBusinessOpen(any())).thenReturn(result); GuideBankCardResponse response = service.openBankCardBusiness(8L, 10); assertThat(response.getStatus()).isEqualTo("approved"); assertThat(account.getTokenNo()).isEqualTo("CARD_TOKEN_100"); assertThat(account.getBankCardStatus()).isEqualTo("approved"); verify(douGongBasicDataBiz).userBusinessOpen(account); } @Test void openBankCardBusinessShouldBeIdempotentAfterApproval() throws Exception { GuideWithdrawalAccount account = approvedAccountWithLocalCard(); account.setTokenNo("CARD_TOKEN_100"); account.setBankCardStatus("approved"); when(accountMapper.selectCurrentForUpdate(anyInt())).thenReturn(account); GuideBankCardResponse response = service.openBankCardBusiness(8L, 10); assertThat(response.getStatus()).isEqualTo("approved"); verify(douGongBasicDataBiz, never()).userBusinessOpen(any()); } @Test void openBankCardBusinessShouldOnlyRefreshExistingPendingApplication() throws Exception { GuideWithdrawalAccount account = approvedAccountWithLocalCard(); account.setBankCardStatus("pending"); account.setCusApplicationId("APPLY100"); when(accountMapper.selectCurrentForUpdate(anyInt())).thenReturn(account); when(douGongBasicDataBiz.userApplyQuery(account, "APPLY100")).thenReturn(success()); GuideBankCardResponse response = service.openBankCardBusiness(8L, 10); assertThat(response.getStatus()).isEqualTo("pending"); verify(douGongBasicDataBiz, never()).userBusinessOpen(any()); verify(douGongBasicDataBiz).userApplyQuery(account, "APPLY100"); } @Test void resubmitShouldNotChangeExistingSubjectType() { GuideWithdrawalAccount account = new GuideWithdrawalAccount(); account.setId(10); account.setSubjectType("individual"); account.setHuifuId("HF100"); account.setIsAudit(3); account.setApplicationStatus("failed"); when(accountMapper.selectOne(any())).thenReturn(account); assertThatThrownBy(() -> service.submit(8L, enterpriseRequest())) .hasMessage("已提交的进件主体类型不可变更,请按原类型重新提交"); } private GuideSettlementOnboardingRequest enterpriseRequest() { GuideSettlementOnboardingRequest request = baseRequest("enterprise"); request.setEnterpriseName("成都名导文旅有限公司"); request.setCreditCode("91510100MA1234567X"); request.setLicenseValidityType("fixed"); request.setLicenseBeginDate("2020-01-01"); request.setLicenseEndDate("2040-01-01"); request.setRegisteredProvinceCode("510000"); request.setRegisteredCityCode("510100"); request.setRegisteredDistrictCode("510107"); request.setRegisteredAddress("四川省成都市武侯区测试路1号"); return request; } private GuideSettlementOnboardingRequest individualRequest() { return baseRequest("individual"); } private GuideSettlementOnboardingRequest baseRequest(String subjectType) { GuideSettlementOnboardingRequest request = new GuideSettlementOnboardingRequest(); request.setSubjectType(subjectType); request.setSubjectName("张三"); request.setSubjectIdNumber("510000199001010000"); request.setSubjectCertificateValidityType("long_term"); request.setSubjectCertificateBeginDate("2020-01-01"); request.setContactName("张三"); request.setContactMobile("13800138000"); return request; } private GuideBankCardRequest bankCardRequest() { GuideBankCardRequest request = new GuideBankCardRequest(); request.setCardNumber("6222021234567890"); request.setProvinceCode("510000"); request.setCityCode("510100"); request.setBankName("测试银行"); request.setBranchName("测试支行"); request.setBranchCode("105510000001"); return request; } private GuideWithdrawalAccount approvedAccountWithLocalCard() { GuideWithdrawalAccount account = new GuideWithdrawalAccount(); account.setId(10); account.setIsAudit(2); account.setHuifuId("HF100"); account.setCertificateName("张三"); account.setCertificateCode("510000199001010000"); account.setCertificateExpireTime("2020-01-01,长期"); account.setBankName("数据库标准银行"); account.setBankCardCode("6222021234567890"); account.setBankBranchName("数据库标准支行"); account.setBankBranchCode("105510000001"); account.setBankCardProvinceCode("510000"); account.setBankCardCityCode("510100"); account.setOpenAccountName("张三"); account.setBankCardStatus("local"); return account; } private Map success() { Map result = new HashMap(); result.put("resp_code", "00000000"); result.put("huifu_id", "HF100"); result.put("apply_no", "APPLY100"); return result; } private Map individualSuccess() { Map result = new HashMap(); result.put("resp_code", "00000000"); result.put("resp_desc", "成功"); result.put("huifu_id", "HF100"); return result; } }