# 腾讯文档API完整修复总结 ## 修复日期 2025-11-05 ## 修复概述 针对腾讯文档开放平台 V3 API 集成,完成了以下全面修复: 1. 修正了 API 基础路径配置 2. 修正了 API 端点路径结构 3. 修正了鉴权方式(从 Authorization: Bearer 改为三个独立请求头) 4. 更新了所有 Service 层调用以支持新的鉴权方式 ## 修复详情 ### 1. API 基础路径修复 #### 修改文件 - `ruoyi-system/src/main/java/com/ruoyi/jarvis/config/TencentDocConfig.java` - `ruoyi-admin/src/main/resources/application-dev.yml` - `ruoyi-admin/src/main/resources/application-prod.yml` #### 修改内容 ```java // 修改前 private String apiBaseUrl = "https://docs.qq.com/open/v1"; // 修改后 private String apiBaseUrl = "https://docs.qq.com/openapi/spreadsheet/v3"; ``` #### 配置文件修改 ```yaml # application-dev.yml 和 application-prod.yml # 修改前 api-base-url: https://docs.qq.com/open/v1 # 修改后 api-base-url: https://docs.qq.com/openapi/spreadsheet/v3 ``` ### 2. API 端点路径结构修复 #### 修改文件 - `ruoyi-system/src/main/java/com/ruoyi/jarvis/util/TencentDocApiUtil.java` #### 修改的 API 端点 ##### 2.1 读取表格数据 (readSheetData) ```java // 修改前 String apiUrl = String.format("%s/spreadsheets/%s/%s/%s", apiBaseUrl, fileId, sheetId, range); // 修改后 String apiUrl = String.format("%s/files/%s/%s/%s", apiBaseUrl, fileId, sheetId, range); // 完整路径示例: // https://docs.qq.com/openapi/spreadsheet/v3/files/{fileId}/{sheetId}/{range} ``` ##### 2.2 写入表格数据 (writeSheetData) ```java // 修改前 String apiUrl = String.format("%s/spreadsheets/%s/batchUpdate", apiBaseUrl, fileId); // 修改后 String apiUrl = String.format("%s/files/%s/batchUpdate", apiBaseUrl, fileId); // 完整路径示例: // https://docs.qq.com/openapi/spreadsheet/v3/files/{fileId}/batchUpdate ``` ##### 2.3 追加表格数据 (appendSheetData) ```java // 修改前 String infoUrl = String.format("%s/spreadsheets/%s", apiBaseUrl, fileId); // 修改后 String infoUrl = String.format("%s/files/%s", apiBaseUrl, fileId); // 完整路径示例: // https://docs.qq.com/openapi/spreadsheet/v3/files/{fileId} ``` ##### 2.4 获取文件信息 (getFileInfo) ```java // 修改前 String apiUrl = String.format("%s/spreadsheets/%s", apiBaseUrl, fileId); // 修改后 String apiUrl = String.format("%s/files/%s", apiBaseUrl, fileId); // 完整路径示例: // https://docs.qq.com/openapi/spreadsheet/v3/files/{fileId} ``` ##### 2.5 获取工作表列表 (getSheetList) ```java // 修改前 String apiUrl = String.format("%s/spreadsheets/%s", apiBaseUrl, fileId); // 修改后 String apiUrl = String.format("%s/files/%s", apiBaseUrl, fileId); // 完整路径示例: // https://docs.qq.com/openapi/spreadsheet/v3/files/{fileId} ``` ### 3. 鉴权方式修复 #### 3.1 callApi 方法签名修改 ```java // 修改前 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) ``` #### 3.2 请求头修改 ```java // 修改前(错误的鉴权方式) conn.setRequestProperty("Authorization", "Bearer " + accessToken); // 修改后(正确的鉴权方式) conn.setRequestProperty("Access-Token", accessToken); conn.setRequestProperty("Client-Id", clientId); conn.setRequestProperty("Open-Id", openId); ``` #### 3.3 新增辅助方法 ##### getUserInfo 方法 用于获取用户信息(包含 Open-Id),使用传统的 Authorization: Bearer 鉴权方式。 ```java /** * 获取用户信息(包含Open-Id) * * @param accessToken 访问令牌 * @return 用户信息(包含 openId 字段) */ 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 callApiLegacy(accessToken, apiUrl, "GET", null); } ``` ##### callApiLegacy 方法 用于支持旧版 OAuth2 用户信息接口的 Authorization: Bearer 鉴权方式。 ```java /** * 调用腾讯文档API(使用传统的 Authorization: Bearer 鉴权方式) * 仅用于 OAuth2 用户信息接口 */ private static JSONObject callApiLegacy(String accessToken, String apiUrl, String method, String body) { try { // ... 连接设置 ... conn.setRequestMethod(method); conn.setRequestProperty("Authorization", "Bearer " + accessToken); conn.setRequestProperty("Content-Type", "application/json"); // ... 处理请求和响应 ... } catch (Exception e) { // ... 错误处理 ... } } ``` ### 4. Service 层更新 #### 修改文件 - `ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocServiceImpl.java` #### 修改的方法 所有与腾讯文档 API 交互的方法都进行了更新,在调用 API 前先获取 Open-Id: ##### 4.1 uploadLogisticsToSheet 方法 ```java @Override public JSONObject uploadLogisticsToSheet(String accessToken, String fileId, String sheetId, List orders) { try { // ... 参数验证 ... // 获取用户信息(包含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.appendSheetData( accessToken, tencentDocConfig.getAppId(), openId, fileId, sheetId, values, tencentDocConfig.getApiBaseUrl() ); } catch (Exception e) { // ... 错误处理 ... } } ``` ##### 4.2 appendLogisticsToSheet 方法 类似的修改模式:先获取 openId,然后传递给 API 调用。 ##### 4.3 readSheetData 方法 ```java @Override public JSONObject readSheetData(String accessToken, String fileId, String sheetId, String range) { try { // 获取用户信息(包含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) { // ... 错误处理 ... } } ``` ##### 4.4 writeSheetData 方法 同样的模式。 ##### 4.5 getFileInfo 方法 同样的模式。 ##### 4.6 getSheetList 方法 同样的模式。 ## 完整的修复清单 ### 配置文件(3个) 1. ✅ `TencentDocConfig.java` - 修正 API 基础路径 2. ✅ `application-dev.yml` - 修正 API 基础路径 3. ✅ `application-prod.yml` - 修正 API 基础路径 ### Util 工具类(1个) 4. ✅ `TencentDocApiUtil.java` - 修正 `callApi` 方法签名(添加 clientId, openId 参数) - 修正请求头设置(改用 Access-Token, Client-Id, Open-Id) - 新增 `getUserInfo` 方法(获取用户信息和 Open-Id) - 新增 `callApiLegacy` 方法(支持旧版 OAuth2 接口) - 修正 `readSheetData` 方法(更新 API 路径和参数) - 修正 `writeSheetData` 方法(更新 API 路径和参数) - 修正 `appendSheetData` 方法(更新 API 路径和参数) - 修正 `getFileInfo` 方法(更新 API 路径和参数) - 修正 `getSheetList` 方法(更新 API 路径和参数) ### Service 服务类(1个) 5. ✅ `TencentDocServiceImpl.java` - 修正 `uploadLogisticsToSheet` 方法(添加 Open-Id 获取逻辑) - 修正 `appendLogisticsToSheet` 方法(添加 Open-Id 获取逻辑) - 修正 `readSheetData` 方法(添加 Open-Id 获取逻辑) - 修正 `writeSheetData` 方法(添加 Open-Id 获取逻辑) - 修正 `getFileInfo` 方法(添加 Open-Id 获取逻辑) - 修正 `getSheetList` 方法(添加 Open-Id 获取逻辑) ## 官方文档参考 ### API 路径规范 ``` 基础URL:https://docs.qq.com/openapi/spreadsheet/v3 API 端点: - 批量更新:POST /files/{fileId}/batchUpdate - 获取文件信息:GET /files/{fileId} - 读取表格数据:GET /files/{fileId}/{sheetId}/{range} ``` ### 鉴权方式规范 根据官方文档(https://docs.qq.com/open/document/app/openapi/v3/sheet/batchUpdate.html), 所有 V3 API 请求必须包含以下三个请求头: ```http Access-Token: ACCESS_TOKEN Client-Id: CLIENT_ID Open-Id: OPEN_ID ``` ### Open-Id 获取 通过 OAuth2 用户信息接口获取: ``` GET https://docs.qq.com/oauth/v2/userinfo Authorization: Bearer ACCESS_TOKEN ``` 响应示例: ```json { "openId": "用户的开放平台ID", "unionId": "用户的联合ID", "nickname": "用户昵称", ... } ``` ## 测试建议 ### 1. 配置验证 ```bash # 检查配置文件中的 API 基础地址是否正确 grep "api-base-url" ruoyi-admin/src/main/resources/application-*.yml ``` ### 2. 编译验证 ```bash cd ruoyi-java mvn clean compile ``` ### 3. 功能测试步骤 1. 启动应用 2. 进行 OAuth2 授权,获取 Access Token 3. 调用 `getUserInfo` API,验证是否能正确获取 Open-Id 4. 调用 `getFileInfo` API,验证是否能正确访问文档 5. 调用 `readSheetData` API,验证是否能正确读取数据 6. 调用 `writeSheetData` API,验证是否能正确写入数据 7. 调用 `appendSheetData` API,验证是否能正确追加数据 ### 4. 错误排查 如果仍然出现 404 错误: - 检查 fileId 是否正确 - 检查 sheetId 是否正确 - 检查 Access Token 是否有效 - 检查 Open-Id 是否成功获取 - 检查网络连接和代理设置 如果出现 401 错误: - 检查 Access Token 是否过期 - 检查 Client-Id (AppId) 是否正确 - 检查 Open-Id 是否正确 - 检查用户是否有权限访问该文档 ## 注意事项 1. **代理设置**:代码中已添加 `Proxy.NO_PROXY` 设置,确保直接连接腾讯文档 API,避免代理干扰。 2. **Open-Id 获取**:每次调用 V3 API 前都会先调用 getUserInfo 获取 Open-Id。如果频繁调用可能影响性能,建议后续优化为缓存机制。 3. **错误处理**:所有 API 调用都包含完善的错误处理和日志记录,便于问题排查。 4. **API 版本**:确保使用 V3 版本的 API,V1 和 V2 版本可能已经废弃或行为不同。 5. **鉴权方式差异**: - V3 Spreadsheet API:使用 `Access-Token`, `Client-Id`, `Open-Id` 三个请求头 - OAuth2 用户信息 API:使用 `Authorization: Bearer {token}` 请求头 ## 总结 本次修复完全基于腾讯文档开放平台官方 V3 API 文档,修正了以下核心问题: 1. ✅ API 基础路径从 `/open/v1` 修正为 `/openapi/spreadsheet/v3` 2. ✅ API 端点路径从 `/spreadsheets/` 修正为 `/files/` 3. ✅ 鉴权方式从 `Authorization: Bearer` 修正为 `Access-Token`, `Client-Id`, `Open-Id` 三个独立请求头 4. ✅ Service 层所有调用都已更新以支持新的鉴权方式 5. ✅ 新增 `getUserInfo` 方法自动获取 Open-Id 所有修改已通过代码编译检查,无 lint 错误。接下来需要进行实际的集成测试以验证 API 调用是否正常。