diff --git a/blade-third-party-api/blade-bfi-api/src/test/java/org/springblade/thirdparty/bfi/service/BfiServiceImplTest.java b/blade-third-party-api/blade-bfi-api/src/test/java/org/springblade/thirdparty/bfi/service/BfiServiceImplTest.java
new file mode 100644
index 00000000..4591ccb8
--- /dev/null
+++ b/blade-third-party-api/blade-bfi-api/src/test/java/org/springblade/thirdparty/bfi/service/BfiServiceImplTest.java
@@ -0,0 +1,592 @@
+/*
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * Author: Chill Zhuang (bladejava@qq.com)
+ */
+package org.springblade.thirdparty.bfi.service;
+
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import feign.Request;
+import feign.Response;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.thirdparty.bfi.config.BfiProperties;
+import org.springblade.thirdparty.bfi.exception.BfiException;
+import org.springblade.thirdparty.bfi.feign.IBfiClient;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveEntryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiArBillQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiArBillSaveDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiArBillSaveEntryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimAnnounceQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimPushDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiClaimPushEntryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerSaveDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerGroupEntryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplyOpenApiQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplyQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplySaveDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplySaveEntryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceAssignBodyDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceAssignItemDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceAssignRefDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoicePoolQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiReceivedInvoiceQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiReceivedInvoiceSaveDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiReceivedInvoiceSaveEntryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiRecBillQueryDTO;
+import org.springblade.thirdparty.bfi.pojo.dto.BfiTokenDTO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiApBillVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiArBillVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiClaimAnnounceVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiCustomerVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoiceApplyVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoicePoolVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiOpenApiResultVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiReceivedInvoiceVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiRecBillVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiResultVO;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiTokenVO;
+import org.springframework.data.redis.core.ValueOperations;
+
+import org.springblade.thirdparty.bfi.service.impl.BfiServiceImpl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * 业财融合(BFI)服务实现单元测试
+ *
+ * @author Chill
+ */
+@ExtendWith(MockitoExtension.class)
+class BfiServiceImplTest {
+
+ @Mock
+ private BfiProperties properties;
+
+ @Mock
+ private IBfiClient client;
+
+ @Mock
+ private BladeRedis bladeRedis;
+
+ @Mock
+ private ValueOperations valueOperations;
+
+ @InjectMocks
+ private BfiServiceImpl service;
+
+ @BeforeEach
+ void setUp() {
+ lenient().when(properties.getClientId()).thenReturn("test-client");
+ lenient().when(properties.getClientSecret()).thenReturn("test-secret");
+ lenient().when(properties.getUsername()).thenReturn("test-user");
+ lenient().when(properties.getAccountId()).thenReturn("test-account");
+ lenient().when(properties.getLanguage()).thenReturn("zh_CN");
+ lenient().when(bladeRedis.getValueOps()).thenReturn(valueOperations);
+ }
+
+ /**
+ * 构建成功响应
+ */
+ private BfiResultVO success(T data) {
+ BfiResultVO result = new BfiResultVO<>();
+ result.setStatus(true);
+ result.setErrorCode("0");
+ result.setData(data);
+ return result;
+ }
+
+ /**
+ * 构建保存结果
+ */
+ private BfiBillSaveResultVO saveResult(int successCount) {
+ BfiBillSaveResultVO result = new BfiBillSaveResultVO();
+ result.setSuccessCount(String.valueOf(successCount));
+ result.setFailCount("0");
+ return result;
+ }
+
+ /**
+ * 构建令牌数据
+ */
+ private BfiTokenVO tokenVO() {
+ BfiTokenVO tokenVO = new BfiTokenVO();
+ tokenVO.setAccessToken("token-1");
+ tokenVO.setExpiresIn(600_000L);
+ return tokenVO;
+ }
+
+ /**
+ * 模拟缓存未命中并通过 getToken 获取令牌
+ */
+ private void mockAccessToken() {
+ when(bladeRedis.get("blade:third:bfi:token")).thenReturn(null);
+ when(client.getToken(any(BfiTokenDTO.class))).thenReturn(success(tokenVO()));
+ }
+
+ @Test
+ void getTokenShouldCacheAndReturnToken() {
+ when(client.getToken(any(BfiTokenDTO.class))).thenReturn(success(tokenVO()));
+ BfiTokenVO tokenVO = service.getToken();
+ assertEquals("token-1", tokenVO.getAccessToken());
+ ArgumentCaptor captor = ArgumentCaptor.forClass(BfiTokenDTO.class);
+ verify(client).getToken(captor.capture());
+ assertEquals("test-client", captor.getValue().getClientId());
+ assertNotNull(captor.getValue().getNonce());
+ assertNotNull(captor.getValue().getTimestamp());
+ verify(valueOperations).setIfAbsent(eq("blade:third:bfi:token"), eq("token-1"), any());
+ }
+
+ @Test
+ void getTokenShouldThrowWhenDataEmpty() {
+ when(client.getToken(any(BfiTokenDTO.class))).thenReturn(success(new BfiTokenVO()));
+ assertThrows(BfiException.class, () -> service.getToken());
+ }
+
+ @Test
+ void getAccessTokenShouldReturnCachedValue() {
+ when(bladeRedis.get("blade:third:bfi:token")).thenReturn("cached-token");
+ assertEquals("cached-token", service.getAccessToken());
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void getAccessTokenShouldFetchWhenCacheMiss() {
+ mockAccessToken();
+ assertEquals("token-1", service.getAccessToken());
+ }
+
+ @Test
+ void queryCustomerShouldReturnPage() {
+ mockAccessToken();
+ BfiPageVO page = new BfiPageVO<>();
+ when(client.customerQuery(eq("token-1"), eq(1), eq(10), any(), any(), any(), any(), any(), any(), any(), any()))
+ .thenReturn(success(page));
+ BfiCustomerQueryDTO param = new BfiCustomerQueryDTO();
+ param.setPageNo(1);
+ param.setPageSize(10);
+ assertEquals(page, service.queryCustomer(param));
+ }
+
+ @Test
+ void queryCustomerShouldThrowWhenPageEmpty() {
+ assertThrows(BfiException.class, () -> service.queryCustomer(new BfiCustomerQueryDTO()));
+ }
+
+ @Test
+ void saveCustomerShouldValidateRequiredFields() {
+ BfiCustomerSaveDTO noName = new BfiCustomerSaveDTO();
+ assertThrows(BfiException.class, () -> service.saveCustomer(noName));
+
+ BfiCustomerSaveDTO noCreditCode = new BfiCustomerSaveDTO();
+ noCreditCode.setName("客户A");
+ noCreditCode.setEntryGroupstandard(List.of(new BfiCustomerGroupEntryDTO()));
+ assertThrows(BfiException.class, () -> service.saveCustomer(noCreditCode));
+
+ BfiCustomerSaveDTO noGroup = new BfiCustomerSaveDTO();
+ noGroup.setName("客户A");
+ noGroup.setSocietycreditcode("91450000MA5Q1G1M59");
+ assertThrows(BfiException.class, () -> service.saveCustomer(noGroup));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void saveCustomerShouldWrapData() {
+ mockAccessToken();
+ when(client.customerSave(anyString(), any())).thenReturn(success(saveResult(1)));
+ BfiCustomerSaveDTO param = new BfiCustomerSaveDTO();
+ param.setName("客户A");
+ param.setSocietycreditcode("91450000MA5Q1G1M59");
+ param.setEntryGroupstandard(List.of(new BfiCustomerGroupEntryDTO()));
+ BfiBillSaveResultVO result = service.saveCustomer(param);
+ assertEquals("1", result.getSuccessCount());
+ ArgumentCaptor captor =
+ ArgumentCaptor.forClass(org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerSaveBodyDTO.class);
+ verify(client).customerSave(eq("token-1"), captor.capture());
+ assertEquals(param, captor.getValue().getData().get(0));
+ }
+
+ @Test
+ void saveApBillShouldValidateRequiredFields() {
+ BfiApBillSaveDTO noSrc = new BfiApBillSaveDTO();
+ assertThrows(BfiException.class, () -> service.saveApBill(noSrc));
+
+ BfiApBillSaveDTO noEntry = new BfiApBillSaveDTO();
+ noEntry.setAwrpSrcSys("WCCK");
+ noEntry.setAwrpSrcCode("AP-1");
+ assertThrows(BfiException.class, () -> service.saveApBill(noEntry));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void saveApBillShouldWrapData() {
+ mockAccessToken();
+ when(client.apBillSave(anyString(), any())).thenReturn(success(saveResult(1)));
+ BfiApBillSaveDTO param = new BfiApBillSaveDTO();
+ param.setAwrpSrcSys("WCCK");
+ param.setAwrpSrcCode("AP-1");
+ param.setDetailentry(List.of(new BfiApBillSaveEntryDTO()));
+ assertEquals("1", service.saveApBill(param).getSuccessCount());
+ }
+
+ @Test
+ void queryApBillShouldReturnPage() {
+ mockAccessToken();
+ BfiPageVO page = new BfiPageVO<>();
+ when(client.apBillQuery(eq("token-1"), eq(1), eq(10), any(), any(), any(), any(), any(), any()))
+ .thenReturn(success(page));
+ BfiApBillQueryDTO param = new BfiApBillQueryDTO();
+ param.setPageNo(1);
+ param.setPageSize(10);
+ assertEquals(page, service.queryApBill(param));
+ }
+
+ @Test
+ void saveArBillShouldValidateRequiredFields() {
+ BfiArBillSaveDTO noSrc = new BfiArBillSaveDTO();
+ assertThrows(BfiException.class, () -> service.saveArBill(noSrc));
+
+ BfiArBillSaveDTO noEntry = new BfiArBillSaveDTO();
+ noEntry.setAwrpSrcSys("WCCK");
+ noEntry.setAwrpSrcCode("AR-1");
+ assertThrows(BfiException.class, () -> service.saveArBill(noEntry));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void saveArBillShouldWrapData() {
+ mockAccessToken();
+ when(client.arBillSave(anyString(), any())).thenReturn(success(saveResult(1)));
+ BfiArBillSaveDTO param = new BfiArBillSaveDTO();
+ param.setAwrpSrcSys("WCCK");
+ param.setAwrpSrcCode("AR-1");
+ param.setEntry(List.of(new BfiArBillSaveEntryDTO()));
+ assertEquals("1", service.saveArBill(param).getSuccessCount());
+ }
+
+ @Test
+ void queryArBillShouldReturnPage() {
+ mockAccessToken();
+ BfiPageVO page = new BfiPageVO<>();
+ when(client.arBillQuery(eq("token-1"), eq(1), eq(10), any(), any(), any(), any(), any(), any()))
+ .thenReturn(success(page));
+ BfiArBillQueryDTO param = new BfiArBillQueryDTO();
+ param.setPageNo(1);
+ param.setPageSize(10);
+ assertEquals(page, service.queryArBill(param));
+ }
+
+ @Test
+ void invoiceAssignShouldValidateAndWrapBody() {
+ assertThrows(BfiException.class, () -> service.invoiceAssign(null, List.of()));
+ assertThrows(BfiException.class, () -> service.invoiceAssign("assign", List.of()));
+
+ mockAccessToken();
+ when(client.invoiceAssign(anyString(), any())).thenReturn(success(saveResult(1)));
+ BfiInvoiceAssignRefDTO invoice = new BfiInvoiceAssignRefDTO();
+ invoice.setAwrpSrcCode("INV-1");
+ invoice.setAwrpSrcSys("WCCK");
+ BfiInvoiceAssignRefDTO apBill = new BfiInvoiceAssignRefDTO();
+ apBill.setAwrpSrcCode("AP-1");
+ apBill.setAwrpSrcSys("WCCK");
+ BfiInvoiceAssignItemDTO item = new BfiInvoiceAssignItemDTO();
+ item.setApInvoice(List.of(invoice));
+ item.setApFinapbill(List.of(apBill));
+ assertEquals("1", service.invoiceAssign("assign", List.of(item)).getSuccessCount());
+ ArgumentCaptor captor = ArgumentCaptor.forClass(BfiInvoiceAssignBodyDTO.class);
+ verify(client).invoiceAssign(eq("token-1"), captor.capture());
+ assertEquals("assign", captor.getValue().getOp());
+ }
+
+ @Test
+ void saveInvoiceApplyShouldValidateRequiredFields() {
+ BfiInvoiceApplySaveDTO noBillNo = new BfiInvoiceApplySaveDTO();
+ assertThrows(BfiException.class, () -> service.saveInvoiceApply(noBillNo));
+
+ BfiInvoiceApplySaveDTO noItem = new BfiInvoiceApplySaveDTO();
+ noItem.setBillno("KPSQ-1");
+ noItem.setOrgidNumber("01.00");
+ assertThrows(BfiException.class, () -> service.saveInvoiceApply(noItem));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void saveInvoiceApplyShouldWrapData() {
+ mockAccessToken();
+ when(client.invoiceApplySave(anyString(), any())).thenReturn(success(saveResult(1)));
+ BfiInvoiceApplySaveDTO param = new BfiInvoiceApplySaveDTO();
+ param.setBillno("KPSQ-1");
+ param.setOrgidNumber("01.00");
+ param.setSimOriginalBillItem(List.of(new BfiInvoiceApplySaveEntryDTO()));
+ assertEquals("1", service.saveInvoiceApply(param).getSuccessCount());
+ }
+
+ @Test
+ void queryInvoiceApplyShouldReturnPage() {
+ mockAccessToken();
+ BfiPageVO page = new BfiPageVO<>();
+ when(client.invoiceApplyQuery(eq("token-1"), eq(1), eq(10), any(), any(), any(), any()))
+ .thenReturn(success(page));
+ BfiInvoiceApplyQueryDTO param = new BfiInvoiceApplyQueryDTO();
+ param.setPageNo(1);
+ param.setPageSize(10);
+ assertEquals(page, service.queryInvoiceApply(param));
+ }
+
+ @Test
+ void queryInvoiceApplyInvoiceShouldEncodeBase64Data() {
+ mockAccessToken();
+ BfiOpenApiResultVO> openApiResult = new BfiOpenApiResultVO<>();
+ openApiResult.setSuccess(true);
+ openApiResult.setErrorCode("S0000");
+ openApiResult.setData(List.of());
+ when(client.invoiceApplyOpenApi(anyString(), any())).thenReturn(openApiResult);
+ BfiInvoiceApplyOpenApiQueryDTO param = new BfiInvoiceApplyOpenApiQueryDTO();
+ param.setSerialNo("KPSQ-1,KPSQ-2");
+ param.setSellerTaxpayerId("91450000MA5Q1G1M59");
+ service.queryInvoiceApplyInvoice(param);
+ ArgumentCaptor captor =
+ ArgumentCaptor.forClass(org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplyOpenApiBodyDTO.class);
+ verify(client).invoiceApplyOpenApi(eq("token-1"), captor.capture());
+ assertEquals("BILL.INVOICE.QUERY", captor.getValue().getInterfaceCode());
+ assertEquals("test-client", captor.getValue().getBusinessSystemCode());
+ String decoded = new String(Base64.getDecoder().decode(captor.getValue().getData()), StandardCharsets.UTF_8);
+ JSONObject json = JSONUtil.parseObj(decoded);
+ assertEquals("KPSQ-1,KPSQ-2", json.getStr("serialNo"));
+ assertEquals("91450000MA5Q1G1M59", json.getStr("sellerTaxpayerId"));
+ }
+
+ @Test
+ void queryInvoiceApplyInvoiceShouldThrowWhenParamsBlank() {
+ assertThrows(BfiException.class, () -> service.queryInvoiceApplyInvoice(new BfiInvoiceApplyOpenApiQueryDTO()));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void saveReceivedInvoiceShouldValidateRequiredFields() {
+ BfiReceivedInvoiceSaveDTO noSrc = new BfiReceivedInvoiceSaveDTO();
+ assertThrows(BfiException.class, () -> service.saveReceivedInvoice(noSrc));
+
+ BfiReceivedInvoiceSaveDTO noEntry = new BfiReceivedInvoiceSaveDTO();
+ noEntry.setAwrpSrcSys("WCCK");
+ noEntry.setAwrpSrcCode("INV-1");
+ assertThrows(BfiException.class, () -> service.saveReceivedInvoice(noEntry));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void saveReceivedInvoiceShouldWrapData() {
+ mockAccessToken();
+ when(client.receivedInvoiceSave(anyString(), any())).thenReturn(success(saveResult(1)));
+ BfiReceivedInvoiceSaveDTO param = new BfiReceivedInvoiceSaveDTO();
+ param.setAwrpSrcSys("WCCK");
+ param.setAwrpSrcCode("INV-1");
+ param.setEntry(List.of(new BfiReceivedInvoiceSaveEntryDTO()));
+ assertEquals("1", service.saveReceivedInvoice(param).getSuccessCount());
+ }
+
+ @Test
+ void queryReceivedInvoiceShouldValidateModifyTime() {
+ assertThrows(BfiException.class, () -> service.queryReceivedInvoice(new BfiReceivedInvoiceQueryDTO()));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void queryReceivedInvoiceShouldReturnPage() {
+ mockAccessToken();
+ BfiPageVO page = new BfiPageVO<>();
+ when(client.receivedInvoiceQuery(anyString(), any(BfiReceivedInvoiceQueryDTO.class))).thenReturn(success(page));
+ BfiReceivedInvoiceQueryDTO param = new BfiReceivedInvoiceQueryDTO();
+ param.setModifytime("2026-09-08 10:36:29");
+ param.setModifytimeE("2026-09-08 10:39:36");
+ assertEquals(page, service.queryReceivedInvoice(param));
+ }
+
+ @Test
+ void queryRecBillShouldValidateRequiredFields() {
+ assertThrows(BfiException.class, () -> service.queryRecBill(new BfiRecBillQueryDTO()));
+
+ BfiRecBillQueryDTO noDate = new BfiRecBillQueryDTO();
+ noDate.setPageNo(1);
+ noDate.setPageSize(10);
+ assertThrows(BfiException.class, () -> service.queryRecBill(noDate));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void queryRecBillShouldReturnPage() {
+ mockAccessToken();
+ BfiPageVO page = new BfiPageVO<>();
+ when(client.recBillQuery(eq("token-1"), eq(1), eq(10), any(), any(), any(), any()))
+ .thenReturn(success(page));
+ BfiRecBillQueryDTO param = new BfiRecBillQueryDTO();
+ param.setPageNo(1);
+ param.setPageSize(10);
+ param.setBeginBizdate("2026-08-08");
+ param.setEndBizdate("2026-09-08");
+ assertEquals(page, service.queryRecBill(param));
+ }
+
+ @Test
+ void queryClaimAnnounceShouldValidateParams() {
+ assertThrows(BfiException.class, () -> service.queryClaimAnnounce(null, 1, 10));
+ assertThrows(BfiException.class, () -> service.queryClaimAnnounce("张三", null, 10));
+ }
+
+ @Test
+ void queryClaimAnnounceShouldReturnPage() {
+ mockAccessToken();
+ BfiPageVO page = new BfiPageVO<>();
+ when(client.claimAnnounceQuery("token-1", "张三", 1, 10)).thenReturn(success(page));
+ assertEquals(page, service.queryClaimAnnounce("张三", 1, 10));
+ }
+
+ @Test
+ void pushClaimResultShouldValidateRequiredFields() {
+ assertThrows(BfiException.class, () -> service.pushClaimResult(null));
+
+ BfiClaimPushDTO noBillId = new BfiClaimPushDTO();
+ assertThrows(BfiException.class, () -> service.pushClaimResult(noBillId));
+
+ BfiClaimPushDTO noEntry = new BfiClaimPushDTO();
+ noEntry.setBillid(1L);
+ noEntry.setAwrpSrcSys("WCCK");
+ noEntry.setAwrpSrcCode("RL-1");
+ assertThrows(BfiException.class, () -> service.pushClaimResult(noEntry));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void pushClaimResultShouldWrapData() {
+ mockAccessToken();
+ when(client.claimBillPush(anyString(), any())).thenReturn(success(saveResult(1)));
+ BfiClaimPushDTO param = new BfiClaimPushDTO();
+ param.setBillid(2398805822369971200L);
+ param.setAwrpSrcSys("WCCK");
+ param.setAwrpSrcCode("RL-1");
+ param.setEntryentity(List.of(new BfiClaimPushEntryDTO()));
+ assertEquals("1", service.pushClaimResult(param).getSuccessCount());
+ }
+
+ @Test
+ void queryInvoicePoolShouldValidateQueryParam() {
+ assertThrows(BfiException.class, () -> service.queryInvoicePool(new BfiInvoicePoolQueryDTO()));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void queryInvoicePoolShouldReturnRows() {
+ mockAccessToken();
+ BfiInvoicePoolVO invoice = new BfiInvoicePoolVO();
+ when(client.invoicePoolQuery(anyString(), any(BfiInvoicePoolQueryDTO.class)))
+ .thenReturn(success(List.of(invoice)));
+ BfiInvoicePoolQueryDTO param = new BfiInvoicePoolQueryDTO();
+ param.setQueryParam(Map.of("invoiceNo", "25532000000044538098"));
+ List rows = service.queryInvoicePool(param);
+ assertEquals(1, rows.size());
+ }
+
+ @Test
+ void downloadInvoiceShouldValidatePath() {
+ assertThrows(BfiException.class, () -> service.downloadInvoice(null));
+ verifyNoInteractions(client);
+ }
+
+ @Test
+ void downloadInvoiceShouldThrowWhenStatusNotOk() {
+ mockAccessToken();
+ Response response = downloadResponse(500, "Internal Error");
+ when(client.invoiceDownload(anyString(), any())).thenReturn(response);
+ assertThrows(BfiException.class, () -> service.downloadInvoice("234e244b2c803400"));
+ response.close();
+ }
+
+ @Test
+ void downloadInvoiceShouldReturnResponse() {
+ mockAccessToken();
+ Response response = downloadResponse(200, "OK");
+ when(client.invoiceDownload(anyString(), any())).thenReturn(response);
+ assertEquals(response, service.downloadInvoice("234e244b2c803400"));
+ response.close();
+ }
+
+ /**
+ * 构建下载响应
+ */
+ private Response downloadResponse(int status, String reason) {
+ Request request = Request.create(Request.HttpMethod.POST, "/ierp/kapi/v2/awrp/bosext/attachment/download",
+ Map.of(), null, StandardCharsets.UTF_8);
+ return Response.builder().status(status).reason(reason).request(request).build();
+ }
+
+ @Test
+ void commonCallShouldThrowWhenRemoteFailed() {
+ mockAccessToken();
+ BfiResultVO> failed = new BfiResultVO<>();
+ failed.setStatus(false);
+ failed.setErrorCode("400");
+ failed.setMessage("操作失败");
+ when(client.customerQuery(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()))
+ .thenReturn(failed);
+ BfiCustomerQueryDTO param = new BfiCustomerQueryDTO();
+ param.setPageNo(1);
+ param.setPageSize(10);
+ BfiException exception = assertThrows(BfiException.class, () -> service.queryCustomer(param));
+ assertTrue(exception.getMessage().contains("操作失败"));
+ }
+
+ @Test
+ void getAccessTokenShouldNotCallRemoteWhenCached() {
+ when(bladeRedis.get("blade:third:bfi:token")).thenReturn("cached-token");
+ service.getAccessToken();
+ verify(client, never()).getToken(any());
+ }
+}