This commit is contained in:
van
2026-03-05 23:22:49 +08:00
parent f321e40876
commit dc8b0b2fcf
3 changed files with 51 additions and 131 deletions

View File

@@ -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);

View File

@@ -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 - 199endRow = 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);

View File

@@ -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(