10 KiB
10 KiB
腾讯文档 API 读取失败诊断指南
问题描述
当调用腾讯文档读取接口时,返回错误:
{
"msg": "无法读取表头,请检查headerRow参数",
"code": 500
}
请求参数:
{
"fileId": "DUW50RUprWXh2TGJK",
"sheetId": "BB08J2",
"headerRow": 1,
"orderNoColumn": 3,
"logisticsLinkColumn": 13
}
已添加的调试功能
我已经在代码中添加了详细的日志记录,现在会输出以下信息:
1. Service 层日志
- 开始读取表格数据的参数
- 获取用户信息的响应
- Open ID 提取结果
- API 调用参数
- API 返回结果
2. Controller 层日志
- 读取表头的范围
- 表头数据的完整响应
- values 数组是否为空
诊断步骤
步骤 1:查看应用日志
启用 DEBUG 级别日志:
application-dev.yml:
logging:
level:
com.ruoyi.jarvis.service.impl.TencentDocServiceImpl: DEBUG
com.ruoyi.jarvis.util.TencentDocApiUtil: DEBUG
com.ruoyi.web.controller.jarvis.TencentDocController: DEBUG
重启应用后,再次调用 API,查看日志输出。
步骤 2:分析日志信息
2.1 检查用户信息获取
查找日志:
正在获取用户信息...
用户信息响应: {"ret":0,"msg":"Succeed","data":{...}}
可能的问题:
- ❌ 如果看到
401 Unauthorized:Access Token 无效或过期 - ❌ 如果看到
ret != 0:业务逻辑错误 - ❌ 如果
data为 null:响应格式不正确
解决方案:
- 检查 Access Token 是否有效
- 使用 Refresh Token 刷新 Access Token
- 重新进行 OAuth2 授权
2.2 检查 Open ID 获取
查找日志:
成功获取 Open ID: bcb50c8a4b724d86bbcf6fc64c5e2b22
可能的问题:
- ❌ 如果看到
openID 字段不存在:响应结构解析错误 - ❌ 如果 openID 为空:用户信息不完整
解决方案:
- 检查用户信息响应的完整内容
- 确认响应格式是否为:
{"ret":0,"msg":"Succeed","data":{"openID":"xxx",...}} - 注意字段名是
openID(大写 ID)
2.3 检查 API 调用
查找日志:
读取表格数据 - fileId: DUW50RUprWXh2TGJK, sheetId: BB08J2, range: A1:Z1, apiUrl: https://docs.qq.com/openapi/spreadsheet/v3/files/DUW50RUprWXh2TGJK/BB08J2/A1:Z1
可能的问题:
- ❌ 如果看到
404 Not Found:文件 ID 或工作表 ID 错误 - ❌ 如果看到
403 Forbidden:没有访问权限 - ❌ 如果看到
400 Bad Request:请求参数格式错误
解决方案:
-
验证 File ID:
- 打开腾讯文档,从 URL 中获取正确的 File ID
- URL 格式:
https://docs.qq.com/sheet/DUW50RUprWXh2TGJK?tab=BB08J2 - File ID 是
sheet/后面到?之前的部分
-
验证 Sheet ID:
- Sheet ID 是 URL 中
tab=后面的部分 - 例如:
BB08J2
- Sheet ID 是 URL 中
-
检查文档权限:
- 确认授权用户有权限访问该文档
- 在腾讯文档中检查分享设置
2.4 检查 API 响应
查找日志:
表头数据响应: {"values":[["列1","列2","列3"]]}
或
表头数据中values数组为空,完整响应: {...}
可能的问题:
问题 A:API 返回成功但 values 为空
{
"values": []
}
或
{}
原因:
- 指定的行数据确实为空
- Range 格式不正确
- 权限不足,只能看到空数据
解决方案:
- 手动在腾讯文档中检查第 1 行是否有数据
- 尝试不同的 range:
A1:A1(单个单元格)A1:E1(前 5 列)A1(从 A1 开始的所有数据)
问题 B:API 返回错误
可能的错误响应:
{
"error": "invalid_token",
"error_description": "Invalid access token"
}
原因:
- Access Token 无效或过期
- Open ID 不正确
- Client ID(App ID)不正确
解决方案:
- 刷新 Access Token
- 重新获取 Open ID
- 检查配置文件中的 App ID
常见问题和解决方案
问题 1:Access Token 过期
症状:
getUserInfo 返回 401 Unauthorized
解决方案:
// 使用 Refresh Token 刷新 Access Token
JSONObject newTokens = tencentDocService.refreshAccessToken(refreshToken);
String newAccessToken = newTokens.getString("access_token");
String newRefreshToken = newTokens.getString("refresh_token");
// 保存新的 tokens
// ...
问题 2:文档权限不足
症状:
调用腾讯文档API失败: HTTP 403 Forbidden
解决方案:
- 在腾讯文档中打开该文档
- 点击右上角"分享"按钮
- 确认授权用户的微信/QQ 账号有访问权限
- 如果是企业文档,需要确认企业权限设置
问题 3:File ID 或 Sheet ID 错误
症状:
调用腾讯文档API失败: HTTP 404 Not Found
解决方案:
- 重新从浏览器地址栏复制完整 URL
- 正确提取 File ID 和 Sheet ID:
URL: https://docs.qq.com/sheet/DUW50RUprWXh2TGJK?tab=BB08J2
↑ ↑
File ID Sheet ID
(18个字符) (6个字符)
- File ID 通常以
D开头,长度约 18 个字符 - Sheet ID 通常是 6 个大写字母和数字的组合
问题 4:Range 格式错误
症状:
values 数组为空,但手动检查文档有数据
可能的原因:
- Range 格式不符合腾讯文档 API 规范
- 行号从 0 开始而不是从 1 开始
测试不同的 Range 格式:
# 测试 1:单个单元格
curl "http://localhost:8080/api/test/read?fileId=XXX&sheetId=YYY&range=A1"
# 测试 2:单行范围
curl "http://localhost:8080/api/test/read?fileId=XXX&sheetId=YYY&range=A1:Z1"
# 测试 3:多行范围
curl "http://localhost:8080/api/test/read?fileId=XXX&sheetId=YYY&range=A1:Z10"
# 测试 4:使用行号 0(如果API是从0开始)
curl "http://localhost:8080/api/test/read?fileId=XXX&sheetId=YYY&range=A0:Z0"
问题 5:鉴权头设置错误
症状:
调用腾讯文档API失败: HTTP 401 Unauthorized
检查: 确认代码中使用了正确的鉴权方式:
conn.setRequestProperty("Access-Token", accessToken);
conn.setRequestProperty("Client-Id", clientId);
conn.setRequestProperty("Open-Id", openId);
而不是:
conn.setRequestProperty("Authorization", "Bearer " + accessToken); // ❌ 错误
快速诊断脚本
创建一个测试接口来诊断问题:
@GetMapping("/test/diagnose")
public Map<String, Object> diagnose(@RequestParam String accessToken) {
Map<String, Object> result = new LinkedHashMap<>();
try {
// 1. 测试获取用户信息
System.out.println("\n=== 步骤1:获取用户信息 ===");
JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken);
result.put("1_userInfo", userInfo);
System.out.println("✓ 用户信息: " + userInfo.toJSONString());
// 2. 提取 Open ID
System.out.println("\n=== 步骤2:提取 Open ID ===");
JSONObject data = userInfo.getJSONObject("data");
String openID = data != null ? data.getString("openID") : null;
result.put("2_openID", openID);
System.out.println("✓ Open ID: " + openID);
// 3. 测试读取文档
String testFileId = "DUW50RUprWXh2TGJK";
String testSheetId = "BB08J2";
String testRange = "A1:Z1";
System.out.println("\n=== 步骤3:读取表格数据 ===");
System.out.println("File ID: " + testFileId);
System.out.println("Sheet ID: " + testSheetId);
System.out.println("Range: " + testRange);
JSONObject readResult = tencentDocService.readSheetData(
accessToken, testFileId, testSheetId, testRange
);
result.put("3_readResult", readResult);
System.out.println("✓ 读取结果: " + readResult.toJSONString());
// 4. 检查 values 数组
System.out.println("\n=== 步骤4:检查 values 数组 ===");
JSONArray values = readResult != null ? readResult.getJSONArray("values") : null;
result.put("4_values", values);
result.put("4_valuesCount", values != null ? values.size() : 0);
System.out.println("✓ Values 数组大小: " + (values != null ? values.size() : 0));
if (values != null && !values.isEmpty()) {
System.out.println("✓ 第一行数据: " + values.getJSONArray(0).toJSONString());
}
result.put("status", "success");
result.put("message", "所有测试通过");
} catch (Exception e) {
result.put("status", "error");
result.put("error", e.getMessage());
result.put("stackTrace", Arrays.toString(e.getStackTrace()));
System.err.println("✗ 诊断失败: " + e.getMessage());
e.printStackTrace();
}
return result;
}
使用方法:
curl "http://localhost:8080/test/diagnose?accessToken=YOUR_ACCESS_TOKEN"
检查清单
执行以下检查清单,确保所有配置正确:
配置检查
application-dev.yml中app-id配置正确application-dev.yml中app-secret配置正确application-dev.yml中api-base-url为https://docs.qq.com/openapi/spreadsheet/v3- 日志级别设置为 DEBUG
授权检查
- Access Token 未过期(有效期3天)
- 授权用户有访问该文档的权限
- 文档没有被删除或移动
参数检查
- File ID 正确(从 URL 中复制)
- Sheet ID 正确(从 URL 的 tab 参数中复制)
- headerRow 参数正确(通常为 1)
- Range 格式正确(如
A1:Z1)
代码检查
- 使用查询参数方式调用
/oauth/v2/userinfo?access_token=xxx - 正确解析用户信息:
userInfo.getJSONObject("data").getString("openID") - 使用三个鉴权头:
Access-Token,Client-Id,Open-Id
联系支持
如果以上步骤都无法解决问题,请提供以下信息:
- 完整的日志输出(DEBUG 级别)
- 请求参数:
- File ID
- Sheet ID
- Header Row
- Range
- 腾讯文档 URL(用于验证 ID 是否正确)
- 错误信息(完整的堆栈跟踪)
- 用户信息响应(脱敏后的 JSON)
- API 调用响应(完整的 JSON)
诊断指南版本:1.0
创建时间:2025-11-05
适用场景:腾讯文档 API 读取失败问题排查