Compare commits
7 Commits
26f6f6e058
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4407487fbf | ||
|
|
465e0993d6 | ||
|
|
0a20825831 | ||
|
|
b4749f3516 | ||
|
|
dc8b0b2fcf | ||
|
|
f321e40876 | ||
|
|
a58891ef04 |
@@ -34,15 +34,17 @@ public class InstructionController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取历史消息记录
|
* 获取历史消息记录(支持关键词搜索,在全部历史数据中匹配)
|
||||||
* @param type 消息类型:request(请求) 或 response(响应)
|
* @param type 消息类型:request(请求) 或 response(响应)
|
||||||
* @param limit 获取数量,默认100条
|
* @param limit 获取数量,默认100条;有 keyword 时为返回匹配条数上限,默认200
|
||||||
|
* @param keyword 可选,搜索关键词;不为空时在全部数据中过滤后返回
|
||||||
* @return 历史消息列表
|
* @return 历史消息列表
|
||||||
*/
|
*/
|
||||||
@GetMapping("/history")
|
@GetMapping("/history")
|
||||||
public AjaxResult getHistory(@RequestParam(required = false, defaultValue = "request") String type,
|
public AjaxResult getHistory(@RequestParam(required = false, defaultValue = "request") String type,
|
||||||
@RequestParam(required = false, defaultValue = "100") Integer limit) {
|
@RequestParam(required = false, defaultValue = "100") Integer limit,
|
||||||
java.util.List<String> history = instructionService.getHistory(type, limit);
|
@RequestParam(required = false) String keyword) {
|
||||||
|
java.util.List<String> history = instructionService.getHistory(type, limit, keyword);
|
||||||
return AjaxResult.success(history);
|
return AjaxResult.success(history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ public class OrderRowsController extends BaseController
|
|||||||
TableDataInfo dataTable = getDataTable(list);
|
TableDataInfo dataTable = getDataTable(list);
|
||||||
Date beginTime = getDateFromParams(orderRows.getParams(), "beginTime");
|
Date beginTime = getDateFromParams(orderRows.getParams(), "beginTime");
|
||||||
Date endTime = getDateFromParams(orderRows.getParams(), "endTime");
|
Date endTime = getDateFromParams(orderRows.getParams(), "endTime");
|
||||||
dataTable.setStatistics(buildStatistics(orderRows, beginTime, endTime));
|
// 与列表同数据源,不排除 isCount=0,保证总订单数与分页 total 一致
|
||||||
|
dataTable.setStatistics(buildStatistics(orderRows, beginTime, endTime, true));
|
||||||
return dataTable;
|
return dataTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,21 +173,24 @@ public class OrderRowsController extends BaseController
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/statistics")
|
@GetMapping("/statistics")
|
||||||
public AjaxResult getStatistics(OrderRows orderRows, Date beginTime, Date endTime) {
|
public AjaxResult getStatistics(OrderRows orderRows, Date beginTime, Date endTime) {
|
||||||
return AjaxResult.success(buildStatistics(orderRows, beginTime, endTime));
|
return AjaxResult.success(buildStatistics(orderRows, beginTime, endTime, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按列表相同条件构建统计数据(与 selectOrderRowsList 同条件:日期、unionId 等,排除 isCount=0)
|
* 构建统计数据。
|
||||||
|
* @param forList true=与列表同数据源(不排除 isCount=0),保证总订单数与分页一致;false=独立统计(排除 isCount=0)
|
||||||
*/
|
*/
|
||||||
private Map<String, Object> buildStatistics(OrderRows orderRows, Date beginTime, Date endTime) {
|
private Map<String, Object> buildStatistics(OrderRows orderRows, Date beginTime, Date endTime, boolean forList) {
|
||||||
Map<String, Object> result = new HashMap<>();
|
Map<String, Object> result = new HashMap<>();
|
||||||
List<Long> excludeUnionIds = new ArrayList<>();
|
List<Long> excludeUnionIds = new ArrayList<>();
|
||||||
List<SuperAdmin> superAdminList = superAdminService.selectSuperAdminList(null);
|
if (!forList) {
|
||||||
for (SuperAdmin superAdmin : superAdminList) {
|
List<SuperAdmin> superAdminList = superAdminService.selectSuperAdminList(null);
|
||||||
if (superAdmin.getIsCount() != null && superAdmin.getIsCount() == 0 && superAdmin.getUnionId() != null) {
|
for (SuperAdmin superAdmin : superAdminList) {
|
||||||
try {
|
if (superAdmin.getIsCount() != null && superAdmin.getIsCount() == 0 && superAdmin.getUnionId() != null) {
|
||||||
excludeUnionIds.add(Long.parseLong(superAdmin.getUnionId()));
|
try {
|
||||||
} catch (NumberFormatException e) {
|
excludeUnionIds.add(Long.parseLong(superAdmin.getUnionId()));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,5 +155,26 @@ public class SocialMediaController extends BaseController
|
|||||||
return AjaxResult.error("删除失败: " + e.getMessage());
|
return AjaxResult.error("删除失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 闲鱼文案(手动):根据标题+可选型号生成代下单、教你下单文案,不依赖JD接口
|
||||||
|
*/
|
||||||
|
@Log(title = "闲鱼文案(手动)生成", businessType = BusinessType.OTHER)
|
||||||
|
@PostMapping("/xianyu-wenan/generate")
|
||||||
|
public AjaxResult generateXianyuWenan(@RequestBody Map<String, Object> request)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
String title = (String) request.get("title");
|
||||||
|
String remark = (String) request.get("remark");
|
||||||
|
Map<String, Object> result = socialMediaService.generateXianyuWenan(title, remark);
|
||||||
|
if (Boolean.TRUE.equals(result.get("success"))) {
|
||||||
|
return AjaxResult.success(result);
|
||||||
|
}
|
||||||
|
return AjaxResult.error((String) result.get("error"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("闲鱼文案生成失败", e);
|
||||||
|
return AjaxResult.error("生成失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
config.put("appId", tencentDocConfig.getAppId());
|
config.put("appId", tencentDocConfig.getAppId());
|
||||||
config.put("apiBaseUrl", tencentDocConfig.getApiBaseUrl());
|
config.put("apiBaseUrl", tencentDocConfig.getApiBaseUrl());
|
||||||
|
|
||||||
// 从接口获取 rowCount(匹配 sheetId 的表格实际有数据的行数),替代 Redis 上次记录的行数
|
// 仅从接口获取 rowCount 用于展示,无任何进度缓存;与填充逻辑一致:每次取最后200行
|
||||||
if (fileId != null && !fileId.isEmpty() && sheetId != null && !sheetId.isEmpty()) {
|
if (fileId != null && !fileId.isEmpty() && sheetId != null && !sheetId.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
String accessToken = tencentDocTokenService.getValidAccessToken();
|
String accessToken = tencentDocTokenService.getValidAccessToken();
|
||||||
@@ -104,14 +104,13 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
int rowCount = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId);
|
int rowCount = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId);
|
||||||
if (rowCount > 0) {
|
if (rowCount > 0) {
|
||||||
config.put("currentProgress", rowCount);
|
config.put("currentProgress", rowCount);
|
||||||
int effectiveStart = startRow != null ? startRow : 3;
|
int nextStartRow = Math.max(3, rowCount - 199); // 与填充逻辑一致:取最后200行
|
||||||
int nextStartRow = Math.max(effectiveStart, rowCount - 200);
|
|
||||||
config.put("nextStartRow", nextStartRow);
|
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 {
|
} else {
|
||||||
config.put("currentProgress", null);
|
config.put("currentProgress", null);
|
||||||
config.put("nextStartRow", startRow);
|
config.put("nextStartRow", startRow);
|
||||||
config.put("progressHint", String.format("未获取到 rowCount,将从第 %d 行开始", startRow));
|
config.put("progressHint", "未获取到行数");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
config.put("currentProgress", null);
|
config.put("currentProgress", null);
|
||||||
|
|||||||
@@ -941,24 +941,13 @@ public class TencentDocController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从配置中读取表头行和数据起始行
|
// 从配置中读取表头行(仅用于读表头,不参与范围计算)
|
||||||
Integer headerRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "headerRow");
|
Integer headerRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "headerRow");
|
||||||
Integer configStartRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "startRow");
|
|
||||||
|
|
||||||
if (headerRow == null) {
|
if (headerRow == null) {
|
||||||
headerRow = tencentDocConfig.getHeaderRow();
|
headerRow = tencentDocConfig.getHeaderRow();
|
||||||
}
|
}
|
||||||
if (configStartRow == null) {
|
|
||||||
configStartRow = tencentDocConfig.getStartRow();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 可选参数:是否强制从指定行开始(如果为true,则忽略Redis记录的最大行数)
|
// 是否跳过已推送的订单(默认true,防止重复推送)
|
||||||
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,防止重复推送)
|
|
||||||
Boolean skipPushedOrders = params.get("skipPushedOrders") != null ?
|
Boolean skipPushedOrders = params.get("skipPushedOrders") != null ?
|
||||||
Boolean.valueOf(params.get("skipPushedOrders").toString()) : true;
|
Boolean.valueOf(params.get("skipPushedOrders").toString()) : true;
|
||||||
|
|
||||||
@@ -966,91 +955,30 @@ public class TencentDocController extends BaseController {
|
|||||||
return AjaxResult.error("文档配置不完整,请先配置 fileId 和 sheetId");
|
return AjaxResult.error("文档配置不完整,请先配置 fileId 和 sheetId");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果batchId为空,创建一个新的批次ID和批量推送记录(用于日志记录)
|
// 每次从接口获取当前行数,无任何缓存。取「最后 200 行」:startRow = rowCount - 199,endRow = rowCount
|
||||||
if (batchId == null || batchId.trim().isEmpty()) {
|
int rowCount = tencentDocService.getSheetRowTotal(accessToken, fileId, sheetId);
|
||||||
// 计算起始行和结束行(用于创建批量推送记录)
|
if (rowCount <= 0) {
|
||||||
int estimatedStartRow = configStartRow != null ? configStartRow : (headerRow + 1);
|
log.warn("接口未返回有效行数,无法执行填充");
|
||||||
int estimatedEndRow = estimatedStartRow + 199;
|
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(
|
batchId = batchPushService.createBatchPushRecord(
|
||||||
fileId,
|
fileId,
|
||||||
sheetId,
|
sheetId,
|
||||||
"MANUAL", // 手动触发
|
"MANUAL",
|
||||||
"MANUAL_TRIGGER", // 手动触发来源
|
"MANUAL_TRIGGER",
|
||||||
estimatedStartRow,
|
startRow,
|
||||||
estimatedEndRow
|
endRow
|
||||||
);
|
);
|
||||||
log.info("未提供batchId,自动创建新的批次ID和批量推送记录: {}", batchId);
|
log.info("未提供batchId,自动创建批量推送记录: {}", batchId);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("同步物流配置 - fileId: {}, sheetId: {}, batchId: {}, 配置起始行: {}, 表头行: {}",
|
log.info("同步物流 - fileId: {}, sheetId: {}, 接口行数: {}, 本次范围: 第 {} ~ {} 行(共 {} 行)",
|
||||||
fileId, sheetId, batchId, configStartRow, headerRow);
|
fileId, sheetId, rowCount, startRow, endRow, endRow - startRow + 1);
|
||||||
|
|
||||||
// 不再使用 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 = rowTotal - 200,且必须大于 2(>= MIN_START_ROW_WHEN_USE_ROW_TOTAL)
|
|
||||||
startRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL,
|
|
||||||
Math.max(effectiveStartRow, rowTotal - READ_ROWS_WHEN_USE_ROW_TOTAL));
|
|
||||||
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) {
|
|
||||||
// 配置/缓存的起始行超出表尾时,必须忽略 effectiveStartRow,按 rowTotal 回溯
|
|
||||||
startRow = Math.max(MIN_START_ROW_WHEN_USE_ROW_TOTAL, rowTotal - READ_ROWS_WHEN_USE_ROW_TOTAL);
|
|
||||||
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 : "未获取");
|
|
||||||
|
|
||||||
// 读取表格数据(先读取表头行用于识别列位置)
|
// 读取表格数据(先读取表头行用于识别列位置)
|
||||||
// 根据官方文档,使用 A1 表示法(Excel格式)
|
// 根据官方文档,使用 A1 表示法(Excel格式)
|
||||||
@@ -1164,6 +1092,7 @@ public class TencentDocController extends BaseController {
|
|||||||
orderNoColumn, logisticsLinkColumn, remarkColumn, arrangedColumn, markColumn, phoneColumn);
|
orderNoColumn, logisticsLinkColumn, remarkColumn, arrangedColumn, markColumn, phoneColumn);
|
||||||
|
|
||||||
// 读取数据行:接口实际只能读 200 行,严格限制单次行数,失败时逐步缩小范围重试
|
// 读取数据行:接口实际只能读 200 行,严格限制单次行数,失败时逐步缩小范围重试
|
||||||
|
// 腾讯文档 get_range 的 range 为「结束行不包含」:要读到 endRow 含最后一行,须传 endRow+1
|
||||||
int effectiveEndRow = Math.min(endRow, startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1);
|
int effectiveEndRow = Math.min(endRow, startRow + READ_ROWS_WHEN_USE_ROW_TOTAL - 1);
|
||||||
JSONObject sheetData = null;
|
JSONObject sheetData = null;
|
||||||
int[] retryDecrements = new int[] { 0, 20, 50, 100 };
|
int[] retryDecrements = new int[] { 0, 20, 50, 100 };
|
||||||
@@ -1177,7 +1106,8 @@ public class TencentDocController extends BaseController {
|
|||||||
if (tryEndRow < startRow) {
|
if (tryEndRow < startRow) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String range = String.format("A%d:%s%d", startRow, DATA_RANGE_COL_END, tryEndRow);
|
int rangeEndInclusive = tryEndRow + 1; // API 结束行不包含,+1 才能读到 tryEndRow 行
|
||||||
|
String range = String.format("A%d:%s%d", startRow, DATA_RANGE_COL_END, rangeEndInclusive);
|
||||||
log.info("开始读取数据行 - 行号: {} ~ {} (共 {} 行), range: {} (尝试 decrement={})", startRow, tryEndRow, tryRowCount, range, decrement);
|
log.info("开始读取数据行 - 行号: {} ~ {} (共 {} 行), range: {} (尝试 decrement={})", startRow, tryEndRow, tryRowCount, range, decrement);
|
||||||
try {
|
try {
|
||||||
sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, range);
|
sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, range);
|
||||||
@@ -1207,7 +1137,7 @@ public class TencentDocController extends BaseController {
|
|||||||
for (int decrement : new int[] { 1, 10 }) {
|
for (int decrement : new int[] { 1, 10 }) {
|
||||||
int tryEndRow = Math.max(startRow, effectiveEndRow - decrement);
|
int tryEndRow = Math.max(startRow, effectiveEndRow - decrement);
|
||||||
if (tryEndRow >= startRow) {
|
if (tryEndRow >= startRow) {
|
||||||
String retryRange = String.format("A%d:%s%d", startRow, DATA_RANGE_COL_END, tryEndRow);
|
String retryRange = String.format("A%d:%s%d", startRow, DATA_RANGE_COL_END, tryEndRow + 1); // API 结束行不包含
|
||||||
try {
|
try {
|
||||||
sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, retryRange);
|
sheetData = tencentDocService.readSheetData(accessToken, fileId, sheetId, retryRange);
|
||||||
if (sheetData != null) {
|
if (sheetData != null) {
|
||||||
@@ -1245,7 +1175,7 @@ public class TencentDocController extends BaseController {
|
|||||||
// 无数据时计算下次起始行(不再写入 Redis,下次从接口获取 rowCount)
|
// 无数据时计算下次起始行(不再写入 Redis,下次从接口获取 rowCount)
|
||||||
int emptyCurrentMax = effectiveEndRow;
|
int emptyCurrentMax = effectiveEndRow;
|
||||||
int emptyBacktrack = BACKTRACK_ROWS_WHEN_ALL_SKIPPED;
|
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));
|
Math.max(startRow + MIN_ADVANCE_ROWS, emptyCurrentMax + 1 - emptyBacktrack));
|
||||||
log.info("本批无数据,下次起始行: {}(不再保存进度,下次从接口获取 rowCount)", emptyNextStart);
|
log.info("本批无数据,下次起始行: {}(不再保存进度,下次从接口获取 rowCount)", emptyNextStart);
|
||||||
|
|
||||||
@@ -1254,7 +1184,7 @@ public class TencentDocController extends BaseController {
|
|||||||
result.put("endRow", effectiveEndRow);
|
result.put("endRow", effectiveEndRow);
|
||||||
result.put("lastMaxRow", null); // 不再使用 Redis 存储,改为从接口获取 rowCount
|
result.put("lastMaxRow", null); // 不再使用 Redis 存储,改为从接口获取 rowCount
|
||||||
result.put("nextStartRow", emptyNextStart);
|
result.put("nextStartRow", emptyNextStart);
|
||||||
result.put("rowTotal", rowTotal > 0 ? rowTotal : null);
|
result.put("rowTotal", rowCount > 0 ? rowCount : null);
|
||||||
result.put("currentMaxRow", emptyCurrentMax);
|
result.put("currentMaxRow", emptyCurrentMax);
|
||||||
result.put("filledCount", 0);
|
result.put("filledCount", 0);
|
||||||
result.put("skippedCount", 0);
|
result.put("skippedCount", 0);
|
||||||
@@ -1745,7 +1675,7 @@ public class TencentDocController extends BaseController {
|
|||||||
int currentMaxRow = (maxSuccessRow > 0) ? maxSuccessRow : effectiveEndRow;
|
int currentMaxRow = (maxSuccessRow > 0) ? maxSuccessRow : effectiveEndRow;
|
||||||
// 整批都跳过时用较小回溯,尽快扫到后续行;有写入时用完整回溯覆盖物流变更
|
// 整批都跳过时用较小回溯,尽快扫到后续行;有写入时用完整回溯覆盖物流变更
|
||||||
int backtrack = (successUpdates > 0) ? BACKTRACK_ROWS : BACKTRACK_ROWS_WHEN_ALL_SKIPPED;
|
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));
|
Math.max(startRow + MIN_ADVANCE_ROWS, currentMaxRow + 1 - backtrack));
|
||||||
|
|
||||||
String nextSyncHint = String.format(
|
String nextSyncHint = String.format(
|
||||||
@@ -1759,7 +1689,7 @@ public class TencentDocController extends BaseController {
|
|||||||
result.put("currentMaxRow", currentMaxRow);
|
result.put("currentMaxRow", currentMaxRow);
|
||||||
result.put("lastMaxRow", null); // 不再使用 Redis 存储,改为从接口获取 rowCount
|
result.put("lastMaxRow", null); // 不再使用 Redis 存储,改为从接口获取 rowCount
|
||||||
result.put("nextStartRow", nextStartRow);
|
result.put("nextStartRow", nextStartRow);
|
||||||
result.put("rowTotal", rowTotal > 0 ? rowTotal : null);
|
result.put("rowTotal", rowCount > 0 ? rowCount : null);
|
||||||
result.put("filledCount", filledCount);
|
result.put("filledCount", filledCount);
|
||||||
result.put("skippedCount", skippedCount);
|
result.put("skippedCount", skippedCount);
|
||||||
result.put("errorCount", errorCount);
|
result.put("errorCount", errorCount);
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
package com.ruoyi.web.controller.monitor;
|
package com.ruoyi.web.controller.monitor;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.utils.http.HttpUtils;
|
||||||
import com.ruoyi.framework.web.domain.Server;
|
import com.ruoyi.framework.web.domain.Server;
|
||||||
import com.ruoyi.jarvis.service.ILogisticsService;
|
import com.ruoyi.jarvis.service.ILogisticsService;
|
||||||
import com.ruoyi.jarvis.service.IWxSendService;
|
import com.ruoyi.jarvis.service.IWxSendService;
|
||||||
@@ -28,6 +32,10 @@ public class ServerController
|
|||||||
@Resource
|
@Resource
|
||||||
private IWxSendService wxSendService;
|
private IWxSendService wxSendService;
|
||||||
|
|
||||||
|
/** Ollama 服务地址,用于健康检查 */
|
||||||
|
@Value("${jarvis.ollama.base-url:http://192.168.8.34:11434}")
|
||||||
|
private String ollamaBaseUrl;
|
||||||
|
|
||||||
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
|
@PreAuthorize("@ss.hasPermi('monitor:server:list')")
|
||||||
@GetMapping()
|
@GetMapping()
|
||||||
public AjaxResult getInfo() throws Exception
|
public AjaxResult getInfo() throws Exception
|
||||||
@@ -82,6 +90,38 @@ public class ServerController
|
|||||||
healthMap.put("wxSend", wxSendMap);
|
healthMap.put("wxSend", wxSendMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ollama 服务健康检测(调试用)
|
||||||
|
try {
|
||||||
|
String url = ollamaBaseUrl.replaceAll("/$", "") + "/api/tags";
|
||||||
|
String result = HttpUtils.sendGet(url);
|
||||||
|
if (result != null && !result.trim().isEmpty()) {
|
||||||
|
JSONObject json = JSON.parseObject(result);
|
||||||
|
if (json != null && json.containsKey("models") && !json.containsKey("error")) {
|
||||||
|
Map<String, Object> ollamaMap = new HashMap<>();
|
||||||
|
ollamaMap.put("healthy", true);
|
||||||
|
ollamaMap.put("status", "正常");
|
||||||
|
ollamaMap.put("message", "Ollama 服务可用");
|
||||||
|
ollamaMap.put("serviceUrl", ollamaBaseUrl);
|
||||||
|
healthMap.put("ollama", ollamaMap);
|
||||||
|
} else {
|
||||||
|
putOllamaUnhealthy(healthMap, url, json != null && json.getString("error") != null ? json.getString("error") : "返回格式异常");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
putOllamaUnhealthy(healthMap, url, "返回为空");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
putOllamaUnhealthy(healthMap, ollamaBaseUrl, "健康检测异常: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
return AjaxResult.success(healthMap);
|
return AjaxResult.success(healthMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void putOllamaUnhealthy(Map<String, Object> healthMap, String url, String message) {
|
||||||
|
Map<String, Object> ollamaMap = new HashMap<>();
|
||||||
|
ollamaMap.put("healthy", false);
|
||||||
|
ollamaMap.put("status", "异常");
|
||||||
|
ollamaMap.put("message", message);
|
||||||
|
ollamaMap.put("serviceUrl", url);
|
||||||
|
healthMap.put("ollama", ollamaMap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,6 +205,10 @@ jarvis:
|
|||||||
# 获取评论接口服务地址(后端转发,避免前端跨域)
|
# 获取评论接口服务地址(后端转发,避免前端跨域)
|
||||||
fetch-comments:
|
fetch-comments:
|
||||||
base-url: http://192.168.8.60:5008
|
base-url: http://192.168.8.60:5008
|
||||||
|
# Ollama 大模型服务(监控健康度调试用)
|
||||||
|
ollama:
|
||||||
|
base-url: http://192.168.8.34:11434
|
||||||
|
model: qwen3.5:9b
|
||||||
# 腾讯文档开放平台配置
|
# 腾讯文档开放平台配置
|
||||||
# 文档地址:https://docs.qq.com/open/document/app/openapi/v3/sheet/model/spreadsheet.html
|
# 文档地址:https://docs.qq.com/open/document/app/openapi/v3/sheet/model/spreadsheet.html
|
||||||
tencent:
|
tencent:
|
||||||
|
|||||||
@@ -205,6 +205,10 @@ jarvis:
|
|||||||
# 获取评论接口服务地址(后端转发)
|
# 获取评论接口服务地址(后端转发)
|
||||||
fetch-comments:
|
fetch-comments:
|
||||||
base-url: http://192.168.8.60:5008
|
base-url: http://192.168.8.60:5008
|
||||||
|
# Ollama 大模型服务(监控健康度调试用)
|
||||||
|
ollama:
|
||||||
|
base-url: http://192.168.8.34:11434
|
||||||
|
model: qwen3.5:9b
|
||||||
# 腾讯文档开放平台配置
|
# 腾讯文档开放平台配置
|
||||||
# 文档地址:https://docs.qq.com/open/document/app/openapi/v3/sheet/model/spreadsheet.html
|
# 文档地址:https://docs.qq.com/open/document/app/openapi/v3/sheet/model/spreadsheet.html
|
||||||
tencent:
|
tencent:
|
||||||
|
|||||||
@@ -35,6 +35,15 @@ public interface IInstructionService {
|
|||||||
* @return 历史消息列表
|
* @return 历史消息列表
|
||||||
*/
|
*/
|
||||||
java.util.List<String> getHistory(String type, Integer limit);
|
java.util.List<String> getHistory(String type, Integer limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取历史消息记录(支持关键词搜索,在全部数据中匹配)
|
||||||
|
* @param type 消息类型:request(请求) 或 response(响应)
|
||||||
|
* @param limit 返回数量上限,默认200条
|
||||||
|
* @param keyword 搜索关键词,为空则按 limit 取最近 N 条
|
||||||
|
* @return 历史消息列表
|
||||||
|
*/
|
||||||
|
java.util.List<String> getHistory(String type, Integer limit, String keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -63,5 +63,14 @@ public interface ISocialMediaService
|
|||||||
* 删除提示词模板(恢复默认)
|
* 删除提示词模板(恢复默认)
|
||||||
*/
|
*/
|
||||||
com.ruoyi.common.core.domain.AjaxResult deletePromptTemplate(String key);
|
com.ruoyi.common.core.domain.AjaxResult deletePromptTemplate(String key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据标题(+可选型号备注)生成闲鱼文案(代下单、教你下单),不依赖JD接口
|
||||||
|
*
|
||||||
|
* @param title 商品标题(必填)
|
||||||
|
* @param remark 型号/备注(可选)
|
||||||
|
* @return 包含代下单、教你下单两种文案的 Map
|
||||||
|
*/
|
||||||
|
Map<String, Object> generateXianyuWenan(String title, String remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,9 +131,9 @@ public class CommentServiceImpl implements ICommentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (statMap != null) {
|
if (statMap != null) {
|
||||||
stats.setTotalCount(((Number) statMap.get("totalCount")).longValue());
|
stats.setTotalCount(toLong(statMap.get("totalCount")));
|
||||||
stats.setAvailableCount(((Number) statMap.get("availableCount")).longValue());
|
stats.setAvailableCount(toLong(statMap.get("availableCount")));
|
||||||
stats.setUsedCount(((Number) statMap.get("usedCount")).longValue());
|
stats.setUsedCount(toLong(statMap.get("usedCount")));
|
||||||
// 设置最后一条评论的创建时间
|
// 设置最后一条评论的创建时间
|
||||||
Object lastUpdateTime = statMap.get("lastCommentUpdateTime");
|
Object lastUpdateTime = statMap.get("lastCommentUpdateTime");
|
||||||
if (lastUpdateTime != null) {
|
if (lastUpdateTime != null) {
|
||||||
@@ -439,5 +439,22 @@ public class CommentServiceImpl implements ICommentService {
|
|||||||
|
|
||||||
return statistics;
|
return statistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Map 中的统计值安全转为 long,null 或不存在时返回 0
|
||||||
|
*/
|
||||||
|
private static long toLong(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).longValue();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Long.parseLong(value.toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,12 +142,16 @@ public class InstructionServiceImpl implements IInstructionService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getHistory(String type, Integer limit) {
|
public List<String> getHistory(String type, Integer limit) {
|
||||||
|
return getHistory(type, limit, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getHistory(String type, Integer limit, String keyword) {
|
||||||
if (stringRedisTemplate == null) {
|
if (stringRedisTemplate == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 确定Redis键
|
|
||||||
String key;
|
String key;
|
||||||
if ("request".equalsIgnoreCase(type)) {
|
if ("request".equalsIgnoreCase(type)) {
|
||||||
key = "instruction:request";
|
key = "instruction:request";
|
||||||
@@ -157,11 +161,23 @@ public class InstructionServiceImpl implements IInstructionService {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确定获取数量,默认100条
|
int maxReturn = (limit != null && limit > 0) ? Math.min(limit, 1000) : 200;
|
||||||
int count = (limit != null && limit > 0) ? Math.min(limit, 1000) : 1000;
|
boolean hasKeyword = keyword != null && !keyword.trim().isEmpty();
|
||||||
|
String kwLower = hasKeyword ? keyword.trim().toLowerCase() : null;
|
||||||
|
|
||||||
// 从Redis获取历史消息(索引0到count-1)
|
List<String> messages;
|
||||||
List<String> messages = stringRedisTemplate.opsForList().range(key, 0, count - 1);
|
if (hasKeyword) {
|
||||||
|
// 搜索模式:取全部数据后在内存中按关键词过滤
|
||||||
|
messages = stringRedisTemplate.opsForList().range(key, 0, -1);
|
||||||
|
if (messages == null) messages = Collections.emptyList();
|
||||||
|
messages = messages.stream()
|
||||||
|
.filter(msg -> msg != null && msg.toLowerCase().contains(kwLower))
|
||||||
|
.limit(maxReturn)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
// 普通模式:只取最近 N 条
|
||||||
|
messages = stringRedisTemplate.opsForList().range(key, 0, maxReturn - 1);
|
||||||
|
}
|
||||||
|
|
||||||
return messages != null ? messages : Collections.emptyList();
|
return messages != null ? messages : Collections.emptyList();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -52,6 +52,23 @@ public class SocialMediaServiceImpl implements ISocialMediaService
|
|||||||
put("content:both", "通用文案生成提示词模板\n占位符:%s - 商品名称,%s - 价格信息,%s - 关键词信息");
|
put("content:both", "通用文案生成提示词模板\n占位符:%s - 商品名称,%s - 价格信息,%s - 关键词信息");
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
/** 闲鱼文案-代下单(一键代下)固定正文 */
|
||||||
|
private static final String WENAN_ZCXS =
|
||||||
|
"\n\n 购买后,两小时内出库,物流会电话联系您,同时生成京东官方安装单。送装一体,无需担心。\n\n\n"
|
||||||
|
+ " 1:全新正品,原包装未拆封(京东商城代购,就近直发)\n"
|
||||||
|
+ " 2:可提供下单运单号与电子发票(发票在收到货后找我要)。\n"
|
||||||
|
+ " 3:收货时查看是否有质量或运损问题。可拍照让京东免费申请换新。\n"
|
||||||
|
+ " 4:价格有浮动,不支持补差价,谢谢理解。\n"
|
||||||
|
+ " 5:全国联保,全国统一安装标准。支持官方 400,服务号查询,假一赔十。\n ";
|
||||||
|
/** 闲鱼文案-教你下单固定正文(含“信息更新日期:”占位,生成时替换为当前日期) */
|
||||||
|
private static final String WENAN_FANAN_BX = "本人提供免费指导下单服务,一台也是团购价,细心指导\n" + "\n"
|
||||||
|
+ "【质量】官旗下单,包正的\n" + "【物流】您自己账户可跟踪,24小时发货\n" + "【售后】您自己账户直接联系,无忧售后\n"
|
||||||
|
+ "【安装】专业人员安装,全程无需您操心\n" + "【价格】标价就是到手价,骑共享单车去酒吧,该省省该花花\n"
|
||||||
|
+ "【服务】手把手教您下单,有问题随时咨询\n" + "【体验】所有服务都是官旗提供,价格有内部渠道优惠,同品质更优惠!\n" + "\n"
|
||||||
|
+ "信息更新日期:\n" + "\n" + "捡漏价格不定时有变动,优惠不等人,发「省份+型号」免费咨询当日最低价!";
|
||||||
|
/** 标题/型号清洗:去掉营销敏感词 */
|
||||||
|
private static final String TITLE_CLEAN_REGEX = "以旧|政府|换新|领取|国家|补贴|15%|20%|国补|立减|【|】";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提取商品标题关键词
|
* 提取商品标题关键词
|
||||||
*/
|
*/
|
||||||
@@ -418,5 +435,40 @@ public class SocialMediaServiceImpl implements ISocialMediaService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据标题(+可选型号备注)生成闲鱼文案(代下单、教你下单),不依赖JD接口
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> generateXianyuWenan(String title, String remark) {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
if (StringUtils.isEmpty(title) || StringUtils.isEmpty(title.trim())) {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("error", "商品标题不能为空");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
String cleanTitle = cleanTitleOrRemark(title.trim());
|
||||||
|
String cleanRemark = StringUtils.isNotEmpty(remark) ? cleanTitleOrRemark(remark.trim()) : "";
|
||||||
|
String displayTitle = cleanTitle + cleanRemark;
|
||||||
|
|
||||||
|
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
|
||||||
|
String format = sdf.format(new java.util.Date());
|
||||||
|
String wenanJiaonixiadan = WENAN_FANAN_BX.replace("信息更新日期:", "信息更新日期:" + format);
|
||||||
|
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("daixiadan", "(一键代下) " + displayTitle + "\n" + WENAN_ZCXS);
|
||||||
|
result.put("jiaonixiadan", "【教你下单】 " + displayTitle + "\n" + wenanJiaonixiadan);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清洗标题/型号中的敏感词
|
||||||
|
*/
|
||||||
|
private static String cleanTitleOrRemark(String text) {
|
||||||
|
if (text == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return text.replaceAll(TITLE_CLEAN_REGEX, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -274,46 +274,39 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS
|
|||||||
try {
|
try {
|
||||||
log.info("开始执行批量同步...");
|
log.info("开始执行批量同步...");
|
||||||
|
|
||||||
// 从 Redis 读取配置信息(用户通过前端配置页面设置)
|
// 从 Redis 读取 fileId、sheetId(仅文档标识,无行数缓存)
|
||||||
// 注意:使用与 TencentDocConfigController 相同的 key 前缀
|
|
||||||
final String CONFIG_KEY_PREFIX = "tencent:doc:auto:config:";
|
final String CONFIG_KEY_PREFIX = "tencent:doc:auto:config:";
|
||||||
String fileId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "fileId");
|
String fileId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "fileId");
|
||||||
String sheetId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "sheetId");
|
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);
|
|
||||||
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)) {
|
if (StringUtils.isEmpty(fileId) || StringUtils.isEmpty(sheetId)) {
|
||||||
log.error("腾讯文档配置不完整,无法执行批量同步。请先在前端配置页面设置文件ID和工作表ID");
|
log.error("腾讯文档配置不完整,无法执行批量同步。请先在前端配置页面设置文件ID和工作表ID");
|
||||||
return;
|
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(
|
batchId = batchPushService.createBatchPushRecord(
|
||||||
fileId,
|
fileId,
|
||||||
|
|||||||
Reference in New Issue
Block a user