liusheng
5 天以前 a2562974917ee6ef93325bb0a9ba785c82222792
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
package com.ruoyi.project.utils;
 
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.http.HttpUtils;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
 
/**
 * 钉钉API代理客户端
 * 通过Nginx代理访问钉钉API,解决内网访问外网的问题
 *
 * @author 
 * @date 2025-01-01
 */
@Slf4j
@Component
public class DingTalkProxyClient {
 
    @Value("${dingtalk.proxy.enabled:false}")
    private boolean proxyEnabled;
 
    @Value("${dingtalk.proxy.url:https://oapi.dingtalk.com}")
    private String proxyUrl;
 
    @Value("${dingAppid}")
    private String dingAppid;
 
    @Value("${dingAppSecret}")
    private String dingAppSecret;
 
    private String cachedAccessToken;
    private long tokenExpireTime;
 
    /**
     * 获取访问令牌
     */
    public String getAccessToken() throws ApiException {
        // 检查缓存的token是否仍然有效(预留30秒缓冲时间)
        if (cachedAccessToken != null && System.currentTimeMillis() < tokenExpireTime - 30000) {
            return cachedAccessToken;
        }
 
        String url;
        if (proxyEnabled) {
            url = proxyUrl + "/gettoken";
        } else {
            url = "https://oapi.dingtalk.com/gettoken";
        }
 
        Map<String, String> params = new HashMap<>();
        params.put("appkey", dingAppid);
        params.put("appsecret", dingAppSecret);
        params.put("grant_type", "client_credentials");
 
        String paramString = buildParamString(params);
        String response = HttpUtils.sendGet(url, paramString);
        JSONObject result = JSON.parseObject(response);
 
        if (result.getInteger("errcode") == 0) {
            cachedAccessToken = result.getString("access_token");
            // token通常有效期为7200秒,这里设置为7000秒后过期
            tokenExpireTime = System.currentTimeMillis() + 7000 * 1000L;
            return cachedAccessToken;
        } else {
            throw new ApiException("获取钉钉访问令牌失败: " + result.getString("errmsg"));
        }
    }
 
    /**
     * 将Map参数转换为URL编码的字符串
     */
    private String buildParamString(Map<String, String> params) {
        StringBuilder paramString = new StringBuilder();
        if (params != null) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if (paramString.length() > 0) {
                    paramString.append("&");
                }
                try {
                    paramString.append(URLEncoder.encode(entry.getKey(), "UTF-8"))
                            .append("=")
                            .append(URLEncoder.encode(entry.getValue() != null ? entry.getValue() : "", "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    // 这种情况不应该发生,因为UTF-8是标准编码
                    paramString.append(entry.getKey())
                            .append("=")
                            .append(entry.getValue() != null ? entry.getValue() : "");
                }
            }
        }
        return paramString.toString();
    }
 
    /**
     * 执行GET请求
     */
    public String executeGet(String apiPath, Map<String, String> params) throws ApiException {
        String url;
        if (proxyEnabled) {
            url = proxyUrl + apiPath;
        } else {
            url = "https://oapi.dingtalk.com" + apiPath;
        }
 
        String paramString = buildParamString(params);
        return HttpUtils.sendGet(url, paramString);
    }
 
    /**
     * 执行POST请求,支持JSON Content-Type
     */
    public String executePost(String apiPathWithParams, String params) throws ApiException {
        String url;
        if (proxyEnabled) {
            url = proxyUrl + apiPathWithParams;
        } else {
            url = "https://oapi.dingtalk.com" + apiPathWithParams;
        }
 
        return sendJsonPost(url, params);
    }
 
    /**
     * 发送JSON格式的POST请求
     */
    private String sendJsonPost(String url, String jsonData) throws ApiException {
        HttpURLConnection connection = null;
        try {
            URL obj = new URL(url);
            connection = (HttpURLConnection) obj.openConnection();
 
            // 设置请求方法
            connection.setRequestMethod("POST");
 
            // 设置请求头
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setRequestProperty("Accept", "application/json");
            connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36");
 
            // 发送POST请求
            connection.setDoOutput(true);
            try (OutputStream os = connection.getOutputStream()) {
                byte[] input = jsonData.getBytes("utf-8");
                os.write(input, 0, input.length);
            }
 
            // 读取响应
            int responseCode = connection.getResponseCode();
            StringBuilder response = new StringBuilder();
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(connection.getInputStream(), "utf-8"))) {
                String responseLine;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
            }
 
            return response.toString();
        } catch (Exception e) {
            throw new ApiException("POST请求失败: " + e.getMessage());
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
 
    /**
     * 执行带访问令牌的GET请求
     */
    public String executeGetWithToken(String apiPath, Map<String, String> params) throws ApiException {
        if (params == null) {
            params = new HashMap<>();
        }
        params.put("access_token", getAccessToken());
        return executeGet(apiPath, params);
    }
 
    /**
     * 执行带访问令牌的POST请求
     */
    public String executePostWithToken(String apiPath, String params) throws ApiException {
        String fullParams;
        if (params.contains("access_token=")) {
            fullParams = params;
        } else {
            // 对于JSON格式的请求体,access_token应该已经包含在params中
            fullParams = params;
        }
        return executePost(apiPath, fullParams);
    }
 
    public boolean isProxyEnabled() {
        return proxyEnabled;
    }
}