dangsn
2024-06-06 33321f5486fd586fda6fd3f46b7e71754fede28b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package com.vci.starter.web.autoconfigure;
 
import com.alibaba.fastjson.JSONObject;
import com.vci.starter.web.constant.TokenKeyConstant;
import com.vci.starter.web.pagemodel.SessionInfo;
import com.vci.starter.web.util.VciBaseUtil;
import feign.*;
import feign.form.spring.SpringFormEncoder;
import feign.okhttp.OkHttpClient;
import okhttp3.ConnectionPool;
import org.slf4j.MDC;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.bind.annotation.RequestMethod;
 
import javax.annotation.PreDestroy;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;
 
/**
 * feign客户端配置
 * @author weidy
 * @date 2019/11/5 10:23 AM
 */
@AutoConfigureAfter(AppAutoConfigure.class)
@Configuration
public class FeignConfig implements RequestInterceptor {
 
    /**
     * 使用okHttpClient来传输数据
     */
    private okhttp3.OkHttpClient okHttpClient;
 
    /**
     * 当前应用程序的私钥
     */
    @Value("${app.privateTokenKey:}")
    private String privateTokenKey;
 
    /**
     * 客户端连接池
     * @param httpClientProperties http链接配置
     * @param connectionPoolFactory 连接池工程
     * @return 连接池
     */
    @Bean
    @ConditionalOnMissingBean({ConnectionPool.class})
    public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) {
        Integer maxTotalConnections = httpClientProperties.getMaxConnections();
        Long timeToLive = httpClientProperties.getTimeToLive();
        TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
        return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
    }
 
    /**
     * 初始化okHttp的客户端对象
     * @param httpClientFactory 客户端链接工程
     * @param connectionPool 连接池
     * @param httpClientProperties 客户端链接配置
     * @return
     */
    @Bean
    public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
        Boolean followRedirects = httpClientProperties.isFollowRedirects();
        Integer connectTimeout = httpClientProperties.getConnectionTimeout();
        Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
        this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation).connectTimeout((long)connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects).readTimeout(30*60,TimeUnit.SECONDS).connectionPool(connectionPool).build();
        return this.okHttpClient;
    }
 
    /**
     * 销毁之前关闭okHttp
     */
    @PreDestroy
    public void destroy() {
        if (this.okHttpClient != null) {
            this.okHttpClient.dispatcher().executorService().shutdown();
            this.okHttpClient.connectionPool().evictAll();
        }
 
    }
 
    /**
     * 配置feign的客户端类
     * @param client 客户端链接对象
     * @return 客户端连接对象
     */
    @Bean
    @ConditionalOnMissingBean({Client.class})
    public Client feignClient(okhttp3.OkHttpClient client) {
        return new OkHttpClient(client);
    }
 
    /**
     * 设置请求的连接时间和读取的时间
     * @param client 链接的客户端
     * @return 链接的客户端
     */
    @Bean
    public Request.Options feignOptions(okhttp3.OkHttpClient client) {
        return new Request.Options(client.connectTimeoutMillis(), client.readTimeoutMillis());
    }
 
    /**
     * 配置feign的日志等级
     * @return 日志等级
     */
    @Bean
    Logger.Level feignLoggerLevel() {
        //这里记录所有,根据实际情况选择合适的日志level
        return Logger.Level.FULL;
    }
 
    /**
     * 将fastjson设置到decoder中,解码器
     * @return decoder
     */
    @Bean
    public ResponseEntityDecoder feignDecoder(){
        HttpMessageConverter fastJsonConverter = SpringMVCConfig.createFastJsonConverter();
        ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(new StringHttpMessageConverter(StandardCharsets.UTF_8),fastJsonConverter);
        return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
    }
 
    /**
     * 将fastjson设置到encoder中,编码器
     * @return encoder
     */
    @Bean
    public SpringFormEncoder feignEncoder(){
        HttpMessageConverter fastJsonConverter = SpringMVCConfig.createFastJsonConverter();
        ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(new StringHttpMessageConverter(StandardCharsets.UTF_8),fastJsonConverter);
        return new SpringFormEncoder(new SpringEncoder(objectFactory));
    }
 
 
    /**
     * 在发送请求的时候,设置拦截器,将用户的token和当前服务的私钥传递过去,并且传递当前服务的名称用于追溯日志
     * @param requestTemplate 请求的目标
     */
    @Override
    public void apply(RequestTemplate requestTemplate) {
        //如果是GET,但是body里有内容
        Request.Body body = requestTemplate.requestBody();
        if(RequestMethod.GET.toString().equalsIgnoreCase(requestTemplate.method())
                && body != null && body.length() > 0){
            //主要是列表查询和树查询这几个地方,支持map的传输
            String bodyString = new String(body.asBytes(),requestTemplate.requestCharset());
            try{
                JSONObject jsonObject = JSONObject.parseObject(bodyString);
                for(String key : jsonObject.keySet()){
                    Object value = jsonObject.get(key);
                    if(value == null){
                        requestTemplate.query(key,"");
                    }else{
                        if(value instanceof Map){
                            //说明这个属性是map
                            Map mapValues = (Map)value;
                            for(Object mapKey : mapValues.keySet()){
                                Object mapQueryValue = mapValues.get(mapKey);
                                String mapQueryValueString = "";
                                if(mapQueryValue == null){
                                    mapQueryValueString = "";
                                }else{
                                    mapQueryValueString = VciBaseUtil.getStringValueFromObject(mapQueryValue);
                                }
                                requestTemplate.query(key + "[\"" + mapKey.toString() + "\"]",mapQueryValueString);
                            }
                        }else{
                            requestTemplate.query(key,VciBaseUtil.getStringValueFromObject(value));
                        }
                    }
                }
                requestTemplate.body(Request.Body.empty());
            }catch (Throwable e){
                //可能不是json的类型
            }
        }
 
        //需要传递用户的token
        //系统的token,用于权限验证
        //日志的链接ID
        SessionInfo sessionInfo = VciBaseUtil.getCurrentUserSessionInfoNotException();
        if(sessionInfo != null){
            requestTemplate.header(TokenKeyConstant.USER_TOKEN_KEY,sessionInfo.getToken());
        }else{
            requestTemplate.header(TokenKeyConstant.USER_TOKEN_KEY,"");
        }
        requestTemplate.header(TokenKeyConstant.SYSTEM_PRIVATE_KEY,getPrivateTokenKey());
 
        String logTraceId = MDC.get(TokenKeyConstant.TRACE_ID);
        if(logTraceId == null){
            logTraceId = "";
        }
        requestTemplate.header(TokenKeyConstant.LOG_TRACE_ID_KEY,logTraceId);
        //设置语言
        requestTemplate.header(TokenKeyConstant.LANGUAGE_KEY,LocaleContextHolder.getLocale().toLanguageTag());
    }
 
    public String getPrivateTokenKey() {
        return privateTokenKey;
    }
 
    public void setPrivateTokenKey(String privateTokenKey) {
        this.privateTokenKey = privateTokenKey;
    }
 
}