Procházet zdrojové kódy

feat: 新增Feign动态代理

hidewnd před 2 týdny
rodič
revize
78fc2801d3

+ 38 - 0
yt-agent/agent-service/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java

@@ -0,0 +1,38 @@
+package com.ytpm.config.feign;
+
+
+import feign.Feign;
+import feign.Request;
+import feign.Retryer;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
+import org.springframework.cloud.openfeign.support.SpringEncoder;
+import org.springframework.cloud.openfeign.support.SpringMvcContract;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 自定义FeignClient 构造
+ * @author lih
+ * @date 2025-10-24 09:41
+ */
+@Configuration
+public class FeignBuilderConfig {
+
+    @Bean
+    @Scope("prototype")
+    public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> messageConverters) {
+        return Feign.builder()
+                .contract(new SpringMvcContract())
+                .encoder(new SpringEncoder(messageConverters))
+                .decoder(new SpringDecoder(messageConverters))
+                .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMicros(1), 3))
+                .options(new Request.Options(3000, 3000))
+                .requestInterceptor(new FeignConfiguration());
+
+    }
+}

+ 53 - 0
yt-agent/agent-service/src/main/java/com/ytpm/handle/DynamicFeignClientFactory.java

@@ -0,0 +1,53 @@
+package com.ytpm.handle;
+
+
+import feign.Feign;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * 动态FeignClient 构造工厂
+ * @author lih
+ * @date 2025-10-24 09:45
+ */
+@Slf4j
+@Component
+public class DynamicFeignClientFactory {
+
+    private final Feign.Builder feignBuilder;
+    private final LoadBalancerClient loadBalancerClient;
+
+
+    public DynamicFeignClientFactory(Feign.Builder feignBuilder,
+                                     LoadBalancerClient loadBalancerClient) {
+        this.feignBuilder = feignBuilder;
+        this.loadBalancerClient = loadBalancerClient;
+    }
+
+    public <T> T getClient(String serviceName, Class<T> clazz) {
+        int maxRetry = 3;
+        int retryCount = 0;
+        Exception lastException = null;
+
+        while (retryCount < maxRetry) {
+            try {
+                ServiceInstance instance = loadBalancerClient.choose(serviceName);
+                if (instance == null) {
+                    throw new RuntimeException("未找到可用的服务实例:" + serviceName);
+                }
+                String url = instance.getUri().toString();
+                log.info("为服务{}构建FeignClient,目标地址为:{}", serviceName, url);
+                return feignBuilder.target(clazz, url);
+            } catch (Exception e) {
+                lastException = e;
+                log.warn("第 {} 次尝试获取FeignClient失败,服务名:{},错误信息:{}", retryCount + 1, serviceName, e.getMessage());
+                retryCount++;
+
+            }
+        }
+        throw new RuntimeException("创建FeignClient失败,服务名:" + serviceName, lastException);
+    }
+
+}

+ 25 - 10
yt-agent/agent-service/src/main/java/com/ytpm/utils/FeignClientInvoker.java

@@ -1,9 +1,12 @@
 package com.ytpm.utils;
 
+import com.ytpm.handle.DynamicFeignClientFactory;
+import com.ytpm.question.base.BaseFeign;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
 
+import javax.annotation.Resource;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
@@ -20,6 +23,9 @@ public class FeignClientInvoker {
     private final Map<String, Object> feignClientCache = new HashMap<>();
     private final Map<String, Method> methodCache = new HashMap<>();
 
+    @Resource
+    private DynamicFeignClientFactory feignClientFactory;
+
     public FeignClientInvoker(ApplicationContext applicationContext) {
         this.applicationContext = applicationContext;
     }
@@ -47,21 +53,30 @@ public class FeignClientInvoker {
     }
 
     private Object getFeignClient(String serviceName) {
-        // 优先从缓存获取
         if (feignClientCache.containsKey(serviceName)) {
             return feignClientCache.get(serviceName);
         }
-        
-        // 动态查找标注@FeignClient的Bean
-        Map<String, Object> feignClients = applicationContext.getBeansWithAnnotation(FeignClient.class);
-        for (Object bean : feignClients.values()) {
-            FeignClient annotation = bean.getClass().getInterfaces()[0].getAnnotation(FeignClient.class);
-            if (annotation != null && serviceName.equals(annotation.name())) {
-                feignClientCache.put(serviceName, bean);
-                return bean;
+        Object feignClient = null;
+        try {
+            // 默认查询声明实例
+            // 动态查找标注@FeignClient的Bean
+            Map<String, Object> feignClients = applicationContext.getBeansWithAnnotation(FeignClient.class);
+            for (Object bean : feignClients.values()) {
+                FeignClient annotation = bean.getClass().getInterfaces()[0].getAnnotation(FeignClient.class);
+                if (annotation != null && serviceName.equals(annotation.name())) {
+                    feignClientCache.put(serviceName, bean);
+                    feignClient = bean;
+                }
             }
+            // 构建动态FeignClient
+            if (feignClient == null) {
+                feignClient = feignClientFactory.getClient(serviceName, BaseFeign.class);
+            }
+            feignClientCache.put(serviceName, feignClient);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("未找到服务: " + serviceName);
         }
-        throw new IllegalArgumentException("未找到服务: " + serviceName);
+        return feignClient;
     }
 
     private Method getTargetMethod(Object feignClient, String methodName, Object[] args) 

+ 37 - 0
yt-middle/middle-platform/src/main/java/com/ytpm/middle/config/FeignBuilderConfig.java

@@ -0,0 +1,37 @@
+package com.ytpm.middle.config;
+
+
+import feign.Feign;
+import feign.Request;
+import feign.Retryer;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
+import org.springframework.cloud.openfeign.support.SpringEncoder;
+import org.springframework.cloud.openfeign.support.SpringMvcContract;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 自定义FeignClient 构造
+ * @author lih
+ * @date 2025-10-24 09:41
+ */
+@Configuration
+public class FeignBuilderConfig {
+
+    @Bean
+    @Scope("prototype")
+    public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> messageConverters) {
+        return Feign.builder()
+                .contract(new SpringMvcContract())
+                .encoder(new SpringEncoder(messageConverters))
+                .decoder(new SpringDecoder(messageConverters))
+                .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMicros(1), 3))
+                .options(new Request.Options(3000, 3000));
+
+    }
+}

+ 53 - 0
yt-middle/middle-platform/src/main/java/com/ytpm/middle/handle/DynamicFeignClientFactory.java

@@ -0,0 +1,53 @@
+package com.ytpm.middle.handle;
+
+
+import feign.Feign;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * 动态FeignClient 构造工厂
+ * @author lih
+ * @date 2025-10-24 09:45
+ */
+@Slf4j
+@Component
+public class DynamicFeignClientFactory {
+
+    private final Feign.Builder feignBuilder;
+    private final LoadBalancerClient loadBalancerClient;
+
+
+    public DynamicFeignClientFactory(Feign.Builder feignBuilder,
+                                     LoadBalancerClient loadBalancerClient) {
+        this.feignBuilder = feignBuilder;
+        this.loadBalancerClient = loadBalancerClient;
+    }
+
+    public <T> T getClient(String serviceName, Class<T> clazz) {
+        int maxRetry = 3;
+        int retryCount = 0;
+        Exception lastException = null;
+
+        while (retryCount < maxRetry) {
+            try {
+                ServiceInstance instance = loadBalancerClient.choose(serviceName);
+                if (instance == null) {
+                    throw new RuntimeException("未找到可用的服务实例:" + serviceName);
+                }
+                String url = instance.getUri().toString();
+                log.info("为服务{}构建FeignClient,目标地址为:{}", serviceName, url);
+                return feignBuilder.target(clazz, url);
+            } catch (Exception e) {
+                lastException = e;
+                log.warn("第 {} 次尝试获取FeignClient失败,服务名:{},错误信息:{}", retryCount + 1, serviceName, e.getMessage());
+                retryCount++;
+
+            }
+        }
+        throw new RuntimeException("创建FeignClient失败,服务名:" + serviceName, lastException);
+    }
+
+}

+ 25 - 10
yt-middle/middle-platform/src/main/java/com/ytpm/middle/util/FeignClientInvoker.java

@@ -1,9 +1,12 @@
 package com.ytpm.middle.util;
 
+import com.ytpm.middle.handle.DynamicFeignClientFactory;
+import com.ytpm.question.base.BaseFeign;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
 
+import javax.annotation.Resource;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
@@ -20,6 +23,9 @@ public class FeignClientInvoker {
     private final Map<String, Object> feignClientCache = new HashMap<>();
     private final Map<String, Method> methodCache = new HashMap<>();
 
+    @Resource
+    private DynamicFeignClientFactory feignClientFactory;
+
     public FeignClientInvoker(ApplicationContext applicationContext) {
         this.applicationContext = applicationContext;
     }
@@ -47,21 +53,30 @@ public class FeignClientInvoker {
     }
 
     private Object getFeignClient(String serviceName) {
-        // 优先从缓存获取
         if (feignClientCache.containsKey(serviceName)) {
             return feignClientCache.get(serviceName);
         }
-        
-        // 动态查找标注@FeignClient的Bean
-        Map<String, Object> feignClients = applicationContext.getBeansWithAnnotation(FeignClient.class);
-        for (Object bean : feignClients.values()) {
-            FeignClient annotation = bean.getClass().getInterfaces()[0].getAnnotation(FeignClient.class);
-            if (annotation != null && serviceName.equals(annotation.name())) {
-                feignClientCache.put(serviceName, bean);
-                return bean;
+        Object feignClient = null;
+        try {
+            // 默认查询声明实例
+            // 动态查找标注@FeignClient的Bean
+            Map<String, Object> feignClients = applicationContext.getBeansWithAnnotation(FeignClient.class);
+            for (Object bean : feignClients.values()) {
+                FeignClient annotation = bean.getClass().getInterfaces()[0].getAnnotation(FeignClient.class);
+                if (annotation != null && serviceName.equals(annotation.name())) {
+                    feignClientCache.put(serviceName, bean);
+                    feignClient = bean;
+                }
             }
+            // 构建动态FeignClient
+            if (feignClient == null) {
+                feignClient = feignClientFactory.getClient(serviceName, BaseFeign.class);
+            }
+            feignClientCache.put(serviceName, feignClient);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("未找到服务: " + serviceName);
         }
-        throw new IllegalArgumentException("未找到服务: " + serviceName);
+        return feignClient;
     }
 
     private Method getTargetMethod(Object feignClient, String methodName, Object[] args) 

+ 174 - 0
yt-question/yt-question-feign/src/main/java/com/ytpm/question/base/DynamicFeignClient.java

@@ -0,0 +1,174 @@
+package com.ytpm.question.base;
+
+
+import com.ytpm.agent.param.AdRecordListParam;
+import com.ytpm.agent.param.AuditCheckParam;
+import com.ytpm.agent.view.AgentAdGroupStaticsVO;
+import com.ytpm.agent.view.AgentTopCountView;
+import com.ytpm.app.model.YtAppDefaultConfig;
+import com.ytpm.app.model.YtDyzAdRecord;
+import com.ytpm.app.model.YtUser;
+import com.ytpm.app.param.AppConfigUpdateParam;
+import com.ytpm.app.param.AppQueryUserTodayTimeParam;
+import com.ytpm.app.param.AppUserParam;
+import com.ytpm.app.param.AppUserQueryParam;
+import com.ytpm.app.param.AppUserTodayBannedParam;
+import com.ytpm.app.param.YtAppUserListParam;
+import com.ytpm.app.view.WxDefaultConfig;
+import com.ytpm.app.view.YtAppUserListView;
+import com.ytpm.general.Result;
+import com.ytpm.general.ResultTable;
+import com.ytpm.middle.view.DashboardRankingListVO;
+import com.ytpm.middle.view.DashboardRevenueVO;
+import com.ytpm.middle.view.DashboardRiskVO;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author lih
+ * @date 2025-10-24 09:15
+ */
+@FeignClient(name = "dynamic-client", url = "dynamic-url")
+public interface DynamicFeignClient {
+
+    @GetMapping("{serviceName}/visitor/getLoginDitchCount")
+    int getLoginDitchCount(@PathVariable("serviceName") String serviceName,
+                           @RequestParam("deviceId") String deviceId,
+                           @RequestParam("hours") Integer hours);
+
+    @GetMapping("{serviceName}/visitor/getDitchCount")
+    int getDitchCount(@RequestParam("deviceId") String deviceId,
+                      @RequestParam("hours") Integer hours);
+
+    @GetMapping("{serviceName}/user/getMonthRegistryUser")
+    List<YtUser> getMonthRegistryUser(@PathVariable("serviceName") String serviceName,
+                                      @RequestParam("appIds") String appIds,
+                                      @RequestParam("type") Integer type);
+
+    @PostMapping("{serviceName}/user/queryAll")
+    ResultTable<YtAppUserListView> queryAll(@PathVariable("serviceName") String serviceName,
+                                            @RequestBody YtAppUserListParam param);
+
+    @GetMapping("{serviceName}/user/getUserInfo")
+    Result<YtUser> getUserInfo(@PathVariable("serviceName") String serviceName, @RequestParam("userId") String userId);
+
+    @PostMapping("{serviceName}/user/getUserList")
+    ResultTable<YtUser> getUserList(@PathVariable("serviceName") String serviceName, @RequestBody AppUserParam param);
+
+    @PostMapping("{serviceName}/user/getUserAll")
+    ResultTable<YtUser> getUserAll(@PathVariable("serviceName") String serviceName, @RequestBody AppUserParam param);
+
+    @PostMapping("{serviceName}/user/updateUserInfo")
+    Result<?> updateUserInfo(@PathVariable("serviceName") String serviceName, @RequestBody YtUser dyzUser);
+
+    @GetMapping("{serviceName}/user/adRecords")
+    ResultTable<YtDyzAdRecord> adRecords(@PathVariable("serviceName") String serviceName,
+                                         @RequestParam(name = "userId", required = true) String userId,
+                                         @RequestParam(name = "adsourceType", required = false) Integer adsourceType);
+
+    @PostMapping("{serviceName}/user/adRecords/page")
+    ResultTable<YtDyzAdRecord> adRecordsPage(@PathVariable("serviceName") String serviceName,
+                                             @RequestBody AdRecordListParam param);
+
+    @GetMapping("{serviceName}/user/adRecords/count/month")
+    Result<Integer> queryRecordMonthCount(@PathVariable("serviceName") String serviceName,
+                                          @RequestParam(name = "userId") String userId,
+                                          @RequestParam(name = "adSourceType", required = false) Integer adSourceType,
+                                          @RequestParam(name = "startTime", required = false) String startTime);
+
+    @PostMapping("{serviceName}/user/queryUserByTime")
+    List<YtUser> queryUserByTime(@PathVariable("serviceName") String serviceName,
+                                 @RequestBody AppUserQueryParam appUserQueryParam);
+
+    @PostMapping("{serviceName}/user/queryUserByTodayTime")
+    int[] queryUserByTodayTime(@PathVariable("serviceName") String serviceName,
+                               @RequestBody AppQueryUserTodayTimeParam appQueryUserTodayTimeParam);
+
+    @PostMapping("{serviceName}/user/queryLoginRecords")
+    List<String> queryLoginRecords(@PathVariable("serviceName") String serviceName,
+                                   @RequestBody AppUserQueryParam appUserQueryParam);
+
+    @GetMapping("{serviceName}/user/queryByOpenid")
+    List<YtUser> queryByOpenid(@PathVariable("serviceName") String serviceName,
+                               @RequestParam("openid") String openid);
+
+    @PostMapping("{serviceName}/wx/saveAppConfig")
+    Result<String> saveAppConfig(@PathVariable("serviceName") String serviceName,
+                                 @RequestBody YtAppDefaultConfig defaultConfig);
+
+    @PostMapping("{serviceName}/wx/updateAppConfig")
+    Result<String> updateAppConfig(@PathVariable("serviceName") String serviceName,
+                                   @RequestBody YtAppDefaultConfig defaultConfig);
+
+    @PostMapping("{serviceName}/wx/updateAppsConfig")
+    void updateAppsConfig(@PathVariable("serviceName") String serviceName,
+                          @RequestBody AppConfigUpdateParam param);
+
+    @GetMapping("{serviceName}/wx/getConfigs")
+    List<WxDefaultConfig> getConfigs(@PathVariable("serviceName") String serviceName,
+                                     @RequestParam(name = "appIds") String appIds);
+
+    @PostMapping("{serviceName}/user/queryTodayBanned")
+    List<YtUser> queryTodayBanned(@PathVariable("serviceName") String serviceName,
+                                  @RequestBody AppUserTodayBannedParam appUserTodayBannedParam);
+
+    @GetMapping("{serviceName}/ad/getAdCount")
+    Map<String, BigDecimal> getAdCount(@PathVariable("serviceName") String serviceName,
+                                       @RequestParam(name = "appIds") String appIds);
+
+    @GetMapping("{serviceName}/ad/getAppTopCount")
+    AgentTopCountView getAppTopCount(@PathVariable("serviceName") String serviceName,
+                                     @RequestParam(name = "appIds") String appIds);
+
+    @GetMapping("{serviceName}/ad/getAppRankingList")
+    DashboardRankingListVO queryRankingList(@PathVariable("serviceName") String serviceName,
+                                            @RequestParam(name = "sortBy") Integer sortBy,
+                                            @RequestParam(name = "limit") Integer limit);
+
+    @GetMapping("{serviceName}/ad/revenueStatics")
+    DashboardRevenueVO revenueStatics(@PathVariable("serviceName") String serviceName,
+                                      @RequestParam(name = "apkIds") String apkIds);
+
+    @GetMapping("{serviceName}/ad/userStatics")
+    DashboardRiskVO userStatics(@PathVariable("serviceName") String serviceName,
+                                @RequestParam(name = "appId") String appId);
+
+    @GetMapping("{serviceName}/ad/getAgentProfit")
+    List<AgentAdGroupStaticsVO> getAgentProfit(@PathVariable("serviceName") String serviceName,
+                                               @RequestParam(name = "appIds") String appIds);
+
+    @PostMapping("{serviceName}/user/getRevenueByTime")
+    BigDecimal getRevenueByTime(@PathVariable("serviceName") String serviceName,
+                                @RequestBody YtAppUserListParam param);
+
+    @GetMapping("{serviceName}/wx/delDefaultConfig")
+    void delAppConfig(@PathVariable("serviceName") String serviceName,
+                      @RequestParam("appId") String appId);
+
+    @PostMapping("{serviceName}/user/unLockUser")
+    void unLockUser(@PathVariable("serviceName") String serviceName,
+                    @RequestParam("userIds") String userIds);
+
+    /**
+     * 锁定用户
+     */
+    @GetMapping("{serviceName}/user/lockUser")
+    YtUser lockUser(@PathVariable("serviceName") String serviceName,
+                    @RequestParam(name = "userId") String userId,
+                    @RequestParam("userStatus") Integer userStatus);
+
+    /**
+     * 批量审核用户
+     */
+    @PostMapping("{serviceName}/user/batchAudit")
+    void batchAudit(@PathVariable("serviceName") String serviceName,
+                    @RequestBody AuditCheckParam auditCheckParam);
+}

+ 38 - 0
yt-risk/risk-manage/src/main/java/com/ytpm/config/feign/FeignBuilderConfig.java

@@ -0,0 +1,38 @@
+package com.ytpm.config.feign;
+
+
+import feign.Feign;
+import feign.Request;
+import feign.Retryer;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
+import org.springframework.cloud.openfeign.support.SpringDecoder;
+import org.springframework.cloud.openfeign.support.SpringEncoder;
+import org.springframework.cloud.openfeign.support.SpringMvcContract;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 自定义FeignClient 构造
+ * @author lih
+ * @date 2025-10-24 09:41
+ */
+@Configuration
+public class FeignBuilderConfig {
+
+    @Bean
+    @Scope("prototype")
+    public Feign.Builder feignBuilder(ObjectFactory<HttpMessageConverters> messageConverters) {
+        return Feign.builder()
+                .contract(new SpringMvcContract())
+                .encoder(new SpringEncoder(messageConverters))
+                .decoder(new SpringDecoder(messageConverters))
+                .retryer(new Retryer.Default(100, TimeUnit.SECONDS.toMicros(1), 3))
+                .options(new Request.Options(3000, 3000))
+                .requestInterceptor(new FeignConfiguration());
+
+    }
+}

+ 53 - 0
yt-risk/risk-manage/src/main/java/com/ytpm/config/handle/DynamicFeignClientFactory.java

@@ -0,0 +1,53 @@
+package com.ytpm.config.handle;
+
+
+import feign.Feign;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.client.ServiceInstance;
+import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
+import org.springframework.stereotype.Component;
+
+/**
+ * 动态FeignClient 构造工厂
+ * @author lih
+ * @date 2025-10-24 09:45
+ */
+@Slf4j
+@Component
+public class DynamicFeignClientFactory {
+
+    private final Feign.Builder feignBuilder;
+    private final LoadBalancerClient loadBalancerClient;
+
+
+    public DynamicFeignClientFactory(Feign.Builder feignBuilder,
+                                     LoadBalancerClient loadBalancerClient) {
+        this.feignBuilder = feignBuilder;
+        this.loadBalancerClient = loadBalancerClient;
+    }
+
+    public <T> T getClient(String serviceName, Class<T> clazz) {
+        int maxRetry = 3;
+        int retryCount = 0;
+        Exception lastException = null;
+
+        while (retryCount < maxRetry) {
+            try {
+                ServiceInstance instance = loadBalancerClient.choose(serviceName);
+                if (instance == null) {
+                    throw new RuntimeException("未找到可用的服务实例:" + serviceName);
+                }
+                String url = instance.getUri().toString();
+                log.info("为服务{}构建FeignClient,目标地址为:{}", serviceName, url);
+                return feignBuilder.target(clazz, url);
+            } catch (Exception e) {
+                lastException = e;
+                log.warn("第 {} 次尝试获取FeignClient失败,服务名:{},错误信息:{}", retryCount + 1, serviceName, e.getMessage());
+                retryCount++;
+
+            }
+        }
+        throw new RuntimeException("创建FeignClient失败,服务名:" + serviceName, lastException);
+    }
+
+}

+ 29 - 9
yt-risk/risk-manage/src/main/java/com/ytpm/util/FeignClientInvoker.java

@@ -1,19 +1,21 @@
 package com.ytpm.util;
 
+import com.ytpm.config.handle.DynamicFeignClientFactory;
+import com.ytpm.question.base.BaseFeign;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
-import org.springframework.beans.factory.annotation.Value;
 
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
 import java.lang.reflect.Method;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import javax.annotation.PostConstruct;
-import javax.annotation.Resource;
 
 /**
  * FeignClient公共调用类
@@ -33,6 +35,8 @@ public class FeignClientInvoker {
     private Semaphore bulkhead;
     @Resource(name = "riskScheduledExecutorService")
     private ScheduledExecutorService scheduledExecutorService;
+    @Resource
+    private DynamicFeignClientFactory feignClientFactory;
 
     public FeignClientInvoker(ApplicationContext applicationContext) {
         this.applicationContext = applicationContext;
@@ -78,7 +82,12 @@ public class FeignClientInvoker {
     }
 
     private Object getFeignClient(String serviceName) {
-        return feignClientCache.computeIfAbsent(serviceName, name -> {
+        if (feignClientCache.containsKey(serviceName)) {
+            return feignClientCache.get(serviceName);
+        }
+        Object feignClient = null;
+        try {
+            // 默认查询声明实例
             Map<String, Object> feignClients = applicationContext.getBeansWithAnnotation(FeignClient.class);
             for (Object bean : feignClients.values()) {
                 Class<?>[] interfaces = bean.getClass().getInterfaces();
@@ -86,14 +95,25 @@ public class FeignClientInvoker {
                     FeignClient annotation = iface.getAnnotation(FeignClient.class);
                     if (annotation != null) {
                         String annName = annotation.name().isEmpty() ? annotation.value() : annotation.name();
-                        if (name.equals(annName)) {
-                            return bean;
+                        if (serviceName.equals(annName)) {
+                            feignClient = bean;
+                            break;
                         }
                     }
                 }
+                if (feignClient != null) {
+                    break;
+                }
             }
-            throw new IllegalArgumentException("未找到服务: " + name);
-        });
+            // 构建动态FeignClient
+            if (feignClient == null) {
+                feignClient = feignClientFactory.getClient(serviceName, BaseFeign.class);
+            }
+            feignClientCache.put(serviceName, feignClient);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("未找到服务: " + serviceName);
+        }
+        return feignClient;
     }
 
     private Method getTargetMethod(Object feignClient, String methodName, Object[] args)