diff --git a/doc/腾讯文档物流链接自动填充-实现逻辑说明.md b/doc/腾讯文档物流链接自动填充-实现逻辑说明.md
new file mode 100644
index 0000000..244b039
--- /dev/null
+++ b/doc/腾讯文档物流链接自动填充-实现逻辑说明.md
@@ -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列 |
+|------|-----|-----|-----|-----|-----------|------|
+| 日期 | 公司| 草号| 型号| ... | 物流单号 | 标记 |
+```
+
+自动识别结果:
+- 草号列(单号列):索引 2(C列)
+- 物流单号列:索引 12(M列)
+
+---
+
+### 第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"
+```
+
+---
+
+## 🔍 关键字段说明
+
+### 表格中的"单号"(草号)
+- **表头名称**:草号
+- **列索引**:2(C列)
+- **示例值**:`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列(索引2,C列)
+- 物流链接列在第13列(索引12,M列)
+
+---
+
+## 🚨 注意事项
+
+### 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` 字段查询订单
+
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java
index 53c8a8f..57547c9 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java
@@ -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();
diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java
index 71f7a82..c295f35 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/mapper/JDOrderMapper.java
@@ -40,6 +40,11 @@ public interface JDOrderMapper {
* 根据物流链接查询订单
*/
JDOrder selectJDOrderByLogisticsLink(String logisticsLink);
+
+ /**
+ * 根据第三方单号查询订单
+ */
+ JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo);
/**
* 批量删除(根据主键ID)
diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java
index fe493c1..030edc1 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/IJDOrderService.java
@@ -39,6 +39,9 @@ public interface IJDOrderService {
/** 根据物流链接查询订单 */
JDOrder selectJDOrderByLogisticsLink(String logisticsLink);
+
+ /** 根据第三方单号查询订单 */
+ JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo);
/** 批量删除(根据主键ID) */
int deleteJDOrderByIds(Long[] ids);
diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java
index 52ee544..a9640ad 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/JDOrderServiceImpl.java
@@ -58,6 +58,11 @@ public class JDOrderServiceImpl implements IJDOrderService {
public JDOrder selectJDOrderByLogisticsLink(String logisticsLink) {
return jdOrderMapper.selectJDOrderByLogisticsLink(logisticsLink);
}
+
+ @Override
+ public JDOrder selectJDOrderByThirdPartyOrderNo(String thirdPartyOrderNo) {
+ return jdOrderMapper.selectJDOrderByThirdPartyOrderNo(thirdPartyOrderNo);
+ }
@Override
public int deleteJDOrderByIds(Long[] ids) {
diff --git a/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml b/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml
index e2608e0..ff4a1a1 100644
--- a/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/jarvis/JDOrderMapper.xml
@@ -153,6 +153,12 @@
order by order_time desc
limit 1
+
+
delete from jd_order where id in