From a8fa21c13e1c7da738a4be6b172c39f169e98945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Tue, 20 Oct 2026 17:41:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9EBFI=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E6=96=B9=E6=B3=95=E7=BA=A7=E6=96=87=E4=BB=B6=E6=A1=A9?= =?UTF-8?q?Mock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thirdParty.bfi.mock-enabled=true 时经 BfiMockConfig 注册 @Primary 的 IBfiClient 代理,{mockDir}/{方法名}.json 有桩走桩、无桩透传真实 Feign,调用侧零改动;桩文件每次调用现读,解析失败返回失败结果而非 null;invoiceDownload 返回 feign.Response 始终透传。 --- .../thirdparty/bfi/config/BfiMockConfig.java | 64 +++++++ .../thirdparty/bfi/config/BfiProperties.java | 7 +- .../thirdparty/bfi/mock/BfiClientMock.java | 176 ++++++++++++++++++ doc/nacos/third-party-api.yaml | 5 + 4 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/config/BfiMockConfig.java create mode 100644 blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/mock/BfiClientMock.java diff --git a/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/config/BfiMockConfig.java b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/config/BfiMockConfig.java new file mode 100644 index 00000000..fcf24015 --- /dev/null +++ b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/config/BfiMockConfig.java @@ -0,0 +1,64 @@ +/** + * 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.config; + +import org.springblade.thirdparty.bfi.feign.IBfiClient; +import org.springblade.thirdparty.bfi.mock.BfiClientMock; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +/** + * BFI 客户端 Mock 配置 + *
+ * thirdParty.bfi.mock-enabled=true(Nacos 单入口总开关)时注册 @Primary 的 mock 实现,
+ * 覆盖同类型 Feign 客户端 bean,{@link org.springblade.thirdparty.bfi.service.impl.BfiServiceImpl} 注入的 {@link IBfiClient}
+ * 无感知切换为"有桩用桩、没桩走接口":{mockDir}/{方法名}.json 有桩走本地 JSON 桩,
+ * 无桩透传真实 Feign 接口({@link BfiClientMock},目录经 thirdParty.bfi.mockDir 配置),调用侧零改动。
+ * 仅本地自测/演示环境开启,生产必须保持 false。
+ *
+ * @author Chill
+ */
+@Configuration
+public class BfiMockConfig {
+
+ /**
+ * mock 版 BFI 客户端(@Primary 优先于 Feign 客户端注入)
+ *
+ * @param properties BFI 配置(取桩文件目录 mockDir)
+ * @param realClientProvider 真实客户端候选
+ * @return mock 客户端
+ */
+ @Bean
+ @Primary
+ // @ConditionalOnProperty 不支持宽松绑定,key 必须与 Nacos 实际键完全一致(本仓库 Nacos 为 thirdParty.bfi.* 驼峰根)
+ @ConditionalOnProperty(name = "thirdParty.bfi.mock-enabled", havingValue = "true")
+ public IBfiClient bfiClientMock(BfiProperties properties, ObjectProvider
+ * 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.mock;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import feign.Response;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.thirdparty.bfi.exception.BfiException;
+import org.springblade.thirdparty.bfi.feign.IBfiClient;
+import org.springblade.thirdparty.bfi.pojo.vo.BfiResultVO;
+import org.springframework.beans.factory.ObjectProvider;
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * BFI 客户端本地 Mock(thirdParty.bfi.mock-enabled=true 时经 BfiMockConfig 注册为 @Primary,包装真实 Feign 客户端)
+ *
+ * 单入口总开关,方法级混合:每个 IBfiClient 方法对应一个外部目录桩文件 {mockDir}/{方法名}.json(thirdParty.bfi.mockDir 配置),
+ * 有桩即走 mock(内容即金蝶真实响应报文,含 BfiResultVO/BfiOpenApiResultVO 包装,按方法返回泛型反序列化,调用侧无感知),
+ * 无桩自动透传真实 Feign 接口,实现"有桩用桩、没桩走接口"。
+ * 桩文件每次调用现读,改文件即生效无需重启;桩解析失败返回失败的 BfiResultVO,不静默吞错。
+ * 文件下载接口 invoiceDownload 返回 feign.Response,无法按 JSON 桩反序列化,始终透传真实接口。
+ *
+ * @author Chill
+ */
+@Slf4j
+public class BfiClientMock implements InvocationHandler {
+
+ /**
+ * 桩文件后缀(文件名即接口方法名)
+ */
+ private static final String MOCK_FILE_SUFFIX = ".json";
+
+ /**
+ * 桩文件目录
+ */
+ private final Path mockDir;
+
+ /**
+ * 真实客户端候选(排除 mock 自身,运行期首次透传时解析并缓存)
+ */
+ private final ObjectProvider