This commit is contained in:
2025-11-04 22:59:55 +08:00
parent 0146e0776a
commit 41f338446d
7 changed files with 1386 additions and 0 deletions

View File

@@ -0,0 +1,248 @@
package com.ruoyi.jarvis.util;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.http.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* 腾讯文档API工具类
*
* @author system
*/
public class TencentDocApiUtil {
private static final Logger log = LoggerFactory.getLogger(TencentDocApiUtil.class);
/**
* 获取访问令牌
*
* @param appId 应用ID
* @param appSecret 应用密钥
* @param code 授权码
* @param redirectUri 回调地址
* @return 包含access_token和refresh_token的JSON对象
*/
public static JSONObject getAccessToken(String appId, String appSecret, String code, String redirectUri, String tokenUrl) {
try {
// 构建请求参数
StringBuilder params = new StringBuilder();
params.append("grant_type=authorization_code");
params.append("&client_id=").append(appId);
params.append("&client_secret=").append(appSecret);
params.append("&code=").append(code);
params.append("&redirect_uri=").append(java.net.URLEncoder.encode(redirectUri, "UTF-8"));
String response = HttpUtils.sendPost(tokenUrl, params.toString());
log.info("获取访问令牌响应: {}", response);
return JSON.parseObject(response);
} catch (Exception e) {
log.error("获取访问令牌失败", e);
throw new RuntimeException("获取访问令牌失败: " + e.getMessage(), e);
}
}
/**
* 刷新访问令牌
*
* @param appId 应用ID
* @param appSecret 应用密钥
* @param refreshToken 刷新令牌
* @param refreshTokenUrl 刷新令牌地址
* @return 包含新的access_token和refresh_token的JSON对象
*/
public static JSONObject refreshAccessToken(String appId, String appSecret, String refreshToken, String refreshTokenUrl) {
try {
// 构建请求参数
StringBuilder params = new StringBuilder();
params.append("grant_type=refresh_token");
params.append("&client_id=").append(appId);
params.append("&client_secret=").append(appSecret);
params.append("&refresh_token=").append(refreshToken);
String response = HttpUtils.sendPost(refreshTokenUrl, params.toString());
log.info("刷新访问令牌响应: {}", response);
return JSON.parseObject(response);
} catch (Exception e) {
log.error("刷新访问令牌失败", e);
throw new RuntimeException("刷新访问令牌失败: " + e.getMessage(), e);
}
}
/**
* 调用腾讯文档API
*
* @param accessToken 访问令牌
* @param apiUrl API地址
* @param method 请求方法 GET/POST/PUT/DELETE
* @param body 请求体JSON格式
* @return API响应
*/
public static JSONObject callApi(String accessToken, String apiUrl, String method, String body) {
try {
URL url = new URL(apiUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(method);
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setConnectTimeout(10000);
conn.setReadTimeout(30000);
// 写入请求体
if (body != null && !body.isEmpty()) {
try (OutputStream os = conn.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
osw.write(body);
osw.flush();
}
}
// 读取响应
int statusCode = conn.getResponseCode();
BufferedReader reader;
if (statusCode >= 200 && statusCode < 300) {
reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
} else {
reader = new BufferedReader(new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8));
}
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
String responseStr = response.toString();
log.debug("腾讯文档API响应: statusCode={}, response={}", statusCode, responseStr);
JSONObject result = JSON.parseObject(responseStr);
// 检查错误码
if (result.containsKey("error_code") && result.getIntValue("error_code") != 0) {
String errorMsg = result.getString("error_msg");
log.error("腾讯文档API调用失败: error_code={}, error_msg={}",
result.getIntValue("error_code"), errorMsg);
throw new RuntimeException("腾讯文档API调用失败: " + errorMsg);
}
if (statusCode < 200 || statusCode >= 300) {
throw new RuntimeException("HTTP请求失败: statusCode=" + statusCode + ", response=" + responseStr);
}
return result;
} catch (Exception e) {
log.error("调用腾讯文档API失败: url={}, method={}", apiUrl, method, e);
throw new RuntimeException("调用腾讯文档API失败: " + e.getMessage(), e);
}
}
/**
* 读取表格数据
*
* @param accessToken 访问令牌
* @param fileId 文件ID
* @param sheetId 工作表ID
* @param range 范围,例如 "A1:Z100"
* @param apiBaseUrl API基础地址
* @return 表格数据
*/
public static JSONObject readSheetData(String accessToken, String fileId, String sheetId, String range, String apiBaseUrl) {
String apiUrl = String.format("%s/files/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
return callApi(accessToken, apiUrl, "GET", null);
}
/**
* 写入表格数据
*
* @param accessToken 访问令牌
* @param fileId 文件ID
* @param sheetId 工作表ID
* @param range 范围,例如 "A1"
* @param values 要写入的数据,二维数组格式 [[["值1"], ["值2"]], [["值3"], ["值4"]]]
* @param apiBaseUrl API基础地址
* @return 写入结果
*/
public static JSONObject writeSheetData(String accessToken, String fileId, String sheetId, String range, Object values, String apiBaseUrl) {
String apiUrl = String.format("%s/files/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
JSONObject requestBody = new JSONObject();
requestBody.put("values", values);
return callApi(accessToken, apiUrl, "PUT", requestBody.toJSONString());
}
/**
* 追加表格数据(在最后一行追加)
*
* @param accessToken 访问令牌
* @param fileId 文件ID
* @param sheetId 工作表ID
* @param values 要追加的数据,二维数组格式
* @param apiBaseUrl API基础地址
* @return 追加结果
*/
public static JSONObject appendSheetData(String accessToken, String fileId, String sheetId, Object values, String apiBaseUrl) {
// 先获取表格信息,找到最后一行
String infoUrl = String.format("%s/files/%s/sheets/%s", apiBaseUrl, fileId, sheetId);
JSONObject sheetInfo = callApi(accessToken, infoUrl, "GET", null);
// 获取行数根据实际API响应调整
int rowCount = 0;
if (sheetInfo.containsKey("row_count")) {
rowCount = sheetInfo.getIntValue("row_count");
} else if (sheetInfo.containsKey("data") && sheetInfo.getJSONObject("data").containsKey("row_count")) {
rowCount = sheetInfo.getJSONObject("data").getIntValue("row_count");
}
if (rowCount == 0) {
rowCount = 1; // 至少有一行(表头)
}
// 计算要写入的起始位置(假设追加一行数据)
String range = "A" + (rowCount + 1);
return writeSheetData(accessToken, fileId, sheetId, range, values, apiBaseUrl);
}
/**
* 获取文件信息
*
* @param accessToken 访问令牌
* @param fileId 文件ID
* @param apiBaseUrl API基础地址
* @return 文件信息
*/
public static JSONObject getFileInfo(String accessToken, String fileId, String apiBaseUrl) {
String apiUrl = String.format("%s/files/%s", apiBaseUrl, fileId);
return callApi(accessToken, apiUrl, "GET", null);
}
/**
* 获取工作表列表
*
* @param accessToken 访问令牌
* @param fileId 文件ID
* @param apiBaseUrl API基础地址
* @return 工作表列表
*/
public static JSONObject getSheetList(String accessToken, String fileId, String apiBaseUrl) {
String apiUrl = String.format("%s/files/%s/sheets", apiBaseUrl, fileId);
return callApi(accessToken, apiUrl, "GET", null);
}
}