# 自动识别列位置 - 优化说明 ## 🎯 优化目标 **修改前**:列位置可以由前端传递,也可以自动识别 **修改后**:所有列位置都由后端自动从表头识别,前端不再需要传递 --- ## ✅ 优化原因 ### 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("物流单号")`
或 `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 **状态**:✅ 已完成