Files
ruoyi-java/doc/腾讯文档API数据格式解析说明.md
2025-11-06 10:46:01 +08:00

469 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 腾讯文档 API 数据格式解析说明
## 问题发现
在实际调用腾讯文档 V3 API 时,发现返回的数据格式与预期完全不同。
---
## 数据格式对比
### ❌ 我们最初预期的格式(简单格式)
```json
{
"values": [
["单元格1", "单元格2", "单元格3"],
["数据1", "数据2", "数据3"]
]
}
```
### ✅ 实际返回的格式gridData 格式)
```json
{
"gridData": {
"startRow": 0,
"startColumn": 0,
"rows": [
{
"values": [
{
"cellValue": {
"text": "JY202506181808"
},
"cellFormat": {
"textFormat": {
"font": "Microsoft YaHei",
"fontSize": 11,
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"color": {
"red": 0,
"green": 0,
"blue": 0,
"alpha": 255
}
},
"horizontalAlignment": "HORIZONTAL_ALIGNMENT_UNSPECIFIED",
"verticalAlignment": "VERTICAL_ALIGNMENT_UNSPECIFIED"
},
"dataType": "DATA_TYPE_UNSPECIFIED"
},
{
"cellValue": {
"text": ""
},
...
}
]
}
],
"rowMetadata": [],
"columnMetadata": []
},
"version": "0"
}
```
---
## 格式差异分析
### 数据层级
**简单格式**
```
响应
└── values (数组)
├── 行1 (数组)
│ ├── "单元格1"
│ └── "单元格2"
└── 行2 (数组)
├── "数据1"
└── "数据2"
```
**gridData 格式**
```
响应
└── gridData (对象)
├── startRow (数字)
├── startColumn (数字)
├── rows (数组)
│ └── 行对象
│ └── values (数组)
│ └── 单元格对象
│ ├── cellValue (对象)
│ │ └── text (字符串) ← 实际文本内容在这里
│ ├── cellFormat (对象)
│ └── dataType (字符串)
├── rowMetadata (数组)
└── columnMetadata (数组)
```
### 关键区别
| 项目 | 简单格式 | gridData 格式 |
|------|---------|--------------|
| 根字段 | `values` | `gridData` |
| 行数据 | 直接数组 | 在 `gridData.rows` 中 |
| 单元格数据 | 直接字符串 | 在 `cellValue.text` 中 |
| 格式信息 | 无 | 在 `cellFormat` 中 |
| 元数据 | 无 | 在 `rowMetadata``columnMetadata` 中 |
---
## 解决方案
### 1. 创建数据解析器
我们创建了 `TencentDocDataParser` 工具类来统一处理两种格式:
**位置**`ruoyi-system/src/main/java/com/ruoyi/jarvis/util/TencentDocDataParser.java`
**核心功能**
#### (1) 解析为简单数组格式
```java
JSONArray parsedValues = TencentDocDataParser.parseToSimpleArray(apiResponse);
```
**输入**gridData 格式):
```json
{
"gridData": {
"rows": [
{
"values": [
{"cellValue": {"text": "单元格1"}},
{"cellValue": {"text": "单元格2"}}
]
}
]
}
}
```
**输出**(简单格式):
```json
[
["单元格1", "单元格2"]
]
```
#### (2) 获取指定行数据
```java
JSONArray row = TencentDocDataParser.getRow(apiResponse, 0); // 获取第1行
```
#### (3) 获取指定单元格文本
```java
String cellText = TencentDocDataParser.getCellText(apiResponse, 0, 2); // 第1行第3列
```
#### (4) 打印数据结构(调试用)
```java
TencentDocDataParser.printDataStructure(apiResponse, 5); // 打印前5行
```
### 2. 更新 Service 层
`TencentDocServiceImpl.java``readSheetData` 方法中:
```java
// API 调用
JSONObject result = TencentDocApiUtil.readSheetData(...);
// 解析数据为统一的简单格式
JSONArray parsedValues = TencentDocDataParser.parseToSimpleArray(result);
// 返回包含简化格式的响应
JSONObject response = new JSONObject();
response.put("values", parsedValues); // 统一格式
response.put("_原始数据", result); // 保留原始数据供调试
return response;
```
### 3. 向后兼容性
解析器会自动检测数据格式:
- 如果有 `gridData` 字段 → 解析为 gridData 格式
- 如果有 `values` 字段 → 直接返回(简单格式)
- 如果都没有 → 返回空数组
```java
public static JSONArray parseToSimpleArray(JSONObject apiResponse) {
// 方式1检查是否有 gridData 字段V3 API 新格式)
JSONObject gridData = apiResponse.getJSONObject("gridData");
if (gridData != null) {
return parseGridData(gridData);
}
// 方式2检查是否有 values 字段(简单格式)
JSONArray values = apiResponse.getJSONArray("values");
if (values != null) {
return values;
}
// 如果都没有,返回空数组
return new JSONArray();
}
```
---
## 使用示例
### 示例 1读取表头
```java
// 读取第2行索引为1作为表头
String headerRange = "A2:Z2";
JSONObject headerData = tencentDocService.readSheetData(accessToken, fileId, sheetId, headerRange);
// 获取简化后的数据
JSONArray headerValues = headerData.getJSONArray("values");
if (headerValues != null && !headerValues.isEmpty()) {
JSONArray headerRow = headerValues.getJSONArray(0); // 第一行数据
// 遍历表头列
for (int i = 0; i < headerRow.size(); i++) {
String columnName = headerRow.getString(i);
System.out.println("第 " + (i+1) + " 列: " + columnName);
}
}
```
### 示例 2查找特定列
```java
// 读取表头行
JSONObject headerData = tencentDocService.readSheetData(accessToken, fileId, sheetId, "A2:Z2");
JSONArray headerValues = headerData.getJSONArray("values");
JSONArray headerRow = headerValues.getJSONArray(0);
// 查找"物流单号"列的索引
int logisticsColumn = -1;
for (int i = 0; i < headerRow.size(); i++) {
String columnName = headerRow.getString(i);
if ("物流单号".equals(columnName)) {
logisticsColumn = i;
break;
}
}
System.out.println("物流单号列在第 " + (logisticsColumn + 1) + " 列(索引: " + logisticsColumn + "");
```
### 示例 3读取数据行
```java
// 读取数据行第3行到第100行
JSONObject sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, "A3:Z100");
JSONArray dataValues = sheetData.getJSONArray("values");
// 遍历每一行
for (int i = 0; i < dataValues.size(); i++) {
JSONArray row = dataValues.getJSONArray(i);
// 获取订单号假设在第1列索引0
String orderNo = row.getString(0);
// 获取物流单号假设在第13列索引12
String logisticsNo = row.getString(12);
System.out.println("订单号: " + orderNo + ", 物流单号: " + logisticsNo);
}
```
---
## 真实数据示例
根据用户提供的截图,表格结构:
```
第1行合并单元格包含链接这是合并的标题行
第2行表头
A列日期
B列公司
C列草号
D列型号
E列数量
F列姓名
G列电话
H列地址
I列价格
J列备注
K列打聚戳图
L列是否安排
M列物流单号
N列标记
第3行及以后数据行
A列3月10日
B列
C列JY20251032904
...
M列物流单号可能为空
```
### 处理代码示例
```java
// 1. 读取表头第2行
JSONObject headerData = tencentDocService.readSheetData(accessToken, fileId, sheetId, "A2:Z2");
JSONArray headerValues = headerData.getJSONArray("values");
JSONArray headerRow = headerValues.getJSONArray(0);
// 2. 查找关键列的索引
int orderNoColumn = -1; // 订单号列(草号)
int logisticsColumn = -1; // 物流单号列
for (int i = 0; i < headerRow.size(); i++) {
String columnName = headerRow.getString(i);
if (columnName != null) {
if (columnName.contains("草号")) {
orderNoColumn = i;
}
if (columnName.contains("物流单号")) {
logisticsColumn = i;
}
}
}
System.out.println("订单号列索引: " + orderNoColumn); // 预期: 2C列
System.out.println("物流单号列索引: " + logisticsColumn); // 预期: 12M列
// 3. 读取数据行从第3行开始
JSONObject sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, "A3:Z100");
JSONArray dataValues = sheetData.getJSONArray("values");
// 4. 处理每一行数据
for (int i = 0; i < dataValues.size(); i++) {
JSONArray row = dataValues.getJSONArray(i);
// 获取订单号
String orderNo = orderNoColumn >= 0 && orderNoColumn < row.size()
? row.getString(orderNoColumn)
: null;
// 获取物流单号
String logisticsNo = logisticsColumn >= 0 && logisticsColumn < row.size()
? row.getString(logisticsColumn)
: null;
if (orderNo != null && !orderNo.isEmpty()) {
System.out.println("第 " + (i+3) + " 行 - 订单号: " + orderNo + ", 物流单号: " + logisticsNo);
// 如果物流单号为空,可以填充
if (logisticsNo == null || logisticsNo.isEmpty()) {
System.out.println(" → 需要填充物流单号");
}
}
}
```
---
## 写入数据注意事项
### 写入接口的数据格式
根据腾讯文档 API 规范,写入数据时仍然使用简单格式:
```java
// 写入数据(简单格式)
Object[][] values = {
{"数据1", "数据2", "数据3"}
};
tencentDocService.writeSheetData(accessToken, fileId, sheetId, "A10", values);
```
**不需要**转换为 gridData 格式API 会自动处理。
---
## 调试技巧
### 1. 启用详细日志
```yaml
logging:
level:
com.ruoyi.jarvis.util.TencentDocDataParser: DEBUG
com.ruoyi.jarvis.service.impl.TencentDocServiceImpl: DEBUG
```
### 2. 查看数据结构
当调用 `readSheetData`会自动打印前3行数据结构
```
数据结构(共 98 行,显示前 3 行):
第 1 行15列: ["日期","公司","草号",...,"物流单号","标记"]
第 2 行15列: ["3月10日","","JY20251032904",...,"",""]
第 3 行15列: ["3月10日","","JY20250309184",...,"6649902864",""]
```
### 3. 检查原始响应
Service 返回的数据中包含 `_原始数据` 字段,可以查看 API 的原始响应:
```java
JSONObject result = tencentDocService.readSheetData(...);
JSONObject originalData = result.getJSONObject("_原始数据");
System.out.println("原始响应: " + originalData.toJSONString());
```
---
## 常见问题
### Q1为什么会有两种数据格式
**A**:腾讯文档 V3 API 使用 gridData 格式以支持更丰富的格式信息(字体、颜色、对齐方式等)。但对于简单的数据读写,我们只需要文本内容,因此解析器会提取纯文本数据。
### Q2读取的数据是否包含格式信息
**A**gridData 格式包含完整的格式信息(字体、颜色等),但我们的解析器只提取文本内容。如果需要格式信息,可以从 `_原始数据` 字段中获取。
### Q3解析器会影响性能吗
**A**:解析器只是简单的 JSON 遍历和文本提取,性能影响很小。对于大数据量(数千行),建议分批读取。
### Q4是否兼容旧代码
**A**:完全兼容。解析后的数据格式与旧代码期望的格式一致(`{"values": [[]]}`),无需修改现有代码。
---
## 总结
### 关键要点
1.**腾讯文档 V3 API 使用 gridData 格式**
2.**创建了 TencentDocDataParser 统一处理**
3.**Service 层自动解析为简单格式**
4.**完全向后兼容,无需修改上层代码**
5.**保留原始数据供调试使用**
### 文件清单
-`TencentDocDataParser.java` - 数据解析器(新增)
-`TencentDocServiceImpl.java` - Service 层(已更新)
-`腾讯文档API数据格式解析说明.md` - 本文档(新增)
---
**文档版本**1.0
**创建时间**2025-11-05
**适用场景**:腾讯文档 V3 API 数据格式解析