1
This commit is contained in:
477
doc/物流链接自动填充-完整逻辑说明.md
Normal file
477
doc/物流链接自动填充-完整逻辑说明.md
Normal file
@@ -0,0 +1,477 @@
|
||||
# 物流链接自动填充 - 完整逻辑说明
|
||||
|
||||
## 📋 功能概述
|
||||
|
||||
自动读取腾讯文档表格中的订单信息,根据**第三方单号**(thirdPartyOrderNo)在数据库中查询对应的物流链接,并自动填充到表格的"物流单号"列。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 完整执行流程
|
||||
|
||||
### 第一步:读取表头(识别列位置)
|
||||
|
||||
```
|
||||
输入参数:
|
||||
- fileId: 文件ID
|
||||
- sheetId: 工作表ID
|
||||
- headerRow: 表头所在行号(默认:2)
|
||||
|
||||
执行逻辑:
|
||||
1. 构建 range:A{headerRow}:Z{headerRow}(例如:A2:Z2)
|
||||
2. 调用 API 读取表头数据
|
||||
3. 解析表头,识别关键列的位置:
|
||||
- "单号" 列(orderNoColumn)→ 这是第三方单号列
|
||||
- "物流单号" 列(logisticsLinkColumn)→ 要写入物流信息的列
|
||||
```
|
||||
|
||||
**示例日志**:
|
||||
```
|
||||
读取表头 - 行号: 2, range: A2:Z2
|
||||
解析后的数据行数: 1
|
||||
第 1 行(26列): ["日期","公司","单号","型号","数量",...,"物流单号","","标记",...]
|
||||
识别到关键列:
|
||||
- "单号" 列位置: 第 3 列(索引2)
|
||||
- "物流单号" 列位置: 第 13 列(索引12)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第二步:读取数据行(批量处理)
|
||||
|
||||
```
|
||||
输入参数:
|
||||
- startRow: 起始行号(表头行 + 1,默认:3)
|
||||
- endRow: 结束行号(startRow + 49,每次读取50行)
|
||||
|
||||
执行逻辑:
|
||||
1. 构建 range:A{startRow}:Z{endRow}(例如:A3:Z52)
|
||||
2. 调用 API 读取数据行
|
||||
3. 如果读取失败(range超出实际范围),返回"没有更多数据"
|
||||
4. 如果成功,继续第三步
|
||||
```
|
||||
|
||||
**示例日志**:
|
||||
```
|
||||
开始读取数据行 - 行号: 3 ~ 52, range: A3:Z52
|
||||
解析后的数据行数: 50
|
||||
```
|
||||
|
||||
**⚠️ 重要限制**:
|
||||
- A1 表示法的 range **不能超出表格实际数据区域**
|
||||
- 如果表格只有 100 行,`A3:Z200` 会报错:`invalid param error: 'range' invalid`
|
||||
- 因此每次只读取 **50 行**,避免超出范围
|
||||
|
||||
---
|
||||
|
||||
### 第三步:逐行处理(匹配+填充)
|
||||
|
||||
```
|
||||
对于每一行数据:
|
||||
|
||||
1. 提取第三方单号
|
||||
- 从 orderNoColumn("单号"列)取值
|
||||
- 例如:row[2] = "JY202506181808"
|
||||
|
||||
2. 数据库查询
|
||||
- 调用:jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo)
|
||||
- 查询条件:third_party_order_no = "JY202506181808"
|
||||
- 返回:JDOrder 对象(包含 logisticsLink 字段)
|
||||
|
||||
3. 检查物流链接
|
||||
- 如果 order == null:跳过,errorCount++
|
||||
- 如果 logisticsLink 为空:跳过,errorCount++
|
||||
- 如果 logisticsLink 不为空:继续第4步
|
||||
|
||||
4. 写入物流链接
|
||||
- 目标单元格:第 {当前行号} 行,第 {logisticsLinkColumn} 列
|
||||
- 例如:第 3 行,第 13 列(M3)
|
||||
- 调用 batchUpdate API 写入物流链接
|
||||
- 成功:filledCount++
|
||||
- 失败:errorCount++
|
||||
```
|
||||
|
||||
**示例日志**:
|
||||
```
|
||||
正在处理订单 - 单号: JY202506181808, 行号: 3, 列: 3
|
||||
查询到订单物流链接: https://m.kuaidi100.com/result.jsp?nu=6649902864
|
||||
准备写入 - 目标单元格: 第 3 行, 第 13 列(索引12)
|
||||
写入成功 - 单号: JY202506181808
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第四步:更新进度(Redis缓存)
|
||||
|
||||
```
|
||||
执行逻辑:
|
||||
1. 记录本次处理的最大行号:lastMaxRow = endRow
|
||||
2. 保存到 Redis:key = "tencent:doc:lastMaxRow:{fileId}:{sheetId}"
|
||||
3. 下次执行时,从 lastMaxRow 开始继续处理
|
||||
```
|
||||
|
||||
**进度管理**:
|
||||
- 首次执行:startRow = 3, endRow = 52
|
||||
- 第二次执行:startRow = 52, endRow = 101
|
||||
- 第三次执行:startRow = 101, endRow = 150
|
||||
- ...以此类推,直到所有数据处理完毕
|
||||
|
||||
---
|
||||
|
||||
## 📊 关键数据结构
|
||||
|
||||
### 1. 表格结构
|
||||
|
||||
| 列号 | 列名 | 数据示例 | 用途 |
|
||||
|------|------|----------|------|
|
||||
| A (0) | 日期 | 3月10日 | - |
|
||||
| B (1) | 公司 | XX公司 | - |
|
||||
| **C (2)** | **单号** | **JY202506181808** | **用于查询数据库** |
|
||||
| D (3) | 型号 | iPhone 14 | - |
|
||||
| E (4) | 数量 | 1 | - |
|
||||
| ... | ... | ... | - |
|
||||
| **M (12)** | **物流单号** | **6649902864** | **要填充的目标列** |
|
||||
|
||||
### 2. 数据库表结构(jd_order)
|
||||
|
||||
| 字段 | 类型 | 说明 | 示例 |
|
||||
|------|------|------|------|
|
||||
| **third_party_order_no** | varchar | **第三方单号(唯一索引)** | **JY202506181808** |
|
||||
| **logistics_link** | varchar | **物流链接** | **https://m.kuaidi100.com/...** |
|
||||
| order_no | varchar | 京东单号 | JD123456789 |
|
||||
| order_status | int | 订单状态 | 1 |
|
||||
| ... | ... | ... | ... |
|
||||
|
||||
### 3. API 请求示例
|
||||
|
||||
**读取表头**:
|
||||
```http
|
||||
GET https://docs.qq.com/openapi/spreadsheet/v3/files/DUW50RUprWXh2TGJK/BB08J2/A2:Z2
|
||||
Headers:
|
||||
Access-Token: {ACCESS_TOKEN}
|
||||
Client-Id: {CLIENT_ID}
|
||||
Open-Id: {OPEN_ID}
|
||||
```
|
||||
|
||||
**读取数据行**:
|
||||
```http
|
||||
GET https://docs.qq.com/openapi/spreadsheet/v3/files/DUW50RUprWXh2TGJK/BB08J2/A3:Z52
|
||||
Headers:
|
||||
Access-Token: {ACCESS_TOKEN}
|
||||
Client-Id: {CLIENT_ID}
|
||||
Open-Id: {OPEN_ID}
|
||||
```
|
||||
|
||||
**写入物流链接**(单个单元格):
|
||||
```http
|
||||
POST https://docs.qq.com/openapi/spreadsheet/v3/files/DUW50RUprWXh2TGJK/batchUpdate
|
||||
Headers:
|
||||
Access-Token: {ACCESS_TOKEN}
|
||||
Client-Id: {CLIENT_ID}
|
||||
Open-Id: {OPEN_ID}
|
||||
Content-Type: application/json
|
||||
|
||||
Body:
|
||||
{
|
||||
"requests": [
|
||||
{
|
||||
"updateCells": {
|
||||
"range": {
|
||||
"sheetId": "BB08J2",
|
||||
"startRowIndex": 2, // 第3行(索引2)
|
||||
"endRowIndex": 3, // 不包含
|
||||
"startColumnIndex": 12, // 第13列(索引12)
|
||||
"endColumnIndex": 13 // 不包含
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"values": [
|
||||
{
|
||||
"cellValue": {
|
||||
"text": "6649902864"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 核心逻辑图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 1. 读取表头(A2:Z2) │
|
||||
│ → 识别"单号"列位置(orderNoColumn) │
|
||||
│ → 识别"物流单号"列位置(logisticsLinkColumn) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 2. 读取数据行(A3:Z52,每次50行) │
|
||||
│ → 解析为二维数组:[[row1], [row2], ...] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 3. 逐行处理 │
|
||||
│ FOR EACH row IN dataRows: │
|
||||
│ a. 提取单号:orderNo = row[orderNoColumn] │
|
||||
│ b. 查询数据库: │
|
||||
│ order = DB.query(third_party_order_no=orderNo) │
|
||||
│ c. 检查物流链接: │
|
||||
│ IF order != null AND logistics_link != null: │
|
||||
│ → 写入表格: │
|
||||
│ cell[rowIndex][logisticsLinkColumn] │
|
||||
│ = order.logistics_link │
|
||||
│ → filledCount++ │
|
||||
│ ELSE: │
|
||||
│ → errorCount++ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 4. 更新进度 │
|
||||
│ → Redis.set("lastMaxRow", endRow) │
|
||||
│ → 返回统计信息:filledCount, errorCount, skippedCount │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 配置参数
|
||||
|
||||
### 接口请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"accessToken": "用户访问令牌(必填)",
|
||||
"fileId": "文件ID(必填)",
|
||||
"sheetId": "工作表ID(必填)",
|
||||
"headerRow": 2, // 表头行号(可选,默认2)
|
||||
"orderNoColumn": 2, // 单号列位置(可选,自动识别)
|
||||
"logisticsLinkColumn": 12 // 物流列位置(可选,自动识别)
|
||||
}
|
||||
```
|
||||
|
||||
### 自动识别列位置
|
||||
|
||||
如果不传 `orderNoColumn` 和 `logisticsLinkColumn`,系统会自动识别:
|
||||
|
||||
```java
|
||||
// 在表头行中查找匹配的列名
|
||||
for (int i = 0; i < headerRow.size(); i++) {
|
||||
String cellValue = headerRow.getString(i);
|
||||
if ("单号".equals(cellValue)) {
|
||||
orderNoColumn = i; // 找到"单号"列
|
||||
}
|
||||
if ("物流单号".equals(cellValue)) {
|
||||
logisticsLinkColumn = i; // 找到"物流单号"列
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 返回结果示例
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "物流链接填充成功",
|
||||
"code": 200,
|
||||
"data": {
|
||||
"startRow": 3,
|
||||
"endRow": 52,
|
||||
"lastMaxRow": 52,
|
||||
"filledCount": 45, // 成功填充45个
|
||||
"skippedCount": 3, // 跳过3个(已有物流信息)
|
||||
"errorCount": 2, // 失败2个(未找到订单)
|
||||
"message": "成功填充45个物流链接,跳过3个,失败2个"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 没有更多数据
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "没有需要处理的数据",
|
||||
"code": 200,
|
||||
"data": {
|
||||
"startRow": 103,
|
||||
"endRow": 152,
|
||||
"lastMaxRow": 100, // 上次处理到第100行
|
||||
"filledCount": 0,
|
||||
"skippedCount": 0,
|
||||
"errorCount": 0,
|
||||
"message": "指定范围内没有数据"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": "读取数据行失败: invalid param error: 'range' invalid",
|
||||
"code": 500
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 关键代码片段
|
||||
|
||||
### 1. 根据第三方单号查询订单
|
||||
|
||||
```java
|
||||
// Controller层
|
||||
String orderNo = row.getString(orderNoColumn); // 从"单号"列取值
|
||||
JDOrder order = jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo);
|
||||
|
||||
// Service层
|
||||
@Override
|
||||
public JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo) {
|
||||
return jdOrderMapper.selectJDOrderByThirdPartyOrderNo(thirdPartyOrderNo);
|
||||
}
|
||||
|
||||
// Mapper.xml
|
||||
<select id="selectJDOrderByThirdPartyOrderNo" parameterType="string" resultMap="JDOrderResult">
|
||||
SELECT * FROM jd_order
|
||||
WHERE third_party_order_no = #{thirdPartyOrderNo}
|
||||
LIMIT 1
|
||||
</select>
|
||||
```
|
||||
|
||||
### 2. 写入物流链接到指定单元格
|
||||
|
||||
```java
|
||||
// 构建更新请求
|
||||
JSONObject updateRequest = new JSONObject();
|
||||
JSONObject updateCells = new JSONObject();
|
||||
|
||||
// 指定目标单元格范围(第excelRow行,第logisticsLinkColumn列)
|
||||
JSONObject range = new JSONObject();
|
||||
range.put("sheetId", sheetId);
|
||||
range.put("startRowIndex", excelRow - 1); // Excel行号转索引
|
||||
range.put("endRowIndex", excelRow); // 不包含
|
||||
range.put("startColumnIndex", logisticsLinkColumn); // 列索引
|
||||
range.put("endColumnIndex", logisticsLinkColumn + 1); // 不包含
|
||||
|
||||
// 设置单元格值
|
||||
JSONArray rows = new JSONArray();
|
||||
JSONObject rowData = new JSONObject();
|
||||
JSONArray values = new JSONArray();
|
||||
JSONObject cellData = new JSONObject();
|
||||
JSONObject cellValue = new JSONObject();
|
||||
cellValue.put("text", logisticsLink); // 物流链接文本
|
||||
cellData.put("cellValue", cellValue);
|
||||
values.add(cellData);
|
||||
rowData.put("values", values);
|
||||
rows.add(rowData);
|
||||
|
||||
updateCells.put("range", range);
|
||||
updateCells.put("rows", rows);
|
||||
updateRequest.put("updateCells", updateCells);
|
||||
|
||||
// 调用 batchUpdate API
|
||||
tencentDocService.batchUpdate(accessToken, fileId, updateRequest);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 1. Range 格式限制
|
||||
|
||||
✅ **正确格式**(A1 表示法):
|
||||
- `A2:Z2` - 表头行
|
||||
- `A3:Z52` - 数据行(50行)
|
||||
- `M3` - 单个单元格
|
||||
|
||||
❌ **错误格式**:
|
||||
- `1,0,1,25` - 索引格式(已废弃)
|
||||
- `A3:Z203` - 超出实际数据范围(会报错)
|
||||
|
||||
### 2. API 限制
|
||||
|
||||
根据官方文档:
|
||||
- 查询范围行数 ≤ 1000
|
||||
- 查询范围列数 ≤ 200
|
||||
- 范围内总单元格数 ≤ 10000
|
||||
|
||||
### 3. 批处理策略
|
||||
|
||||
- **每次读取 50 行**:避免超出表格实际范围
|
||||
- **分批处理**:通过 Redis 记录进度,支持多次执行
|
||||
- **错误重试**:如果某一行写入失败,记录错误但继续处理下一行
|
||||
|
||||
### 4. 数据库字段映射
|
||||
|
||||
⚠️ **关键对应关系**:
|
||||
- 表格中的"单号" → 数据库中的 `third_party_order_no`
|
||||
- **不是** `order_no`(京东单号)
|
||||
- **不是** `remark`(备注)
|
||||
|
||||
### 5. 空值处理
|
||||
|
||||
- 如果单元格为空,`row.getString(index)` 返回空字符串 `""`
|
||||
- 如果行不存在,`values.size()` 会小于预期列数
|
||||
- 需要做好空值判断和边界检查
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试验证
|
||||
|
||||
### 测试请求
|
||||
|
||||
```bash
|
||||
curl -X POST 'http://localhost:30313/jarvis/tencentDoc/fillLogisticsByOrderNo' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"accessToken": "YOUR_ACCESS_TOKEN",
|
||||
"fileId": "DUW50RUprWXh2TGJK",
|
||||
"sheetId": "BB08J2",
|
||||
"headerRow": 2
|
||||
}'
|
||||
```
|
||||
|
||||
### 预期日志
|
||||
|
||||
```
|
||||
读取表头 - 行号: 2, range: A2:Z2
|
||||
解析后的数据行数: 1
|
||||
第 1 行(26列): ["日期","公司","单号",...,"物流单号","","标记",...]
|
||||
|
||||
开始读取数据行 - 行号: 3 ~ 52, range: A3:Z52
|
||||
解析后的数据行数: 50
|
||||
|
||||
正在处理订单 - 单号: JY202506181808, 行号: 3
|
||||
查询到订单物流链接: https://m.kuaidi100.com/result.jsp?nu=6649902864
|
||||
写入成功 - 单号: JY202506181808
|
||||
|
||||
正在处理订单 - 单号: JY202506181809, 行号: 4
|
||||
未找到订单 - 单号: JY202506181809
|
||||
|
||||
...
|
||||
|
||||
填充完成 - 成功: 45, 跳过: 3, 失败: 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [腾讯文档官方 API 文档](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/batchupdate/update.html)
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:1.0
|
||||
**创建时间**:2025-11-05
|
||||
**状态**:✅ 已完成
|
||||
|
||||
Reference in New Issue
Block a user