427 lines
9.5 KiB
Markdown
427 lines
9.5 KiB
Markdown
# 腾讯文档 API 官方格式修复
|
||
|
||
## 修复日期
|
||
2025-11-05
|
||
|
||
## 问题来源
|
||
根据[腾讯文档官方 API 文档](https://docs.qq.com/open/document/app/openapi/v3/sheet/get/get_range.html),发现之前对 Range 格式的理解有误。
|
||
|
||
---
|
||
|
||
## ✅ 官方规范
|
||
|
||
### 1. Range 格式:A1 表示法
|
||
|
||
根据官方文档,range 参数使用 **A1 表示法**(Excel 格式),**不是**索引格式。
|
||
|
||
**官方示例**:
|
||
```bash
|
||
curl 'https://docs.qq.com/openapi/spreadsheet/v3/files/ABCDE123abcde/BB08J2/A10:D11' \
|
||
--header 'Access-Token: {ACCESS_TOKEN}' \
|
||
--header 'Open-Id: {OPEN_ID}' \
|
||
--header 'Client-Id: {CLIENT_ID}'
|
||
```
|
||
|
||
**正确格式**:
|
||
- ✅ `A10:D11` - Excel 格式(A1 表示法)
|
||
- ✅ `A2:Z2` - 表头行
|
||
- ✅ `A3:Z203` - 数据行
|
||
- ❌ `1,0,1,25` - 索引格式(错误)
|
||
|
||
---
|
||
|
||
### 2. 响应结构:data.gridData
|
||
|
||
根据官方文档,成功响应的结构为:
|
||
|
||
```json
|
||
{
|
||
"ret": 0,
|
||
"msg": "Succeed",
|
||
"data": {
|
||
"gridData": {
|
||
"columnMetadata": [],
|
||
"rowMetadata": [],
|
||
"rows": [
|
||
{
|
||
"values": [
|
||
{
|
||
"cellFormat": null,
|
||
"cellValue": {
|
||
"text": "单元格内容"
|
||
},
|
||
"dataType": "DATA_TYPE_UNSPECIFIED"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"startColumn": 0,
|
||
"startRow": 9
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**关键要点**:
|
||
- ✅ 数据在 `data.gridData` 下(有两层包装)
|
||
- ✅ 成功时 `ret: 0`
|
||
- ✅ 错误时 `code != 0`
|
||
|
||
---
|
||
|
||
### 3. API 限制
|
||
|
||
根据官方文档,查询范围有以下限制:
|
||
|
||
| 限制项 | 最大值 |
|
||
|-------|-------|
|
||
| 查询范围行数 | ≤ 1000 |
|
||
| 查询范围列数 | ≤ 200 |
|
||
| 范围内总单元格数 | ≤ 10000 |
|
||
|
||
**我们的范围**:
|
||
- `A3:Z203`:201行 × 26列 = 5226单元格 ✅ 符合限制
|
||
- `A2:Z2`:1行 × 26列 = 26单元格 ✅ 符合限制
|
||
|
||
---
|
||
|
||
## 🔧 修复内容
|
||
|
||
### 修复 1:Range 格式(回到 A1 表示法)
|
||
|
||
#### TencentDocApiUtil.java
|
||
**修改前**:
|
||
```java
|
||
// range格式:startRow,startColumn,endRow,endColumn(从0开始的索引)
|
||
```
|
||
|
||
**修改后**:
|
||
```java
|
||
/**
|
||
* @param range 范围,使用 A1 表示法(如:"A10:D11", "A1:Z100")
|
||
* 根据官方文档:https://docs.qq.com/open/document/app/openapi/v3/sheet/get/get_range.html
|
||
*/
|
||
```
|
||
|
||
#### TencentDocController.java
|
||
**修改前**(错误):
|
||
```java
|
||
int headerRowIndex = headerRow - 1;
|
||
String headerRange = String.format("%d,0,%d,25", headerRowIndex, headerRowIndex);
|
||
// 结果:"1,0,1,25"
|
||
```
|
||
|
||
**修改后**(正确):
|
||
```java
|
||
String headerRange = String.format("A%d:Z%d", headerRow, headerRow);
|
||
// 结果:"A2:Z2"
|
||
```
|
||
|
||
---
|
||
|
||
### 修复 2:响应结构解析(支持 data.gridData)
|
||
|
||
#### TencentDocDataParser.java
|
||
**新增支持**:
|
||
```java
|
||
// 方式1:检查是否有 data.gridData 字段(官方V3 API格式)
|
||
JSONObject data = apiResponse.getJSONObject("data");
|
||
if (data != null) {
|
||
JSONObject gridData = data.getJSONObject("gridData");
|
||
if (gridData != null) {
|
||
return parseGridData(gridData);
|
||
}
|
||
}
|
||
|
||
// 方式2:检查是否有 gridData 字段(直接格式)
|
||
JSONObject gridData = apiResponse.getJSONObject("gridData");
|
||
if (gridData != null) {
|
||
return parseGridData(gridData);
|
||
}
|
||
|
||
// 方式3:检查是否有 values 字段(简单格式)
|
||
JSONArray values = apiResponse.getJSONArray("values");
|
||
if (values != null) {
|
||
return values;
|
||
}
|
||
```
|
||
|
||
**兼容性**:支持三种响应格式
|
||
1. 官方格式:`{ret, msg, data: {gridData}}`
|
||
2. 简化格式:`{gridData}`
|
||
3. 自定义格式:`{values}`
|
||
|
||
---
|
||
|
||
### 修复 3:错误响应检查
|
||
|
||
#### TencentDocServiceImpl.java
|
||
**新增检查**:
|
||
```java
|
||
// 检查错误码(code字段)
|
||
if (result.containsKey("code")) {
|
||
Integer code = result.getInteger("code");
|
||
if (code != null && code != 0) {
|
||
String message = result.getString("message");
|
||
throw new RuntimeException("腾讯文档API错误: " + message + " (code: " + code + ")");
|
||
}
|
||
}
|
||
|
||
// 检查业务返回码(ret字段)
|
||
if (result.containsKey("ret")) {
|
||
Integer ret = result.getInteger("ret");
|
||
if (ret != null && ret != 0) {
|
||
String msg = result.getString("msg");
|
||
throw new RuntimeException("腾讯文档API业务错误: " + msg + " (ret: " + ret + ")");
|
||
}
|
||
}
|
||
```
|
||
|
||
**两种错误格式**:
|
||
- 错误响应:`{code: 400001, message: "..."}`
|
||
- 业务错误:`{ret: 1, msg: "..."}`(虽然官方成功是ret=0,但可能存在业务错误)
|
||
|
||
---
|
||
|
||
## 📊 修改对比表
|
||
|
||
| 项目 | 修改前 | 修改后 |
|
||
|------|--------|--------|
|
||
| Range格式 | `1,0,1,25` | `A2:Z2` ✅ |
|
||
| 表头range | `1,0,1,25` | `A2:Z2` ✅ |
|
||
| 数据range | `2,0,202,25` | `A3:Z203` ✅ |
|
||
| 响应解析 | 只支持 `gridData` | 支持 `data.gridData` ✅ |
|
||
| 错误检查 | 只检查 `code` | 同时检查 `code` 和 `ret` ✅ |
|
||
|
||
---
|
||
|
||
## 🎯 API 调用示例
|
||
|
||
### 完整的 API 请求
|
||
|
||
**读取表头(第2行)**:
|
||
```
|
||
GET https://docs.qq.com/openapi/spreadsheet/v3/files/DUW50RUprWXh2TGJK/BB08J2/A2:Z2
|
||
Headers:
|
||
Access-Token: {YOUR_ACCESS_TOKEN}
|
||
Client-Id: {YOUR_CLIENT_ID}
|
||
Open-Id: {YOUR_OPEN_ID}
|
||
```
|
||
|
||
**读取数据(第3-203行)**:
|
||
```
|
||
GET https://docs.qq.com/openapi/spreadsheet/v3/files/DUW50RUprWXh2TGJK/BB08J2/A3:Z203
|
||
Headers:
|
||
Access-Token: {YOUR_ACCESS_TOKEN}
|
||
Client-Id: {YOUR_CLIENT_ID}
|
||
Open-Id: {YOUR_OPEN_ID}
|
||
```
|
||
|
||
---
|
||
|
||
### 成功响应示例
|
||
|
||
```json
|
||
{
|
||
"ret": 0,
|
||
"msg": "Succeed",
|
||
"data": {
|
||
"gridData": {
|
||
"startRow": 1,
|
||
"startColumn": 0,
|
||
"rows": [
|
||
{
|
||
"values": [
|
||
{
|
||
"cellValue": {"text": "日期"},
|
||
"dataType": "DATA_TYPE_UNSPECIFIED"
|
||
},
|
||
{
|
||
"cellValue": {"text": "公司"},
|
||
"dataType": "DATA_TYPE_UNSPECIFIED"
|
||
},
|
||
{
|
||
"cellValue": {"text": "草号"},
|
||
"dataType": "DATA_TYPE_UNSPECIFIED"
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 错误响应示例
|
||
|
||
```json
|
||
{
|
||
"code": 400001,
|
||
"message": "Req Parameters Range Validate error",
|
||
"details": {
|
||
"DebugInfo": {
|
||
"traceId": "b92e6e2a1c1e4810bf8cfc70eabf7351"
|
||
}
|
||
},
|
||
"internalCode": 0
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 修改文件清单
|
||
|
||
### 1. TencentDocApiUtil.java
|
||
- ✅ 更新 `readSheetData` 方法注释
|
||
- ✅ 说明 range 使用 A1 表示法
|
||
- ✅ 添加官方文档链接
|
||
|
||
### 2. TencentDocController.java
|
||
- ✅ 将 headerRange 改为 A1 格式
|
||
- ✅ 将 dataRange 改为 A1 格式
|
||
- ✅ 简化日志输出
|
||
|
||
### 3. TencentDocDataParser.java
|
||
- ✅ 支持 `data.gridData` 格式(官方格式)
|
||
- ✅ 保持对 `gridData` 和 `values` 的兼容
|
||
- ✅ 添加详细的调试日志
|
||
|
||
### 4. TencentDocServiceImpl.java
|
||
- ✅ 同时检查 `code` 和 `ret` 错误码
|
||
- ✅ 分别处理 API 错误和业务错误
|
||
- ✅ 添加官方文档链接
|
||
|
||
### 5. 新增文档
|
||
- ✅ `腾讯文档API_官方格式修复.md` - 本文档
|
||
|
||
---
|
||
|
||
## 🚀 测试验证
|
||
|
||
### 请求参数
|
||
|
||
```json
|
||
{
|
||
"accessToken": "YOUR_ACCESS_TOKEN",
|
||
"fileId": "DUW50RUprWXh2TGJK",
|
||
"sheetId": "BB08J2",
|
||
"headerRow": 2,
|
||
"orderNoColumn": 2,
|
||
"logisticsLinkColumn": 12
|
||
}
|
||
```
|
||
|
||
### 预期日志输出
|
||
|
||
```
|
||
读取表头 - 行号: 2, range: A2:Z2
|
||
读取数据行 - 行号: 3 ~ 203, range: A3:Z203
|
||
使用 data.gridData 格式解析
|
||
解析后的数据行数: 98
|
||
数据结构(共 98 行,显示前 3 行):
|
||
第 1 行(15列): ["日期","公司","草号",...,"物流单号","标记"]
|
||
第 2 行(15列): ["3月10日","","JY20251032904",...,"",""]
|
||
第 3 行(15列): ["3月10日","","JY20250309184",...,"6649902864",""]
|
||
成功读取 98 行数据,开始处理...
|
||
```
|
||
|
||
### 预期结果
|
||
|
||
```json
|
||
{
|
||
"msg": "物流链接填充成功",
|
||
"code": 200,
|
||
"data": {
|
||
"startRow": 3,
|
||
"endRow": 203,
|
||
"filledCount": 10,
|
||
"skippedCount": 85,
|
||
"errorCount": 3,
|
||
"message": "成功填充10个物流链接"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## ⚠️ 重要提醒
|
||
|
||
### 1. Range 格式必须是 A1 表示法
|
||
|
||
**正确示例**:
|
||
- ✅ `A1`
|
||
- ✅ `A1:Z1`
|
||
- ✅ `A2:Z2`
|
||
- ✅ `A3:Z203`
|
||
- ✅ `M3` (单个单元格)
|
||
|
||
**错误示例**:
|
||
- ❌ `0,0,0,0` (索引格式)
|
||
- ❌ `1,0,1,25` (索引格式)
|
||
- ❌ `a1:z1` (小写,应该大写)
|
||
|
||
### 2. 响应格式有两种
|
||
|
||
**成功响应**:
|
||
```json
|
||
{
|
||
"ret": 0,
|
||
"msg": "Succeed",
|
||
"data": { ... }
|
||
}
|
||
```
|
||
|
||
**错误响应**:
|
||
```json
|
||
{
|
||
"code": 400001,
|
||
"message": "...",
|
||
"details": { ... }
|
||
}
|
||
```
|
||
|
||
### 3. API 限制
|
||
|
||
- 单次查询行数 ≤ 1000
|
||
- 单次查询列数 ≤ 200
|
||
- 总单元格数 ≤ 10000
|
||
|
||
如果超过限制,需要分批查询。
|
||
|
||
---
|
||
|
||
## 📚 官方文档链接
|
||
|
||
- [获取范围内的表格信息](https://docs.qq.com/open/document/app/openapi/v3/sheet/get/get_range.html) ⭐⭐⭐
|
||
- [A1 表示法说明](https://docs.qq.com/open/document/app/openapi/v3/sheet/model/a1_notation.html)
|
||
- [在线表格资源描述](https://docs.qq.com/open/document/app/openapi/v3/sheet/model/spreadsheet.html)
|
||
- [批量更新接口](https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/update.html)
|
||
|
||
---
|
||
|
||
## ✅ 总结
|
||
|
||
### 关键修复点
|
||
|
||
1. ✅ **Range 格式**:从索引格式改回 A1 表示法(Excel 格式)
|
||
2. ✅ **响应解析**:支持官方的 `data.gridData` 结构
|
||
3. ✅ **错误检查**:同时检查 `code` 和 `ret` 两种错误格式
|
||
4. ✅ **文档引用**:所有修改都基于官方文档
|
||
|
||
### 修改影响
|
||
|
||
- ✅ 完全符合官方 API 规范
|
||
- ✅ 向后兼容(支持多种响应格式)
|
||
- ✅ 更好的错误提示
|
||
- ✅ 详细的日志记录
|
||
|
||
---
|
||
|
||
**文档版本**:1.0
|
||
**创建时间**:2025-11-05
|
||
**依据**:腾讯文档开放平台官方 API 文档
|
||
**状态**:✅ 已修复并验证
|
||
|