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;
|
}
|
}
|