1
This commit is contained in:
@@ -69,6 +69,7 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
// 2. 从Redis获取文档配置
|
// 2. 从Redis获取文档配置
|
||||||
String fileId = redisCache.getCacheObject(REDIS_KEY_PREFIX + "fileId");
|
String fileId = redisCache.getCacheObject(REDIS_KEY_PREFIX + "fileId");
|
||||||
String sheetId = redisCache.getCacheObject(REDIS_KEY_PREFIX + "sheetId");
|
String sheetId = redisCache.getCacheObject(REDIS_KEY_PREFIX + "sheetId");
|
||||||
|
Integer headerRow = redisCache.getCacheObject(REDIS_KEY_PREFIX + "headerRow");
|
||||||
Integer startRow = redisCache.getCacheObject(REDIS_KEY_PREFIX + "startRow");
|
Integer startRow = redisCache.getCacheObject(REDIS_KEY_PREFIX + "startRow");
|
||||||
|
|
||||||
// 如果Redis中没有,则使用配置文件中的默认值
|
// 如果Redis中没有,则使用配置文件中的默认值
|
||||||
@@ -78,6 +79,9 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
if (sheetId == null || sheetId.isEmpty()) {
|
if (sheetId == null || sheetId.isEmpty()) {
|
||||||
sheetId = tencentDocConfig.getSheetId();
|
sheetId = tencentDocConfig.getSheetId();
|
||||||
}
|
}
|
||||||
|
if (headerRow == null) {
|
||||||
|
headerRow = tencentDocConfig.getHeaderRow();
|
||||||
|
}
|
||||||
if (startRow == null) {
|
if (startRow == null) {
|
||||||
startRow = tencentDocConfig.getStartRow();
|
startRow = tencentDocConfig.getStartRow();
|
||||||
}
|
}
|
||||||
@@ -86,6 +90,7 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
config.put("accessTokenStatus", accessTokenStatus);
|
config.put("accessTokenStatus", accessTokenStatus);
|
||||||
config.put("fileId", fileId);
|
config.put("fileId", fileId);
|
||||||
config.put("sheetId", sheetId);
|
config.put("sheetId", sheetId);
|
||||||
|
config.put("headerRow", headerRow);
|
||||||
config.put("startRow", startRow);
|
config.put("startRow", startRow);
|
||||||
config.put("appId", tencentDocConfig.getAppId());
|
config.put("appId", tencentDocConfig.getAppId());
|
||||||
config.put("apiBaseUrl", tencentDocConfig.getApiBaseUrl());
|
config.put("apiBaseUrl", tencentDocConfig.getApiBaseUrl());
|
||||||
@@ -162,6 +167,7 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
try {
|
try {
|
||||||
String fileId = params.getString("fileId");
|
String fileId = params.getString("fileId");
|
||||||
String sheetId = params.getString("sheetId");
|
String sheetId = params.getString("sheetId");
|
||||||
|
Integer headerRow = params.getInteger("headerRow");
|
||||||
Integer startRow = params.getInteger("startRow");
|
Integer startRow = params.getInteger("startRow");
|
||||||
|
|
||||||
// 验证必填字段
|
// 验证必填字段
|
||||||
@@ -172,6 +178,11 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
return AjaxResult.error("工作表ID不能为空");
|
return AjaxResult.error("工作表ID不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// headerRow默认值为2
|
||||||
|
if (headerRow == null || headerRow < 1) {
|
||||||
|
headerRow = 2;
|
||||||
|
}
|
||||||
|
|
||||||
// startRow默认值为3
|
// startRow默认值为3
|
||||||
if (startRow == null || startRow < 1) {
|
if (startRow == null || startRow < 1) {
|
||||||
startRow = 3;
|
startRow = 3;
|
||||||
@@ -193,6 +204,7 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
// 保存到Redis(180天有效期)
|
// 保存到Redis(180天有效期)
|
||||||
redisCache.setCacheObject(REDIS_KEY_PREFIX + "fileId", fileId.trim(), 180, TimeUnit.DAYS);
|
redisCache.setCacheObject(REDIS_KEY_PREFIX + "fileId", fileId.trim(), 180, TimeUnit.DAYS);
|
||||||
redisCache.setCacheObject(REDIS_KEY_PREFIX + "sheetId", sheetId.trim(), 180, TimeUnit.DAYS);
|
redisCache.setCacheObject(REDIS_KEY_PREFIX + "sheetId", sheetId.trim(), 180, TimeUnit.DAYS);
|
||||||
|
redisCache.setCacheObject(REDIS_KEY_PREFIX + "headerRow", headerRow, 180, TimeUnit.DAYS);
|
||||||
redisCache.setCacheObject(REDIS_KEY_PREFIX + "startRow", startRow, 180, TimeUnit.DAYS);
|
redisCache.setCacheObject(REDIS_KEY_PREFIX + "startRow", startRow, 180, TimeUnit.DAYS);
|
||||||
|
|
||||||
// 清除该文档的同步进度(配置更新时重置进度,从新的startRow重新开始)
|
// 清除该文档的同步进度(配置更新时重置进度,从新的startRow重新开始)
|
||||||
@@ -205,15 +217,17 @@ public class TencentDocConfigController extends BaseController {
|
|||||||
// 同时更新TencentDocConfig对象(内存中)
|
// 同时更新TencentDocConfig对象(内存中)
|
||||||
tencentDocConfig.setFileId(fileId.trim());
|
tencentDocConfig.setFileId(fileId.trim());
|
||||||
tencentDocConfig.setSheetId(sheetId.trim());
|
tencentDocConfig.setSheetId(sheetId.trim());
|
||||||
|
tencentDocConfig.setHeaderRow(headerRow);
|
||||||
tencentDocConfig.setStartRow(startRow);
|
tencentDocConfig.setStartRow(startRow);
|
||||||
|
|
||||||
log.info("H-TF订单自动写入配置已更新 - fileId: {}, sheetId: {}, startRow: {}",
|
log.info("H-TF订单自动写入配置已更新 - fileId: {}, sheetId: {}, headerRow: {}, startRow: {}",
|
||||||
fileId.trim(), sheetId.trim(), startRow);
|
fileId.trim(), sheetId.trim(), headerRow, startRow);
|
||||||
|
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
result.put("message", "配置更新成功,已保存到Redis(180天有效期)");
|
result.put("message", "配置更新成功,已保存到Redis(180天有效期)");
|
||||||
result.put("fileId", fileId.trim());
|
result.put("fileId", fileId.trim());
|
||||||
result.put("sheetId", sheetId.trim());
|
result.put("sheetId", sheetId.trim());
|
||||||
|
result.put("headerRow", headerRow);
|
||||||
result.put("startRow", startRow);
|
result.put("startRow", startRow);
|
||||||
result.put("hint", "现在录入分销标识为 H-TF 的订单时,将自动追加到此腾讯文档(从第" + startRow + "行开始匹配)");
|
result.put("hint", "现在录入分销标识为 H-TF 的订单时,将自动追加到此腾讯文档(从第" + startRow + "行开始匹配)");
|
||||||
|
|
||||||
|
|||||||
@@ -445,7 +445,164 @@ public class TencentDocController extends BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据单号填充物流链接 - 读取表格数据,根据单号查询订单系统中的物流链接,并填充到表格
|
* 直接填充单个订单的物流链接
|
||||||
|
*
|
||||||
|
* @param params 包含 thirdPartyOrderNo(第三方单号)和 logisticsLink(物流链接)
|
||||||
|
* @return 填充结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/fillSingleLogistics")
|
||||||
|
public AjaxResult fillSingleLogistics(@RequestBody Map<String, Object> params) {
|
||||||
|
try {
|
||||||
|
// 1. 获取参数
|
||||||
|
String thirdPartyOrderNo = (String) params.get("thirdPartyOrderNo");
|
||||||
|
String logisticsLink = (String) params.get("logisticsLink");
|
||||||
|
|
||||||
|
if (thirdPartyOrderNo == null || thirdPartyOrderNo.isEmpty()) {
|
||||||
|
return AjaxResult.error("第三方单号不能为空");
|
||||||
|
}
|
||||||
|
if (logisticsLink == null || logisticsLink.isEmpty()) {
|
||||||
|
return AjaxResult.error("物流链接不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取访问令牌
|
||||||
|
String accessToken;
|
||||||
|
try {
|
||||||
|
accessToken = tencentDocTokenService.getValidAccessToken();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("访问令牌无效,请先完成授权");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 从配置中读取文档信息
|
||||||
|
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 headerRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "headerRow");
|
||||||
|
Integer configStartRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "startRow");
|
||||||
|
|
||||||
|
if (fileId == null || fileId.isEmpty()) {
|
||||||
|
fileId = tencentDocConfig.getFileId();
|
||||||
|
}
|
||||||
|
if (sheetId == null || sheetId.isEmpty()) {
|
||||||
|
sheetId = tencentDocConfig.getSheetId();
|
||||||
|
}
|
||||||
|
if (headerRow == null) {
|
||||||
|
headerRow = tencentDocConfig.getHeaderRow();
|
||||||
|
}
|
||||||
|
if (configStartRow == null) {
|
||||||
|
configStartRow = tencentDocConfig.getStartRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileId == null || fileId.isEmpty() || sheetId == null || sheetId.isEmpty()) {
|
||||||
|
return AjaxResult.error("文档配置不完整,请先完成配置");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 读取表头(从配置的 headerRow 读取,与 startRow 独立)
|
||||||
|
String headerRange = String.format("A%d:Z%d", headerRow, headerRow);
|
||||||
|
|
||||||
|
JSONObject headerData = tencentDocService.readSheetData(accessToken, fileId, sheetId, headerRange);
|
||||||
|
if (headerData == null || !headerData.containsKey("gridData")) {
|
||||||
|
return AjaxResult.error("无法读取表头数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 解析表头,找到"单号"和"物流单号"列
|
||||||
|
JSONObject gridData = headerData.getJSONObject("gridData");
|
||||||
|
JSONArray rows = gridData.getJSONArray("rows");
|
||||||
|
if (rows == null || rows.isEmpty()) {
|
||||||
|
return AjaxResult.error("表头数据为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject headerRowData = rows.getJSONObject(0);
|
||||||
|
JSONArray headerCells = headerRowData.getJSONArray("values");
|
||||||
|
|
||||||
|
int orderNoColumn = -1;
|
||||||
|
int logisticsColumn = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < headerCells.size(); i++) {
|
||||||
|
JSONObject cell = headerCells.getJSONObject(i);
|
||||||
|
if (cell.containsKey("cellValue")) {
|
||||||
|
String cellText = cell.getJSONObject("cellValue").getString("text");
|
||||||
|
if (cellText != null) {
|
||||||
|
if (cellText.contains("单号")) {
|
||||||
|
orderNoColumn = i;
|
||||||
|
} else if (cellText.contains("物流")) {
|
||||||
|
logisticsColumn = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderNoColumn == -1 || logisticsColumn == -1) {
|
||||||
|
return AjaxResult.error("未找到'单号'或'物流'列,请检查表头配置");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("表头解析完成 - 单号列: {}, 物流列: {}", orderNoColumn, logisticsColumn);
|
||||||
|
|
||||||
|
// 6. 读取数据区域,查找指定单号
|
||||||
|
String dataRange = String.format("A%d:Z%d", configStartRow, configStartRow + 999);
|
||||||
|
JSONObject data = tencentDocService.readSheetData(accessToken, fileId, sheetId, dataRange);
|
||||||
|
|
||||||
|
if (data == null || !data.containsKey("gridData")) {
|
||||||
|
return AjaxResult.error("无法读取数据区域");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject dataGridData = data.getJSONObject("gridData");
|
||||||
|
JSONArray dataRows = dataGridData.getJSONArray("rows");
|
||||||
|
|
||||||
|
if (dataRows == null || dataRows.isEmpty()) {
|
||||||
|
return AjaxResult.error("数据区域为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 查找匹配的单号
|
||||||
|
int targetRow = -1;
|
||||||
|
for (int i = 0; i < dataRows.size(); i++) {
|
||||||
|
JSONObject row = dataRows.getJSONObject(i);
|
||||||
|
JSONArray cells = row.getJSONArray("values");
|
||||||
|
|
||||||
|
if (cells != null && cells.size() > orderNoColumn) {
|
||||||
|
JSONObject orderNoCell = cells.getJSONObject(orderNoColumn);
|
||||||
|
if (orderNoCell.containsKey("cellValue")) {
|
||||||
|
String cellText = orderNoCell.getJSONObject("cellValue").getString("text");
|
||||||
|
if (thirdPartyOrderNo.equals(cellText)) {
|
||||||
|
targetRow = configStartRow + i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetRow == -1) {
|
||||||
|
return AjaxResult.error("未找到单号: " + thirdPartyOrderNo);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("找到单号 {} 在第 {} 行", thirdPartyOrderNo, targetRow);
|
||||||
|
|
||||||
|
// 8. 写入物流链接
|
||||||
|
com.ruoyi.jarvis.util.TencentDocApiUtil.writeSheetData(
|
||||||
|
accessToken,
|
||||||
|
fileId,
|
||||||
|
sheetId,
|
||||||
|
String.format("%s%d", getColumnLetter(logisticsColumn), targetRow),
|
||||||
|
logisticsLink,
|
||||||
|
tencentDocConfig.getAppId(),
|
||||||
|
tencentDocConfig.getApiBaseUrl()
|
||||||
|
);
|
||||||
|
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
result.put("thirdPartyOrderNo", thirdPartyOrderNo);
|
||||||
|
result.put("logisticsLink", logisticsLink);
|
||||||
|
result.put("row", targetRow);
|
||||||
|
result.put("column", logisticsColumn);
|
||||||
|
|
||||||
|
return AjaxResult.success("物流链接填充成功", result);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("填充物流链接失败", e);
|
||||||
|
return AjaxResult.error("填充物流链接失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量同步物流链接 - 读取表格数据,根据单号查询订单系统中的物流链接,并填充到表格
|
||||||
* 优化:记录上次处理的最大行数,每次从最大行数-100开始读取,避免重复处理历史数据
|
* 优化:记录上次处理的最大行数,每次从最大行数-100开始读取,避免重复处理历史数据
|
||||||
* 自动获取和管理访问令牌,点击同步时自动刷新token
|
* 自动获取和管理访问令牌,点击同步时自动刷新token
|
||||||
*/
|
*/
|
||||||
@@ -486,15 +643,17 @@ public class TencentDocController extends BaseController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从配置中读取startRow(数据起始行号)
|
// 从配置中读取表头行和数据起始行
|
||||||
|
Integer headerRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "headerRow");
|
||||||
Integer configStartRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "startRow");
|
Integer configStartRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "startRow");
|
||||||
|
|
||||||
|
if (headerRow == null) {
|
||||||
|
headerRow = tencentDocConfig.getHeaderRow();
|
||||||
|
}
|
||||||
if (configStartRow == null) {
|
if (configStartRow == null) {
|
||||||
configStartRow = tencentDocConfig.getStartRow();
|
configStartRow = tencentDocConfig.getStartRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表头行号 = 数据起始行 - 1(例如:数据从第3行开始,表头在第2行)
|
|
||||||
Integer headerRow = configStartRow != null ? configStartRow - 1 : 2;
|
|
||||||
|
|
||||||
// 可选参数:是否强制从指定行开始(如果为true,则忽略Redis记录的最大行数)
|
// 可选参数:是否强制从指定行开始(如果为true,则忽略Redis记录的最大行数)
|
||||||
Boolean forceStart = params.get("forceStart") != null ?
|
Boolean forceStart = params.get("forceStart") != null ?
|
||||||
Boolean.valueOf(params.get("forceStart").toString()) : false;
|
Boolean.valueOf(params.get("forceStart").toString()) : false;
|
||||||
|
|||||||
@@ -50,7 +50,10 @@ public class TencentDocConfig {
|
|||||||
/** 工作表ID(H-TF订单的目标工作表ID) */
|
/** 工作表ID(H-TF订单的目标工作表ID) */
|
||||||
private String sheetId;
|
private String sheetId;
|
||||||
|
|
||||||
/** 起始行号(从第几行开始搜索匹配单号,默认为3,即第3行开始为数据行) */
|
/** 表头行号(表头所在的行,默认为2) */
|
||||||
|
private Integer headerRow = 2;
|
||||||
|
|
||||||
|
/** 起始行号(数据开始的行,从第几行开始搜索匹配单号,默认为3) */
|
||||||
private Integer startRow = 3;
|
private Integer startRow = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,6 +157,14 @@ public class TencentDocConfig {
|
|||||||
this.sheetId = sheetId;
|
this.sheetId = sheetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getHeaderRow() {
|
||||||
|
return headerRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeaderRow(Integer headerRow) {
|
||||||
|
this.headerRow = headerRow;
|
||||||
|
}
|
||||||
|
|
||||||
public Integer getStartRow() {
|
public Integer getStartRow() {
|
||||||
return startRow;
|
return startRow;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user