This commit is contained in:
2025-11-06 10:57:22 +08:00
parent 9b9aea8d40
commit 763d9985fa
6 changed files with 467 additions and 4 deletions

View File

@@ -0,0 +1,418 @@
# 腾讯文档物流链接自动填充 - 实现逻辑说明
## 功能概述
自动从数据库中查询订单的物流链接,并填充到腾讯文档对应的单元格中。
---
## 📋 完整实现逻辑
### 第1步读取表头识别列位置
```java
// 读取 headerRow 行默认第2行
String headerRange = "A2:Z2";
JSONObject headerData = tencentDocService.readSheetData(accessToken, fileId, sheetId, headerRange);
```
**目的**
- 自动识别"单号"列的位置(`orderNoColumn`
- 自动识别"物流链接"列的位置(`logisticsLinkColumn`
**表头示例**
```
| A列 | B列 | C列 | D列 | ... | M列 | N列 |
|------|-----|-----|-----|-----|-----------|------|
| 日期 | 公司| 草号| 型号| ... | 物流单号 | 标记 |
```
自动识别结果:
- 草号列(单号列):索引 2C列
- 物流单号列:索引 12M列
---
### 第2步读取数据行
```java
// 计算读取范围
int startRow = headerRow + 1; // 表头下一行开始
int endRow = startRow + 200; // 每次最多读取200行
String range = "A3:Z203"; // 从第3行到第203行
JSONObject sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, range);
```
**数据示例**
```
| A列 | B列 | C列 | ... | M列 | N列 |
|--------|-----|----------------|-----|-----------------|------|
| 3月10日| | JY20251032904 | ... | (空) | 是 |
| 3月10日| | JY20250309184 | ... | 6649902864 | 是 |
| 3月10日| | JY20250309143 | ... | (空) | 是 |
```
---
### 第3步遍历每一行匹配订单并收集需要更新的数据
```java
for (int i = 0; i < values.size(); i++) {
JSONArray row = values.getJSONArray(i);
// 1. 获取单号(草号列)
String orderNo = row.getString(orderNoColumn); // 例如JY20251032904
// 2. 检查是否为空
if (orderNo == null || orderNo.trim().isEmpty()) {
skippedCount++; // 跳过空单号
continue;
}
// 3. 检查物流链接列是否已有值
String existingLogisticsLink = row.getString(logisticsLinkColumn);
if (existingLogisticsLink != null && !existingLogisticsLink.trim().isEmpty()) {
skippedCount++; // 已有物流链接,跳过
continue;
}
// 4. 从数据库查询订单(使用第三方单号查询)
JDOrder order = jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo);
// 5. 检查是否找到订单且有物流链接
if (order != null && order.getLogisticsLink() != null && !order.getLogisticsLink().trim().isEmpty()) {
String logisticsLink = order.getLogisticsLink().trim();
// 6. 记录需要更新的信息
updates.add({
"row": excelRow, // 行号Excel行号如3表示第3行
"column": logisticsLinkColumn, // 列索引如12表示M列
"orderNo": orderNo, // 单号
"logisticsLink": logisticsLink // 物流链接
});
filledCount++;
} else {
errorCount++; // 未找到订单或物流链接为空
}
}
```
**处理结果示例**
```
找到3个需要更新的单元格
- M3: 订单号 JY20251032904 → 物流链接 6649906880
- M5: 订单号 JY20250309143 → 物流链接 6649914494
- M7: 订单号 JY20250307138 → 物流链接 6649909460
跳过2个已有物流链接的行
跳过1个单号为空的行
找不到物流链接的订单0个
```
---
### 第4步批量写入物流链接
```java
for (JSONObject update : updates) {
int row = update.getIntValue("row"); // 例如3
int column = update.getIntValue("column"); // 例如12
String logisticsLink = update.getString("logisticsLink");
// 1. 计算列字母0→A, 1→B, ..., 12→M
String columnLetter = getColumnLetter(column); // 12 → "M"
// 2. 构建单元格地址
String cellRange = columnLetter + row; // "M3"
// 3. 构建写入数据(二维数组格式)
Object[][] writeData = {{logisticsLink}};
// 4. 写入单个单元格
tencentDocService.writeSheetData(accessToken, fileId, sheetId, cellRange, writeData);
// 5. 延迟100ms避免API限流
Thread.sleep(100);
}
```
**写入示例**
```
写入 M3 单元格 = "6649906880"
写入 M5 单元格 = "6649914494"
写入 M7 单元格 = "6649909460"
```
---
## 🔍 关键字段说明
### 表格中的"单号"(草号)
- **表头名称**:草号
- **列索引**2C列
- **示例值**`JY20251032904`
- **用途**:用于在数据库中查询订单
### 数据库中的"第三方单号"
- **字段名**`third_party_order_no`
- **Java 属性**`thirdPartyOrderNo`
- **对应关系**:表格中的"草号" = 数据库中的"第三方单号"
**数据库查询SQL**
```sql
SELECT * FROM jd_order
WHERE third_party_order_no = #{thirdPartyOrderNo}
LIMIT 1
```
**Java 调用**
```java
// 根据第三方单号查询订单
JDOrder order = jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo);
```
---
## 📊 完整流程图
```
┌─────────────────────────────────────────────────────────────────┐
│ 1. 读取表头第2行
│ 识别列位置:单号列、物流链接列 │
└──────────────────────┬──────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 2. 读取数据行第3行 ~ 第203行200行
│ range: A3:Z203 │
└──────────────────────┬──────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 3. 遍历每一行 │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────┐ │
│ │ 3.1 读取单号草号列C列 │ │
│ └──────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 3.2 单号为空? │ │
│ │ 是 → 跳过 │ │
│ │ 否 → 继续 │ │
│ └──────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 3.3 物流链接列已有值? │ │
│ │ 是 → 跳过 │ │
│ │ 否 → 继续 │ │
│ └──────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 3.4 查询数据库 │ │
│ │ WHERE third_party_order_no = '单号' │ │
│ └──────────────┬──────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 3.5 找到订单 且 有物流链接? │ │
│ │ 是 → 记录到更新列表 │ │
│ │ 否 → 记录为错误 │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────┬──────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 4. 批量写入物流链接 │
├─────────────────────────────────────────────────────────────────┤
│ 对于每个需要更新的单元格: │
│ 1. 计算单元格地址(如 M3
│ 2. 调用写入API │
│ 3. 延迟100ms避免限流
└──────────────────────┬──────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 5. 返回处理结果 │
│ - 成功填充数量 │
│ - 跳过数量 │
│ - 错误数量 │
└─────────────────────────────────────────────────────────────────┘
```
---
## 🔧 API 调用参数
### 请求参数
```json
{
"accessToken": "腾讯文档访问令牌",
"fileId": "DUW50RUprWXh2TGJK",
"sheetId": "BB08J2",
"headerRow": 2,
"orderNoColumn": 2,
"logisticsLinkColumn": 12
}
```
**参数说明**
- `accessToken`:必填,腾讯文档访问令牌
- `fileId`必填文件ID从文档URL中获取
- `sheetId`必填工作表ID从URL的tab参数中获取
- `headerRow`可选表头所在行号默认1但根据您的表格应该是2
- `orderNoColumn`:可选,单号列索引(如果不提供则自动识别)
- `logisticsLinkColumn`:可选,物流链接列索引(如果不提供则自动识别)
### 响应结果
```json
{
"msg": "物流链接填充成功",
"code": 200,
"data": {
"startRow": 3,
"endRow": 203,
"lastMaxRow": null,
"filledCount": 3,
"skippedCount": 2,
"errorCount": 0,
"message": "成功填充3个物流链接"
}
}
```
**结果说明**
- `startRow`:本次处理的起始行
- `endRow`:本次处理的结束行
- `filledCount`:成功填充的数量
- `skippedCount`:跳过的数量(已有值或单号为空)
- `errorCount`:错误数量(未找到订单或物流链接)
---
## 🎯 修改说明
### 修改前:使用 `remark` 字段查询
```java
// 错误:使用内部单号查询
JDOrder order = jdOrderService.selectJDOrderByRemark(orderNo);
```
**问题**
- `remark` 字段是"内部单号"
- 表格中的"草号"对应的是"第三方单号"
- 字段不匹配,查不到订单
### 修改后:使用 `thirdPartyOrderNo` 字段查询
```java
// 正确:使用第三方单号查询
JDOrder order = jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo);
```
**修复内容**
1. ✅ 在 `IJDOrderService` 接口中添加方法
2. ✅ 在 `JDOrderServiceImpl` 实现类中实现方法
3. ✅ 在 `JDOrderMapper` 接口中添加方法声明
4. ✅ 在 `JDOrderMapper.xml` 中添加SQL查询
5. ✅ 在 `TencentDocController` 中更新调用
**修改的文件**
- `ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java`
- `ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java`
- `ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java`
- `ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml`
- `ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java`
---
## 📝 使用示例
### 示例1自动识别列位置
```bash
POST /api/tencent-doc/fill-logistics
{
"accessToken": "YOUR_ACCESS_TOKEN",
"fileId": "DUW50RUprWXh2TGJK",
"sheetId": "BB08J2",
"headerRow": 2
}
```
系统会自动识别:
- 包含"单号"的列作为订单号列
- 包含"物流"的列作为物流链接列
### 示例2手动指定列位置
```bash
POST /api/tencent-doc/fill-logistics
{
"accessToken": "YOUR_ACCESS_TOKEN",
"fileId": "DUW50RUprWXh2TGJK",
"sheetId": "BB08J2",
"headerRow": 2,
"orderNoColumn": 2,
"logisticsLinkColumn": 12
}
```
明确指定:
- 单号列在第3列索引2C列
- 物流链接列在第13列索引12M列
---
## 🚨 注意事项
### 1. 行列索引从0开始
- 列索引A列=0, B列=1, C列=2, ...
- 但行号是Excel行号从1开始
### 2. headerRow 参数
- 您的表格第1行是合并的标题
- 第2行才是真正的表头
- **必须设置 `headerRow: 2`**
### 3. 批量处理限制
- 每次最多处理200行
- 每次写入间隔100ms避免API限流
- 处理200行大约需要20-30秒
### 4. 数据库字段对应
| 表格列名 | 数据库字段名 | Java属性名 |
|---------|------------|-----------|
| 草号 | `third_party_order_no` | `thirdPartyOrderNo` |
| 物流单号 | `logistics_link` | `logisticsLink` |
---
## ✅ 功能特点
1.**自动识别列位置**:无需手动指定列索引
2.**智能跳过**:已有物流链接的行自动跳过
3.**批量处理**:一次处理多行数据
4.**增量处理**:记录上次处理位置,避免重复
5.**详细日志**:每一步都有日志记录
6.**错误处理**:完善的异常捕获和错误统计
---
**文档版本**1.0
**创建时间**2025-11-05
**修改内容**:使用 `thirdPartyOrderNo` 字段查询订单

View File

@@ -566,11 +566,32 @@ public class TencentDocController extends BaseController {
}
// 读取数据行
JSONObject sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, range);
log.info("开始读取数据行 - 范围: {}", range);
JSONObject sheetData = null;
try {
sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, range);
log.info("数据行读取成功,响应: {}", sheetData != null ? "有数据" : "null");
} catch (Exception e) {
log.error("读取数据行失败", e);
return AjaxResult.error("读取数据行失败: " + e.getMessage());
}
if (sheetData == null) {
return AjaxResult.error("读取数据行返回null");
}
JSONArray values = sheetData.getJSONArray("values");
log.info("解析后的数据行数: {}", values != null ? values.size() : "null");
if (values == null || values.isEmpty()) {
log.info("指定范围内没有数据,可能已处理完毕");
log.warn("指定范围内没有数据,可能已处理完毕。range={}, sheetData keys={}",
range, sheetData.keySet());
// 打印前10个键值对用于调试
if (sheetData != null && !sheetData.isEmpty()) {
log.warn("sheetData内容预览: {}", sheetData.toJSONString().substring(0, Math.min(500, sheetData.toJSONString().length())));
}
JSONObject result = new JSONObject();
result.put("startRow", startRow);
result.put("endRow", endRow);
@@ -579,9 +600,14 @@ public class TencentDocController extends BaseController {
result.put("skippedCount", 0);
result.put("errorCount", 0);
result.put("message", "指定范围内没有数据");
result.put("range", range);
result.put("hasSheetData", sheetData != null);
result.put("sheetDataKeys", sheetData != null ? sheetData.keySet() : null);
return AjaxResult.success("没有需要处理的数据", result);
}
log.info("成功读取 {} 行数据,开始处理...", values.size());
// 处理数据行
int filledCount = 0;
int skippedCount = 0;
@@ -617,8 +643,8 @@ public class TencentDocController extends BaseController {
}
try {
// 根据单号查询订单
JDOrder order = jdOrderService.selectJDOrderByRemark(orderNo);
// 根据第三方单号查询订单
JDOrder order = jdOrderService.selectJDOrderByThirdPartyOrderNo(orderNo);
if (order != null && order.getLogisticsLink() != null && !order.getLogisticsLink().trim().isEmpty()) {
String logisticsLink = order.getLogisticsLink().trim();

View File

@@ -41,6 +41,11 @@ public interface JDOrderMapper {
*/
JDOrder selectJDOrderByLogisticsLink(String logisticsLink);
/**
* 根据第三方单号查询订单
*/
JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo);
/**
* 批量删除根据主键ID
*/

View File

@@ -40,6 +40,9 @@ public interface IJDOrderService {
/** 根据物流链接查询订单 */
JDOrder selectJDOrderByLogisticsLink(String logisticsLink);
/** 根据第三方单号查询订单 */
JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo);
/** 批量删除根据主键ID */
int deleteJDOrderByIds(Long[] ids);

View File

@@ -59,6 +59,11 @@ public class JDOrderServiceImpl implements IJDOrderService {
return jdOrderMapper.selectJDOrderByLogisticsLink(logisticsLink);
}
@Override
public JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo) {
return jdOrderMapper.selectJDOrderByThirdPartyOrderNo(thirdPartyOrderNo);
}
@Override
public int deleteJDOrderByIds(Long[] ids) {
if (ids == null || ids.length == 0) {

View File

@@ -154,6 +154,12 @@
limit 1
</select>
<select id="selectJDOrderByThirdPartyOrderNo" parameterType="string" resultMap="JDOrderResult">
<include refid="selectJDOrderBase"/>
where third_party_order_no = #{thirdPartyOrderNo}
limit 1
</select>
<delete id="deleteJDOrderByIds" parameterType="long">
delete from jd_order where id in
<foreach collection="array" item="id" open="(" separator="," close=")">