Files
ruoyi-java/doc/自动识别列位置-优化说明.md
2025-11-06 12:40:16 +08:00

416 lines
11 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.

# 自动识别列位置 - 优化说明
## 🎯 优化目标
**修改前**:列位置可以由前端传递,也可以自动识别
**修改后**:所有列位置都由后端自动从表头识别,前端不再需要传递
---
## ✅ 优化原因
### 1. 降低前端复杂度
**修改前**,前端需要知道列的位置:
```json
{
"accessToken": "...",
"fileId": "...",
"sheetId": "...",
"headerRow": 2,
"orderNoColumn": 2, // ❌ 前端需要传递
"logisticsLinkColumn": 12 // ❌ 前端需要传递
}
```
**修改后**,前端只需要提供基本信息:
```json
{
"accessToken": "...",
"fileId": "...",
"sheetId": "...",
"headerRow": 2 // ✅ 只需要表头行号可选默认为1
}
```
---
### 2. 增强灵活性
**表格结构变化时**,不需要修改前端代码:
| 场景 | 修改前 | 修改后 |
|------|--------|--------|
| 列的顺序改变 | ❌ 需要更新前端参数 | ✅ 自动识别,无需改动 |
| 添加新列 | ❌ 需要重新计算索引 | ✅ 自动识别,无需改动 |
| 列名称不变 | ✅ 无需改动 | ✅ 无需改动 |
---
### 3. 减少出错概率
**常见错误**
- ❌ 前端传递的列索引不正确(数错了列)
- ❌ 前端传递的索引是从1开始但后端期望从0开始
- ❌ 表格结构变化后,前端忘记更新参数
**修改后**
- ✅ 后端自动识别,避免手动数列
- ✅ 统一使用从0开始的索引前端无需关心
- ✅ 表格结构变化后,只要列名不变,自动适配
---
## 🔧 代码修改
### 修改 1删除前端参数接收
**修改前**
```java
// 可选参数:指定列位置
Integer orderNoColumn = params.get("orderNoColumn") != null ?
Integer.valueOf(params.get("orderNoColumn").toString()) : null;
Integer logisticsLinkColumn = params.get("logisticsLinkColumn") != null ?
Integer.valueOf(params.get("logisticsLinkColumn").toString()) : null;
Integer headerRow = params.get("headerRow") != null ?
Integer.valueOf(params.get("headerRow").toString()) : 1;
```
**修改后**
```java
// 可选参数:表头行号
Integer headerRow = params.get("headerRow") != null ?
Integer.valueOf(params.get("headerRow").toString()) : 1;
```
---
### 修改 2始终自动识别列位置
**修改前**(条件识别):
```java
// 自动识别列位置(如果未指定)
if (orderNoColumn == null || logisticsLinkColumn == null) {
// 查找所有相关列
...
}
```
**修改后**(始终识别):
```java
// 自动识别列位置(从表头中识别)
Integer orderNoColumn = null; // "单号"列
Integer logisticsLinkColumn = null; // "物流单号"列
Integer arrangedColumn = null; // "是否安排"列
Integer markColumn = null; // "标记"列
// 查找所有相关列
for (int i = 0; i < headerRowData.size(); i++) {
String cellValue = headerRowData.getString(i);
if (cellValue != null) {
String cellValueTrim = cellValue.trim();
// 识别"单号"列
if (orderNoColumn == null && cellValueTrim.contains("单号")) {
orderNoColumn = i;
log.info("✓ 识别到 '单号' 列:第 {} 列(索引{}", i + 1, i);
}
// 识别"物流单号"或"物流链接"列
if (logisticsLinkColumn == null && (cellValueTrim.contains("物流单号") || cellValueTrim.contains("物流链接"))) {
logisticsLinkColumn = i;
log.info("✓ 识别到 '物流单号' 列:第 {} 列(索引{}", i + 1, i);
}
// 识别"是否安排"列(可选)
if (arrangedColumn == null && cellValueTrim.contains("是否安排")) {
arrangedColumn = i;
log.info("✓ 识别到 '是否安排' 列:第 {} 列(索引{}", i + 1, i);
}
// 识别"标记"列(可选)
if (markColumn == null && cellValueTrim.contains("标记")) {
markColumn = i;
log.info("✓ 识别到 '标记' 列:第 {} 列(索引{}", i + 1, i);
}
}
}
```
---
### 修改 3增强错误提示
**修改后的错误提示更加友好**
```java
// 检查必需的列是否都已识别
if (orderNoColumn == null) {
return AjaxResult.error("无法找到'单号'列,请检查表头是否包含'单号'字段");
}
if (logisticsLinkColumn == null) {
return AjaxResult.error("无法找到'物流单号'或'物流链接'列,请检查表头");
}
// 提示可选列的识别情况
if (arrangedColumn == null) {
log.warn("未找到'是否安排'列,将跳过该字段的更新");
}
if (markColumn == null) {
log.warn("未找到'标记'列,将跳过该字段的更新");
}
log.info("列位置识别完成 - 单号: {}, 物流单号: {}, 是否安排: {}, 标记: {}",
orderNoColumn, logisticsLinkColumn, arrangedColumn, markColumn);
```
---
## 📊 列识别规则
### 必需列
| 列名关键字 | 识别条件 | 必需 | 说明 |
|-----------|---------|------|------|
| "单号" | `cellValue.contains("单号")` | ✅ 是 | 用于匹配订单 |
| "物流单号" 或 "物流链接" | `cellValue.contains("物流单号")` <br>`cellValue.contains("物流链接")` | ✅ 是 | 写入物流链接 |
### 可选列
| 列名关键字 | 识别条件 | 必需 | 说明 |
|-----------|---------|------|------|
| "是否安排" | `cellValue.contains("是否安排")` | ❌ 否 | 写入 "2" |
| "标记" | `cellValue.contains("标记")` | ❌ 否 | 写入日期(`yyMMdd` |
**识别规则**
- ✅ 只要列名**包含**关键字即可(不需要完全匹配)
- ✅ 自动去除前后空格
- ✅ 区分大小写
- ✅ 从左到右查找,找到第一个匹配的列
---
## 🔍 日志输出示例
### 成功识别
```
✓ 识别到 '单号' 列:第 3 列索引2
✓ 识别到 '物流单号' 列:第 13 列索引12
✓ 识别到 '是否安排' 列:第 12 列索引11
✓ 识别到 '标记' 列:第 15 列索引14
列位置识别完成 - 单号: 2, 物流单号: 12, 是否安排: 11, 标记: 14
```
### 部分列缺失(可选列)
```
✓ 识别到 '单号' 列:第 3 列索引2
✓ 识别到 '物流单号' 列:第 13 列索引12
WARN 未找到'是否安排'列,将跳过该字段的更新
WARN 未找到'标记'列,将跳过该字段的更新
列位置识别完成 - 单号: 2, 物流单号: 12, 是否安排: null, 标记: null
```
### 必需列缺失(错误)
```
ERROR 无法找到'单号'列,请检查表头是否包含'单号'字段
```
```
ERROR 无法找到'物流单号'或'物流链接'列,请检查表头
```
---
## 🧪 测试场景
### 场景 1标准表格
**表头**
| 日期 | 公司 | 单号 | 型号 | ... | 物流单号 | 是否安排 | 标记 |
**结果**
- ✅ 所有列都识别成功
- ✅ 同时更新4个字段
---
### 场景 2列名有变化
**表头**
| 日期 | 公司 | 订单单号 | 型号 | ... | 物流链接 | 安排状态 | 备注标记 |
**结果**
- ✅ "订单单号" → 识别为"单号"列(包含"单号"
- ✅ "物流链接" → 识别为"物流单号"列(包含"物流链接"
- ❌ "安排状态" → 无法识别(不包含"是否安排"
- ✅ "备注标记" → 识别为"标记"列(包含"标记"
---
### 场景 3列顺序改变
**原表头**
| 单号 | 公司 | 日期 | ... | 物流单号 | 是否安排 | 标记 |
**新表头**(顺序改变):
| 日期 | 单号 | 公司 | ... | 是否安排 | 标记 | 物流单号 |
**结果**
- ✅ 仍然能正确识别所有列
- ✅ 前端代码无需任何修改
---
### 场景 4最小必需列
**表头**
| 日期 | 公司 | 单号 | 型号 | ... | 物流单号 |
**结果**
- ✅ 必需列识别成功
- ⚠️ "是否安排"列不存在,跳过更新
- ⚠️ "标记"列不存在,跳过更新
- ✅ 只更新物流单号
---
## 📋 前端调用示例
### 修改前(需要传递列位置)
```javascript
// ❌ 需要手动指定列位置
const data = {
accessToken: "...",
fileId: "DUW50RUprWXh2TGJK",
sheetId: "BB08J2",
headerRow: 2,
orderNoColumn: 2, // 需要前端知道列位置
logisticsLinkColumn: 12 // 需要前端知道列位置
};
axios.post('/jarvis/tencentDoc/fillLogisticsByOrderNo', data);
```
---
### 修改后(自动识别)
```javascript
// ✅ 只需要基本信息
const data = {
accessToken: "...",
fileId: "DUW50RUprWXh2TGJK",
sheetId: "BB08J2",
headerRow: 2 // 可选默认为1
};
axios.post('/jarvis/tencentDoc/fillLogisticsByOrderNo', data);
```
---
## 📝 API 参数说明
### 请求参数
| 参数名 | 类型 | 必需 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `accessToken` | String | ✅ 是 | - | 腾讯文档访问令牌 |
| `fileId` | String | ✅ 是 | - | 文件ID |
| `sheetId` | String | ✅ 是 | - | 工作表ID |
| `headerRow` | Integer | ❌ 否 | 1 | 表头所在行号从1开始 |
| `forceStart` | Boolean | ❌ 否 | false | 是否强制从指定行开始 |
| `forceStartRow` | Integer | ❌ 否 | null | 强制起始行号 |
**已移除的参数**
- ~~`orderNoColumn`~~ - 不再需要,自动识别
- ~~`logisticsLinkColumn`~~ - 不再需要,自动识别
---
### 响应示例
```json
{
"msg": "填充物流链接完成",
"code": 200,
"data": {
"startRow": 3,
"endRow": 52,
"filledCount": 45,
"skippedCount": 3,
"errorCount": 0,
"orderNoColumn": 2, // 自动识别的列位置
"logisticsLinkColumn": 12, // 自动识别的列位置
"message": "处理完成:成功填充 45 条,跳过 3 条,错误 0 条"
}
}
```
---
## ⚠️ 注意事项
### 1. 列名要求
**必需列**必须包含特定关键字:
- ✅ "单号"、"订单单号"、"第三方单号" → 都能识别
- ❌ "编号"、"ID" → 无法识别
**建议**:保持列名包含明确的关键字,如"单号"、"物流单号"、"是否安排"、"标记"。
---
### 2. 列名唯一性
如果表格中有多个包含相同关键字的列,只会识别第一个:
**示例**
| 采购单号 | 销售单号 | 物流单号 |
**识别结果**
- "单号"列 → 第1列采购单号
- "物流单号"列 → 第3列
**建议**:如果有多个"单号"列,确保目标列是第一个出现的。
---
### 3. 向后兼容
虽然前端不再需要传递 `orderNoColumn``logisticsLinkColumn`,但如果传递了这些参数,后端会忽略它们,不会报错。
---
## ✅ 总结
### 优化效果
| 方面 | 优化前 | 优化后 |
|------|--------|--------|
| **前端参数** | 5个参数 | 3个参数 ✅ |
| **前端复杂度** | 需要知道列位置 | 只需要基本信息 ✅ |
| **灵活性** | 表格结构变化需要修改前端 | 自动适配 ✅ |
| **出错概率** | 容易传错列索引 | 自动识别,减少错误 ✅ |
| **可维护性** | 前后端都需要维护列信息 | 只有后端识别逻辑 ✅ |
### 关键改进
1.**前端简化**:不再需要传递列位置
2.**自动适配**:表格结构变化时自动识别
3.**错误提示**:更友好的错误信息
4.**日志完善**:详细的识别过程日志
---
**文档版本**1.0
**修改日期**2025-11-05
**状态**:✅ 已完成