1
This commit is contained in:
415
doc/自动识别列位置-优化说明.md
Normal file
415
doc/自动识别列位置-优化说明.md
Normal file
@@ -0,0 +1,415 @@
|
||||
# 自动识别列位置 - 优化说明
|
||||
|
||||
## 🎯 优化目标
|
||||
|
||||
**修改前**:列位置可以由前端传递,也可以自动识别
|
||||
**修改后**:所有列位置都由后端自动从表头识别,前端不再需要传递
|
||||
|
||||
---
|
||||
|
||||
## ✅ 优化原因
|
||||
|
||||
### 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
|
||||
**状态**:✅ 已完成
|
||||
|
||||
@@ -462,11 +462,7 @@ public class TencentDocController extends BaseController {
|
||||
String fileId = (String) params.get("fileId");
|
||||
String sheetId = (String) params.get("sheetId");
|
||||
|
||||
// 可选参数:指定列位置
|
||||
Integer orderNoColumn = params.get("orderNoColumn") != null ?
|
||||
Integer.valueOf(params.get("orderNoColumn").toString()) : null; // 单号列索引(从0开始)
|
||||
Integer logisticsLinkColumn = params.get("logisticsLinkColumn") != null ?
|
||||
Integer.valueOf(params.get("logisticsLinkColumn").toString()) : null; // 物流链接列索引(从0开始)
|
||||
// 可选参数:表头行号
|
||||
Integer headerRow = params.get("headerRow") != null ?
|
||||
Integer.valueOf(params.get("headerRow").toString()) : 1; // 表头所在行(默认第1行,从1开始)
|
||||
|
||||
@@ -536,14 +532,15 @@ public class TencentDocController extends BaseController {
|
||||
return AjaxResult.error("无法读取表头,请检查headerRow参数。API响应: " + headerData.toJSONString());
|
||||
}
|
||||
|
||||
// 自动识别列位置(如果未指定)
|
||||
// 自动识别列位置(从表头中识别)
|
||||
Integer orderNoColumn = null; // "单号"列
|
||||
Integer logisticsLinkColumn = null; // "物流单号"列
|
||||
Integer arrangedColumn = null; // "是否安排"列
|
||||
Integer markColumn = null; // "标记"列
|
||||
|
||||
if (orderNoColumn == null || logisticsLinkColumn == null) {
|
||||
JSONArray headerRowData = headerValues.getJSONArray(0);
|
||||
if (headerRowData == null || headerRowData.isEmpty()) {
|
||||
return AjaxResult.error("无法识别表头,请手动指定列位置");
|
||||
return AjaxResult.error("无法识别表头,表头数据为空");
|
||||
}
|
||||
|
||||
// 查找所有相关列
|
||||
@@ -551,32 +548,51 @@ public class TencentDocController extends BaseController {
|
||||
String cellValue = headerRowData.getString(i);
|
||||
if (cellValue != null) {
|
||||
String cellValueTrim = cellValue.trim();
|
||||
|
||||
// 识别"单号"列
|
||||
if (orderNoColumn == null && cellValueTrim.contains("单号")) {
|
||||
orderNoColumn = i;
|
||||
log.info("识别到 '单号' 列:第 {} 列(索引{})", i + 1, i);
|
||||
log.info("✓ 识别到 '单号' 列:第 {} 列(索引{})", i + 1, i);
|
||||
}
|
||||
|
||||
// 识别"物流单号"或"物流链接"列
|
||||
if (logisticsLinkColumn == null && (cellValueTrim.contains("物流单号") || cellValueTrim.contains("物流链接"))) {
|
||||
logisticsLinkColumn = i;
|
||||
log.info("识别到 '物流单号' 列:第 {} 列(索引{})", i + 1, i);
|
||||
log.info("✓ 识别到 '物流单号' 列:第 {} 列(索引{})", i + 1, i);
|
||||
}
|
||||
|
||||
// 识别"是否安排"列(可选)
|
||||
if (arrangedColumn == null && cellValueTrim.contains("是否安排")) {
|
||||
arrangedColumn = i;
|
||||
log.info("识别到 '是否安排' 列:第 {} 列(索引{})", i + 1, i);
|
||||
log.info("✓ 识别到 '是否安排' 列:第 {} 列(索引{})", i + 1, i);
|
||||
}
|
||||
|
||||
// 识别"标记"列(可选)
|
||||
if (markColumn == null && cellValueTrim.contains("标记")) {
|
||||
markColumn = i;
|
||||
log.info("识别到 '标记' 列:第 {} 列(索引{})", i + 1, i);
|
||||
log.info("✓ 识别到 '标记' 列:第 {} 列(索引{})", i + 1, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查必需的列是否都已识别
|
||||
if (orderNoColumn == null) {
|
||||
return AjaxResult.error("无法找到单号列,请手动指定orderNoColumn参数");
|
||||
return AjaxResult.error("无法找到'单号'列,请检查表头是否包含'单号'字段");
|
||||
}
|
||||
if (logisticsLinkColumn == null) {
|
||||
return AjaxResult.error("无法找到物流链接列,请手动指定logisticsLinkColumn参数");
|
||||
return AjaxResult.error("无法找到'物流单号'或'物流链接'列,请检查表头");
|
||||
}
|
||||
|
||||
// 提示可选列的识别情况
|
||||
if (arrangedColumn == null) {
|
||||
log.warn("未找到'是否安排'列,将跳过该字段的更新");
|
||||
}
|
||||
if (markColumn == null) {
|
||||
log.warn("未找到'标记'列,将跳过该字段的更新");
|
||||
}
|
||||
|
||||
log.info("列位置识别完成 - 单号: {}, 物流单号: {}, 是否安排: {}, 标记: {}",
|
||||
orderNoColumn, logisticsLinkColumn, arrangedColumn, markColumn);
|
||||
|
||||
// 读取数据行
|
||||
// 使用 A1 表示法(Excel格式)
|
||||
|
||||
Reference in New Issue
Block a user