diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocConfigController.java index ba1a919..6646327 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocConfigController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocConfigController.java @@ -96,7 +96,7 @@ public class TencentDocConfigController extends BaseController { config.put("appId", tencentDocConfig.getAppId()); config.put("apiBaseUrl", tencentDocConfig.getApiBaseUrl()); - // 从接口获取 rowCount(匹配 sheetId 的表格实际有数据的行数),替代 Redis 上次记录的行数 + // 仅从接口获取 rowCount 用于展示,无任何进度缓存;与填充逻辑一致:每次取最后200行 if (fileId != null && !fileId.isEmpty() && sheetId != null && !sheetId.isEmpty()) { try { String accessToken = tencentDocTokenService.getValidAccessToken(); @@ -104,14 +104,13 @@ public class TencentDocConfigController extends BaseController { int rowCount = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId); if (rowCount > 0) { config.put("currentProgress", rowCount); - int effectiveStart = startRow != null ? startRow : 3; - int nextStartRow = Math.max(effectiveStart, rowCount - 199); // 与填充逻辑一致,含最后一行共200行 + int nextStartRow = Math.max(3, rowCount - 199); // 与填充逻辑一致:取最后200行 config.put("nextStartRow", nextStartRow); - config.put("progressHint", String.format("表格当前有 %d 行数据(从接口获取),下次将从第 %d 行开始", rowCount, nextStartRow)); + config.put("progressHint", String.format("表格当前 %d 行(接口获取),每次同步取最后200行:第 %d ~ %d 行", rowCount, nextStartRow, rowCount)); } else { config.put("currentProgress", null); config.put("nextStartRow", startRow); - config.put("progressHint", String.format("未获取到 rowCount,将从第 %d 行开始", startRow)); + config.put("progressHint", "未获取到行数"); } } else { config.put("currentProgress", null); 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 c690479..41988d6 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 @@ -941,24 +941,13 @@ public class TencentDocController extends BaseController { } } - // 从配置中读取表头行和数据起始行 + // 从配置中读取表头行(仅用于读表头,不参与范围计算) Integer headerRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "headerRow"); - Integer configStartRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "startRow"); - if (headerRow == null) { headerRow = tencentDocConfig.getHeaderRow(); } - if (configStartRow == null) { - configStartRow = tencentDocConfig.getStartRow(); - } - // 可选参数:是否强制从指定行开始(如果为true,则忽略Redis记录的最大行数) - Boolean forceStart = params.get("forceStart") != null ? - Boolean.valueOf(params.get("forceStart").toString()) : false; - Integer forceStartRow = params.get("forceStartRow") != null ? - Integer.valueOf(params.get("forceStartRow").toString()) : configStartRow; - - // 新增参数:是否跳过已推送的订单(默认true,防止重复推送) + // 是否跳过已推送的订单(默认true,防止重复推送) Boolean skipPushedOrders = params.get("skipPushedOrders") != null ? Boolean.valueOf(params.get("skipPushedOrders").toString()) : true; @@ -966,91 +955,30 @@ public class TencentDocController extends BaseController { return AjaxResult.error("文档配置不完整,请先配置 fileId 和 sheetId"); } - // 如果batchId为空,创建一个新的批次ID和批量推送记录(用于日志记录) - if (batchId == null || batchId.trim().isEmpty()) { - // 计算起始行和结束行(用于创建批量推送记录) - int estimatedStartRow = configStartRow != null ? configStartRow : (headerRow + 1); - int estimatedEndRow = estimatedStartRow + 199; + // 每次从接口获取当前行数,无任何缓存。取「最后 200 行」:startRow = rowCount - 199,endRow = rowCount + int rowCount = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId); + if (rowCount <= 0) { + log.warn("接口未返回有效行数,无法执行填充"); + return AjaxResult.error("无法获取表格行数,请检查文档与授权"); + } + int startRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, rowCount - (READ_ROWS_WHEN_USE_ROW_TOTAL - 1)); + int endRow = rowCount; + // 如果batchId为空,创建批量推送记录(用于日志) + if (batchId == null || batchId.trim().isEmpty()) { batchId = batchPushService.createBatchPushRecord( fileId, sheetId, - "MANUAL", // 手动触发 - "MANUAL_TRIGGER", // 手动触发来源 - estimatedStartRow, - estimatedEndRow + "MANUAL", + "MANUAL_TRIGGER", + startRow, + endRow ); - log.info("未提供batchId,自动创建新的批次ID和批量推送记录: {}", batchId); + log.info("未提供batchId,自动创建批量推送记录: {}", batchId); } - log.info("同步物流配置 - fileId: {}, sheetId: {}, batchId: {}, 配置起始行: {}, 表头行: {}", - fileId, sheetId, batchId, configStartRow, headerRow); - - // 不再使用 Redis 存储进度,改为每次从接口获取 rowCount(匹配 sheetId 的表格实际有数据的行数) - int effectiveStartRow = configStartRow != null ? configStartRow : (headerRow + 1); - int startRow; - int endRow; - - // 从接口获取 rowCount(匹配 sheetId 的表格实际有数据的行数),替代 Redis 上次记录的行数 - int rowTotal = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId); - if (rowTotal > 0) { - if (forceStartRow != null) { - startRow = forceStartRow; - endRow = Math.min(rowTotal, startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1); - } else { - // startRow = rowCount - 199,使 endRow 能取到 rowCount,不漏最后一行(共 200 行含最后一行) - startRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, - Math.max(effectiveStartRow, rowTotal - (READ_ROWS_WHEN_USE_ROW_TOTAL - 1))); - endRow = Math.min(rowTotal, startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1); - } - log.info("使用接口 rowCount={},本次范围: 第 {} ~ {} 行(共 {} 行,单次最多 {} 行)", - rowTotal, startRow, endRow, endRow - startRow + 1, READ_ROWS_WHEN_USE_ROW_TOTAL); - } else { - // 未取到 rowCount 时使用配置起始行(不再从 Redis 读取进度) - if (forceStartRow != null) { - startRow = forceStartRow; - endRow = startRow + API_MAX_ROWS_PER_REQUEST - 1; - log.info("使用强制指定的起始行: {}, 结束行: {}(未取到 rowCount)", startRow, endRow); - } else { - startRow = effectiveStartRow; - endRow = startRow + API_MAX_ROWS_PER_REQUEST - 1; - log.info("从配置起始行开始: {} ~ {}(未取到 rowCount)", startRow, endRow); - } - } - - // 严格限制单次 range 行数(接口实际只能读 200 行) - if (endRow - startRow + 1 > READ_ROWS_WHEN_USE_ROW_TOTAL) { - endRow = startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1; - log.info("已截断结束行以符合 API 限制: endRow={}, 行数={}", endRow, READ_ROWS_WHEN_USE_ROW_TOTAL); - } - - // 若之前未取到 rowTotal 或来自 fallback,再次尝试并用 sheet 最大行硬性限制,避免读到 348 超出 324 行 - if (rowTotal <= 0) { - rowTotal = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId); - } - if (rowTotal > 0) { - if (startRow > rowTotal) { - // 按 rowCount 回溯,且保证 endRow=rowCount 不漏最后一行(rowCount - 199 起共 200 行) - startRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, rowTotal - (READ_ROWS_WHEN_USE_ROW_TOTAL - 1)); - endRow = Math.min(rowTotal, startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1); - log.info("按 rowCount={} 修正范围(含最后一行): 第 {} ~ {} 行", rowTotal, startRow, endRow); - } else if (endRow > rowTotal) { - endRow = rowTotal; - log.info("按 rowTotal={} 截断结束行: endRow={}", rowTotal, endRow); - } - } else { - // 未取到 rowTotal 时:若 startRow 过高(如 350 超出实际 324 行)可能是配置/Redis 过时,回溯至配置最小值 - final int START_ROW_SAFE_THRESHOLD = 350; - if (startRow > START_ROW_SAFE_THRESHOLD) { - int oldStart = startRow; - startRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, headerRow != null ? headerRow + 1 : 3); - endRow = startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1; - log.warn("rowTotal 未获取且 startRow={} 过高,保守回溯至表头下一行 {},范围: {} ~ {}", oldStart, startRow, startRow, endRow); - } - } - - log.info("开始填充物流链接 - 文件ID: {}, 工作表ID: {}, 起始行: {}, 结束行: {}, rowTotal: {}", - fileId, sheetId, startRow, endRow, rowTotal > 0 ? rowTotal : "未获取"); + log.info("同步物流 - fileId: {}, sheetId: {}, 接口行数: {}, 本次范围: 第 {} ~ {} 行(共 {} 行)", + fileId, sheetId, rowCount, startRow, endRow, endRow - startRow + 1); // 读取表格数据(先读取表头行用于识别列位置) // 根据官方文档,使用 A1 表示法(Excel格式) @@ -1247,7 +1175,7 @@ public class TencentDocController extends BaseController { // 无数据时计算下次起始行(不再写入 Redis,下次从接口获取 rowCount) int emptyCurrentMax = effectiveEndRow; int emptyBacktrack = BACKTRACK_ROWS_WHEN_ALL_SKIPPED; - int emptyNextStart = Math.max(effectiveStartRow, + int emptyNextStart = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, Math.max(startRow + MIN_ADVANCE_ROWS, emptyCurrentMax + 1 - emptyBacktrack)); log.info("本批无数据,下次起始行: {}(不再保存进度,下次从接口获取 rowCount)", emptyNextStart); @@ -1256,7 +1184,7 @@ public class TencentDocController extends BaseController { result.put("endRow", effectiveEndRow); result.put("lastMaxRow", null); // 不再使用 Redis 存储,改为从接口获取 rowCount result.put("nextStartRow", emptyNextStart); - result.put("rowTotal", rowTotal > 0 ? rowTotal : null); + result.put("rowTotal", rowCount > 0 ? rowCount : null); result.put("currentMaxRow", emptyCurrentMax); result.put("filledCount", 0); result.put("skippedCount", 0); @@ -1747,7 +1675,7 @@ public class TencentDocController extends BaseController { int currentMaxRow = (maxSuccessRow > 0) ? maxSuccessRow : effectiveEndRow; // 整批都跳过时用较小回溯,尽快扫到后续行;有写入时用完整回溯覆盖物流变更 int backtrack = (successUpdates > 0) ? BACKTRACK_ROWS : BACKTRACK_ROWS_WHEN_ALL_SKIPPED; - int nextStartRow = Math.max(effectiveStartRow, + int nextStartRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, Math.max(startRow + MIN_ADVANCE_ROWS, currentMaxRow + 1 - backtrack)); String nextSyncHint = String.format( @@ -1761,7 +1689,7 @@ public class TencentDocController extends BaseController { result.put("currentMaxRow", currentMaxRow); result.put("lastMaxRow", null); // 不再使用 Redis 存储,改为从接口获取 rowCount result.put("nextStartRow", nextStartRow); - result.put("rowTotal", rowTotal > 0 ? rowTotal : null); + result.put("rowTotal", rowCount > 0 ? rowCount : null); result.put("filledCount", filledCount); result.put("skippedCount", skippedCount); result.put("errorCount", errorCount); diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java index e6acec6..b934c22 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocDelayedPushServiceImpl.java @@ -274,45 +274,38 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS try { log.info("开始执行批量同步..."); - // 从 Redis 读取配置信息(用户通过前端配置页面设置) - // 注意:使用与 TencentDocConfigController 相同的 key 前缀 + // 从 Redis 读取 fileId、sheetId(仅文档标识,无行数缓存) final String CONFIG_KEY_PREFIX = "tencent:doc:auto:config:"; String fileId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "fileId"); String sheetId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "sheetId"); - Integer startRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "startRow"); - - if (startRow == null) { - startRow = MIN_START_ROW_WHEN_USE_ROW_TOTAL; - } - int batchStartRow = startRow; - int batchEndRow = startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1; - - // 先获取 rowTotal,确保 batch 记录使用有效范围(避免 350 超出 324 行) - try { - String accessToken = tokenService.getValidAccessToken(); - if (accessToken != null) { - int rowTotal = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId); - if (rowTotal > 0) { - if (startRow > rowTotal) { - batchStartRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, rowTotal - (READ_ROWS_WHEN_USE_ROW_TOTAL - 1)); - batchEndRow = Math.min(rowTotal, batchStartRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1); - log.info("配置起始行 {} 超出表尾 rowTotal={},修正为第 {} ~ {} 行", startRow, rowTotal, batchStartRow, batchEndRow); - } else { - batchEndRow = Math.min(rowTotal, startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1); - } - } - } - } catch (Exception e) { - log.warn("获取 rowTotal 失败,使用配置起始行创建 batch: {}", e.getMessage()); - } - - log.info("读取配置 - fileId: {}, sheetId: {}, 创建 batch 范围: {} ~ {}", fileId, sheetId, batchStartRow, batchEndRow); - if (StringUtils.isEmpty(fileId) || StringUtils.isEmpty(sheetId)) { log.error("腾讯文档配置不完整,无法执行批量同步。请先在前端配置页面设置文件ID和工作表ID"); return; } + + // 每次从接口获取当前行数,取最后 200 行,无任何缓存 + int batchStartRow; + int batchEndRow; + try { + String accessToken = tokenService.getValidAccessToken(); + if (accessToken == null) { + log.warn("无法获取 accessToken,跳过本次批量同步"); + return; + } + int rowCount = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId); + if (rowCount <= 0) { + log.warn("接口未返回有效行数,跳过本次批量同步"); + return; + } + batchStartRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, rowCount - (READ_ROWS_WHEN_USE_ROW_TOTAL - 1)); + batchEndRow = rowCount; + } catch (Exception e) { + log.warn("获取表格行数失败,跳过本次批量同步: {}", e.getMessage()); + return; + } + + log.info("本次范围(接口 rowCount 取最后200行)- fileId: {}, sheetId: {}, {} ~ {}", fileId, sheetId, batchStartRow, batchEndRow); // 创建批量推送记录(使用有效范围) batchId = batchPushService.createBatchPushRecord(