1
This commit is contained in:
@@ -29,8 +29,8 @@ public class TencentDocConfig {
|
||||
/** 授权回调地址 */
|
||||
private String redirectUri;
|
||||
|
||||
/** API基础地址 - V3版本(注意:根据文档路径推测应该是 /open/api/v3) */
|
||||
private String apiBaseUrl = "https://docs.qq.com/open/api/v3";
|
||||
/** API基础地址 - V3版本(实际路径:/openapi/spreadsheet/v3) */
|
||||
private String apiBaseUrl = "https://docs.qq.com/openapi/spreadsheet/v3";
|
||||
|
||||
/** OAuth授权地址 */
|
||||
private String oauthUrl = "https://docs.qq.com/oauth/v2/authorize";
|
||||
|
||||
@@ -116,6 +116,13 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
throw new IllegalArgumentException("订单列表不能为空");
|
||||
}
|
||||
|
||||
// 获取用户信息(包含Open-Id)
|
||||
JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken);
|
||||
String openId = userInfo.getString("openId");
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
throw new RuntimeException("无法获取Open-Id,请检查Access Token是否有效");
|
||||
}
|
||||
|
||||
// 构建要写入的数据(二维数组格式)
|
||||
JSONArray values = new JSONArray();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
@@ -139,7 +146,15 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
}
|
||||
|
||||
// 追加数据到表格
|
||||
return TencentDocApiUtil.appendSheetData(accessToken, fileId, sheetId, values, tencentDocConfig.getApiBaseUrl());
|
||||
return TencentDocApiUtil.appendSheetData(
|
||||
accessToken,
|
||||
tencentDocConfig.getAppId(),
|
||||
openId,
|
||||
fileId,
|
||||
sheetId,
|
||||
values,
|
||||
tencentDocConfig.getApiBaseUrl()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("上传物流信息到表格失败", e);
|
||||
throw new RuntimeException("上传物流信息失败: " + e.getMessage(), e);
|
||||
@@ -153,6 +168,13 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
throw new IllegalArgumentException("订单信息不能为空");
|
||||
}
|
||||
|
||||
// 获取用户信息(包含Open-Id)
|
||||
JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken);
|
||||
String openId = userInfo.getString("openId");
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
throw new RuntimeException("无法获取Open-Id,请检查Access Token是否有效");
|
||||
}
|
||||
|
||||
// 构建单行数据
|
||||
JSONArray values = new JSONArray();
|
||||
JSONArray row = new JSONArray();
|
||||
@@ -173,7 +195,15 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
values.add(row);
|
||||
|
||||
// 追加数据到表格
|
||||
return TencentDocApiUtil.appendSheetData(accessToken, fileId, sheetId, values, tencentDocConfig.getApiBaseUrl());
|
||||
return TencentDocApiUtil.appendSheetData(
|
||||
accessToken,
|
||||
tencentDocConfig.getAppId(),
|
||||
openId,
|
||||
fileId,
|
||||
sheetId,
|
||||
values,
|
||||
tencentDocConfig.getApiBaseUrl()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("追加物流信息到表格失败", e);
|
||||
throw new RuntimeException("追加物流信息失败: " + e.getMessage(), e);
|
||||
@@ -183,7 +213,22 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
@Override
|
||||
public JSONObject readSheetData(String accessToken, String fileId, String sheetId, String range) {
|
||||
try {
|
||||
return TencentDocApiUtil.readSheetData(accessToken, fileId, sheetId, range, tencentDocConfig.getApiBaseUrl());
|
||||
// 获取用户信息(包含Open-Id)
|
||||
JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken);
|
||||
String openId = userInfo.getString("openId");
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
throw new RuntimeException("无法获取Open-Id,请检查Access Token是否有效");
|
||||
}
|
||||
|
||||
return TencentDocApiUtil.readSheetData(
|
||||
accessToken,
|
||||
tencentDocConfig.getAppId(),
|
||||
openId,
|
||||
fileId,
|
||||
sheetId,
|
||||
range,
|
||||
tencentDocConfig.getApiBaseUrl()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("读取表格数据失败", e);
|
||||
throw new RuntimeException("读取表格数据失败: " + e.getMessage(), e);
|
||||
@@ -193,7 +238,23 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
@Override
|
||||
public JSONObject writeSheetData(String accessToken, String fileId, String sheetId, String range, Object values) {
|
||||
try {
|
||||
return TencentDocApiUtil.writeSheetData(accessToken, fileId, sheetId, range, values, tencentDocConfig.getApiBaseUrl());
|
||||
// 获取用户信息(包含Open-Id)
|
||||
JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken);
|
||||
String openId = userInfo.getString("openId");
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
throw new RuntimeException("无法获取Open-Id,请检查Access Token是否有效");
|
||||
}
|
||||
|
||||
return TencentDocApiUtil.writeSheetData(
|
||||
accessToken,
|
||||
tencentDocConfig.getAppId(),
|
||||
openId,
|
||||
fileId,
|
||||
sheetId,
|
||||
range,
|
||||
values,
|
||||
tencentDocConfig.getApiBaseUrl()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("写入表格数据失败", e);
|
||||
throw new RuntimeException("写入表格数据失败: " + e.getMessage(), e);
|
||||
@@ -203,7 +264,20 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
@Override
|
||||
public JSONObject getFileInfo(String accessToken, String fileId) {
|
||||
try {
|
||||
return TencentDocApiUtil.getFileInfo(accessToken, fileId, tencentDocConfig.getApiBaseUrl());
|
||||
// 获取用户信息(包含Open-Id)
|
||||
JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken);
|
||||
String openId = userInfo.getString("openId");
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
throw new RuntimeException("无法获取Open-Id,请检查Access Token是否有效");
|
||||
}
|
||||
|
||||
return TencentDocApiUtil.getFileInfo(
|
||||
accessToken,
|
||||
tencentDocConfig.getAppId(),
|
||||
openId,
|
||||
fileId,
|
||||
tencentDocConfig.getApiBaseUrl()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("获取文件信息失败", e);
|
||||
throw new RuntimeException("获取文件信息失败: " + e.getMessage(), e);
|
||||
@@ -213,7 +287,20 @@ public class TencentDocServiceImpl implements ITencentDocService {
|
||||
@Override
|
||||
public JSONObject getSheetList(String accessToken, String fileId) {
|
||||
try {
|
||||
return TencentDocApiUtil.getSheetList(accessToken, fileId, tencentDocConfig.getApiBaseUrl());
|
||||
// 获取用户信息(包含Open-Id)
|
||||
JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken);
|
||||
String openId = userInfo.getString("openId");
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
throw new RuntimeException("无法获取Open-Id,请检查Access Token是否有效");
|
||||
}
|
||||
|
||||
return TencentDocApiUtil.getSheetList(
|
||||
accessToken,
|
||||
tencentDocConfig.getAppId(),
|
||||
openId,
|
||||
fileId,
|
||||
tencentDocConfig.getApiBaseUrl()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
log.error("获取工作表列表失败", e);
|
||||
throw new RuntimeException("获取工作表列表失败: " + e.getMessage(), e);
|
||||
|
||||
@@ -101,6 +101,71 @@ public class TencentDocApiUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用级账号 Token(推荐使用)
|
||||
*
|
||||
* @param appId 应用ID (client_id)
|
||||
* @param appSecret 应用密钥 (client_secret)
|
||||
* @return 包含 access_token、refresh_token、user_id(即Open-Id) 的JSON对象
|
||||
* 接口文档:https://docs.qq.com/open/document/app/oauth2/app-account-token.html
|
||||
*/
|
||||
public static JSONObject getAppAccountToken(String appId, String appSecret) {
|
||||
try {
|
||||
// 构建请求URL
|
||||
String apiUrl = "https://docs.qq.com/oauth/v2/app-account-token"
|
||||
+ "?client_id=" + appId
|
||||
+ "&client_secret=" + appSecret;
|
||||
|
||||
log.info("获取应用级账号Token - appId: {}", appId);
|
||||
|
||||
// 直接使用HttpURLConnection,不使用代理
|
||||
URL url = new URL(apiUrl);
|
||||
java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
conn.setConnectTimeout(10000);
|
||||
conn.setReadTimeout(30000);
|
||||
|
||||
// 读取响应
|
||||
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.info("获取应用级账号Token响应: statusCode={}, response={}", statusCode, responseStr);
|
||||
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
throw new RuntimeException("获取应用级账号Token失败: HTTP " + statusCode + ", response=" + responseStr);
|
||||
}
|
||||
|
||||
JSONObject result = JSON.parseObject(responseStr);
|
||||
|
||||
// 验证响应包含必要字段
|
||||
if (!result.containsKey("access_token") || !result.containsKey("user_id")) {
|
||||
throw new RuntimeException("应用级账号Token响应格式错误,缺少必要字段: " + responseStr);
|
||||
}
|
||||
|
||||
log.info("成功获取应用级账号Token - user_id(Open-Id): {}", result.getString("user_id"));
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取应用级账号Token失败", e);
|
||||
throw new RuntimeException("获取应用级账号Token失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*
|
||||
@@ -172,12 +237,14 @@ public class TencentDocApiUtil {
|
||||
* 调用腾讯文档API
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param clientId 应用ID(Client-Id)
|
||||
* @param openId 开放平台用户ID(Open-Id)
|
||||
* @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) {
|
||||
public static JSONObject callApi(String accessToken, String clientId, String openId, String apiUrl, String method, String body) {
|
||||
try {
|
||||
log.info("调用腾讯文档API: url={}, method={}", apiUrl, method);
|
||||
|
||||
@@ -187,7 +254,11 @@ public class TencentDocApiUtil {
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
|
||||
|
||||
conn.setRequestMethod(method);
|
||||
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
|
||||
// 根据腾讯文档官方API文档,使用以下三个请求头进行鉴权
|
||||
// 文档:https://docs.qq.com/open/document/app/openapi/v3/sheet/batchUpdate.html
|
||||
conn.setRequestProperty("Access-Token", accessToken);
|
||||
conn.setRequestProperty("Client-Id", clientId);
|
||||
conn.setRequestProperty("Open-Id", openId);
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
conn.setDoOutput(true);
|
||||
@@ -261,35 +332,39 @@ public class TencentDocApiUtil {
|
||||
* 读取表格数据 - V3 API
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param appId 应用ID
|
||||
* @param openId 开放平台用户ID
|
||||
* @param fileId 文件ID(在线表格的唯一标识)
|
||||
* @param sheetId 工作表ID(可从表格链接中获取,如 ?tab=BB08J2 中的 BB08J2)
|
||||
* @param range 范围,例如 "A1:Z100"(行列从0开始,遵循左闭右开原则)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/spreadsheet/v3)
|
||||
* @return 表格数据(JSON格式,包含values数组)
|
||||
*/
|
||||
public static JSONObject readSheetData(String accessToken, String fileId, String sheetId, String range, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}/ranges/{range}
|
||||
String apiUrl = String.format("%s/spreadsheets/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
|
||||
public static JSONObject readSheetData(String accessToken, String appId, String openId, String fileId, String sheetId, String range, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/openapi/spreadsheet/v3/files/{fileId}/{sheetId}/{range}
|
||||
String apiUrl = String.format("%s/files/%s/%s/%s", apiBaseUrl, fileId, sheetId, range);
|
||||
log.info("读取表格数据 - fileId: {}, sheetId: {}, range: {}, apiUrl: {}", fileId, sheetId, range, apiUrl);
|
||||
return callApi(accessToken, apiUrl, "GET", null);
|
||||
return callApi(accessToken, appId, openId, apiUrl, "GET", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入表格数据(V3 API)
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param appId 应用ID
|
||||
* @param openId 开放平台用户ID
|
||||
* @param fileId 文件ID(在线表格的唯一标识)
|
||||
* @param sheetId 工作表ID(可从表格链接中获取,如 ?tab=BB08J2 中的 BB08J2)
|
||||
* @param range 范围,例如 "A1"(起始单元格位置)
|
||||
* @param values 要写入的数据,支持两种格式:
|
||||
* 1. 简单二维数组:[["值1", "值2"], ["值3", "值4"]]
|
||||
* 2. V3 API完整格式(包含CellData结构)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/spreadsheet/v3)
|
||||
* @return 写入结果
|
||||
*/
|
||||
public static JSONObject writeSheetData(String accessToken, String fileId, String sheetId, String range, Object values, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}/ranges/{range}
|
||||
String apiUrl = String.format("%s/spreadsheets/%s/sheets/%s/ranges/%s", apiBaseUrl, fileId, sheetId, range);
|
||||
public static JSONObject writeSheetData(String accessToken, String appId, String openId, String fileId, String sheetId, String range, Object values, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/openapi/spreadsheet/v3/files/{fileId}/{sheetId}/{range}
|
||||
String apiUrl = String.format("%s/files/%s/%s/%s", apiBaseUrl, fileId, sheetId, range);
|
||||
|
||||
// 构建V3 API规范的请求体
|
||||
// 根据腾讯文档V3 API文档(https://docs.qq.com/open/document/app/openapi/v3/sheet/model/spreadsheet.html)
|
||||
@@ -301,27 +376,29 @@ public class TencentDocApiUtil {
|
||||
log.info("写入表格数据 - fileId: {}, sheetId: {}, range: {}, apiUrl: {}", fileId, sheetId, range, apiUrl);
|
||||
log.debug("写入表格数据 - 请求体: {}", requestBody.toJSONString());
|
||||
|
||||
return callApi(accessToken, apiUrl, "PUT", requestBody.toJSONString());
|
||||
return callApi(accessToken, appId, openId, apiUrl, "PUT", requestBody.toJSONString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加表格数据(在最后一行追加)- V3 API
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param appId 应用ID
|
||||
* @param openId 开放平台用户ID
|
||||
* @param fileId 文件ID(在线表格的唯一标识)
|
||||
* @param sheetId 工作表ID(可从表格链接中获取,如 ?tab=BB08J2 中的 BB08J2)
|
||||
* @param values 要追加的数据,二维数组格式,例如:[["值1", "值2"], ["值3", "值4"]]
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/spreadsheet/v3)
|
||||
* @return 追加结果
|
||||
*/
|
||||
public static JSONObject appendSheetData(String accessToken, String fileId, String sheetId, Object values, String apiBaseUrl) {
|
||||
public static JSONObject appendSheetData(String accessToken, String appId, String openId, String fileId, String sheetId, Object values, String apiBaseUrl) {
|
||||
try {
|
||||
// 先获取工作表信息,找到最后一行(V3版本路径)
|
||||
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets/{sheetId}
|
||||
String infoUrl = String.format("%s/spreadsheets/%s/sheets/%s", apiBaseUrl, fileId, sheetId);
|
||||
// V3版本API路径格式:/openapi/spreadsheet/v3/files/{fileId}
|
||||
String infoUrl = String.format("%s/files/%s", apiBaseUrl, fileId);
|
||||
log.info("获取工作表信息以追加数据 - apiUrl: {}", infoUrl);
|
||||
|
||||
JSONObject sheetInfo = callApi(accessToken, infoUrl, "GET", null);
|
||||
JSONObject sheetInfo = callApi(accessToken, appId, openId, infoUrl, "GET", null);
|
||||
|
||||
// 获取行数(根据实际API响应调整)
|
||||
// V3 API可能返回的字段名:rowCount, row_count, properties.rowCount等
|
||||
@@ -354,7 +431,7 @@ public class TencentDocApiUtil {
|
||||
|
||||
log.info("追加数据到第 {} 行,range: {}", rowCount + 1, range);
|
||||
|
||||
return writeSheetData(accessToken, fileId, sheetId, range, values, apiBaseUrl);
|
||||
return writeSheetData(accessToken, appId, openId, fileId, sheetId, range, values, apiBaseUrl);
|
||||
} catch (Exception e) {
|
||||
log.error("追加表格数据失败 - fileId: {}, sheetId: {}", fileId, sheetId, e);
|
||||
throw new RuntimeException("追加表格数据失败: " + e.getMessage(), e);
|
||||
@@ -365,44 +442,123 @@ public class TencentDocApiUtil {
|
||||
* 获取文件信息 - V3 API
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param appId 应用ID
|
||||
* @param openId 开放平台用户ID
|
||||
* @param fileId 文件ID(在线表格的唯一标识)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/spreadsheet/v3)
|
||||
* @return 文件信息(JSON格式,包含metadata、sheets等信息)
|
||||
* 返回格式示例:{ "fileId": "xxx", "metadata": {...}, "sheets": [...] }
|
||||
*/
|
||||
public static JSONObject getFileInfo(String accessToken, String fileId, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}
|
||||
String apiUrl = String.format("%s/spreadsheets/%s", apiBaseUrl, fileId);
|
||||
public static JSONObject getFileInfo(String accessToken, String appId, String openId, String fileId, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/openapi/spreadsheet/v3/files/{fileId}
|
||||
String apiUrl = String.format("%s/files/%s", apiBaseUrl, fileId);
|
||||
log.info("获取文件信息 - fileId: {}, apiUrl: {}", fileId, apiUrl);
|
||||
return callApi(accessToken, apiUrl, "GET", null);
|
||||
return callApi(accessToken, appId, openId, apiUrl, "GET", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工作表列表 - V3 API
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param appId 应用ID
|
||||
* @param openId 开放平台用户ID
|
||||
* @param fileId 文件ID(在线表格的唯一标识)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/open/api/v3)
|
||||
* @param apiBaseUrl API基础地址(默认:https://docs.qq.com/openapi/spreadsheet/v3)
|
||||
* @return 工作表列表(JSON格式,包含所有sheet的properties信息)
|
||||
* 返回格式示例:{ "sheets": [{ "properties": { "sheetId": "xxx", "title": "工作表1", ... } }] }
|
||||
*/
|
||||
public static JSONObject getSheetList(String accessToken, String fileId, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/open/api/v3/spreadsheets/{spreadsheetId}/sheets
|
||||
String apiUrl = String.format("%s/spreadsheets/%s/sheets", apiBaseUrl, fileId);
|
||||
public static JSONObject getSheetList(String accessToken, String appId, String openId, String fileId, String apiBaseUrl) {
|
||||
// V3版本API路径格式:/openapi/spreadsheet/v3/files/{fileId}(获取文件信息包含sheets列表)
|
||||
String apiUrl = String.format("%s/files/%s", apiBaseUrl, fileId);
|
||||
log.info("获取工作表列表 - fileId: {}, apiUrl: {}", fileId, apiUrl);
|
||||
return callApi(accessToken, apiUrl, "GET", null);
|
||||
return callApi(accessToken, appId, openId, apiUrl, "GET", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* 获取用户信息(包含Open-Id)
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @return 用户信息
|
||||
* @return 用户信息,包含 openId、nickname 等字段
|
||||
*/
|
||||
public static JSONObject getUserInfo(String accessToken) {
|
||||
// 腾讯文档用户信息接口:https://docs.qq.com/open/document/app/oauth2/userinfo.html
|
||||
// 注意:此接口使用不同的鉴权方式(Authorization: Bearer)
|
||||
String apiUrl = "https://docs.qq.com/oauth/v2/userinfo";
|
||||
return callApi(accessToken, apiUrl, "GET", null);
|
||||
return callApiLegacy(accessToken, apiUrl, "GET", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用腾讯文档API(旧版鉴权方式,用于OAuth接口)
|
||||
* 某些接口(如 userinfo)使用 Authorization: Bearer 方式
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param apiUrl API地址
|
||||
* @param method 请求方法
|
||||
* @param body 请求体
|
||||
* @return API响应
|
||||
*/
|
||||
private static JSONObject callApiLegacy(String accessToken, String apiUrl, String method, String body) {
|
||||
try {
|
||||
URL url = new URL(apiUrl);
|
||||
java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
|
||||
|
||||
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 = statusCode >= 200 && statusCode < 300
|
||||
? new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))
|
||||
: 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();
|
||||
|
||||
return JSON.parseObject(response.toString());
|
||||
} catch (Exception e) {
|
||||
log.error("调用腾讯文档API(旧版鉴权)失败: url={}, method={}", apiUrl, method, e);
|
||||
throw new RuntimeException("调用API失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用腾讯文档API(简化方法,自动获取Open-Id)
|
||||
*
|
||||
* @param accessToken 访问令牌
|
||||
* @param appId 应用ID
|
||||
* @param apiUrl API地址
|
||||
* @param method 请求方法
|
||||
* @param body 请求体
|
||||
* @return API响应
|
||||
*/
|
||||
public static JSONObject callApiSimple(String accessToken, String appId, String apiUrl, String method, String body) {
|
||||
// 获取用户信息以获得 openId
|
||||
JSONObject userInfo = getUserInfo(accessToken);
|
||||
String openId = userInfo.getString("openId");
|
||||
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
throw new RuntimeException("无法获取 Open-Id,请检查 Access Token 是否有效");
|
||||
}
|
||||
|
||||
return callApi(accessToken, appId, openId, apiUrl, method, body);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user