This commit is contained in:
Leo
2025-11-15 23:45:41 +08:00
parent 98b56ab11b
commit 4f917dce10

View File

@@ -1680,5 +1680,252 @@ public class TencentDocController extends BaseController {
return AjaxResult.error("取消待推送任务失败: " + e.getMessage());
}
}
/**
* 反向同步第三方单号
* 从腾讯文档的物流单号列读取链接,通过链接匹配本地订单,将腾讯文档的单号列值写入到订单的第三方单号字段
*
* @param params 包含 fileId, sheetId, startRow起始行默认850
* @return 同步结果
*/
@PostMapping("/reverseSyncThirdPartyOrderNo")
public AjaxResult reverseSyncThirdPartyOrderNo(@RequestBody Map<String, Object> params) {
String batchId = java.util.UUID.randomUUID().toString().replace("-", "");
try {
// 获取访问令牌
String accessToken;
try {
accessToken = tencentDocTokenService.refreshAccessToken();
log.info("成功刷新访问令牌");
} catch (Exception e) {
log.error("刷新访问令牌失败", e);
try {
accessToken = tencentDocTokenService.getValidAccessToken();
} catch (Exception e2) {
return AjaxResult.error("访问令牌无效请先完成授权。获取授权URL: GET /jarvis/tendoc/authUrl");
}
}
// 从参数或配置中获取文档信息
String fileId = (String) params.get("fileId");
String sheetId = (String) params.get("sheetId");
final String CONFIG_KEY_PREFIX = "tencent:doc:auto:config:";
if (fileId == null || fileId.isEmpty()) {
fileId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "fileId");
if (fileId == null || fileId.isEmpty()) {
fileId = tencentDocConfig.getFileId();
}
}
if (sheetId == null || sheetId.isEmpty()) {
sheetId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "sheetId");
if (sheetId == null || sheetId.isEmpty()) {
sheetId = tencentDocConfig.getSheetId();
}
}
// 从配置中读取表头行
Integer headerRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "headerRow");
if (headerRow == null) {
headerRow = tencentDocConfig.getHeaderRow();
}
// 起始行默认850
Integer startRow = params.get("startRow") != null ?
Integer.valueOf(params.get("startRow").toString()) : 850;
// 结束行默认读取200行
Integer endRow = params.get("endRow") != null ?
Integer.valueOf(params.get("endRow").toString()) : (startRow + 199);
if (accessToken == null || fileId == null || sheetId == null) {
return AjaxResult.error("文档配置不完整,请先配置 fileId 和 sheetId");
}
log.info("反向同步第三方单号开始 - fileId: {}, sheetId: {}, 起始行: {}, 结束行: {}",
fileId, sheetId, startRow, endRow);
// 读取表头,识别列位置
String headerRange = String.format("A%d:Z%d", headerRow, headerRow);
JSONObject headerData = tencentDocService.readSheetData(accessToken, fileId, sheetId, headerRange);
if (headerData == null || !headerData.containsKey("values")) {
return AjaxResult.error("读取表头失败");
}
JSONArray headerValues = headerData.getJSONArray("values");
if (headerValues == null || headerValues.isEmpty()) {
return AjaxResult.error("表头数据为空");
}
JSONArray headerRowData = headerValues.getJSONArray(0);
if (headerRowData == null || headerRowData.isEmpty()) {
return AjaxResult.error("无法识别表头");
}
// 识别列位置
Integer orderNoColumn = null; // "单号"列
Integer logisticsLinkColumn = 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("单号") && !cellValueTrim.contains("物流")) {
orderNoColumn = i;
log.info("✓ 识别到 '单号' 列:第 {} 列(索引{}", i + 1, i);
}
if (logisticsLinkColumn == null && (cellValueTrim.contains("物流单号") || cellValueTrim.contains("物流链接"))) {
logisticsLinkColumn = i;
log.info("✓ 识别到 '物流单号' 列:第 {} 列(索引{}", i + 1, i);
}
}
}
if (orderNoColumn == null || logisticsLinkColumn == null) {
return AjaxResult.error("无法识别表头列,请确保表头包含'单号'和'物流单号'列");
}
// 读取数据行
String dataRange = String.format("A%d:Z%d", startRow, endRow);
log.info("读取数据范围: {}", dataRange);
JSONObject dataResponse = tencentDocService.readSheetData(accessToken, fileId, sheetId, dataRange);
if (dataResponse == null || !dataResponse.containsKey("values")) {
return AjaxResult.error("读取数据失败");
}
JSONArray rows = dataResponse.getJSONArray("values");
if (rows == null || rows.isEmpty()) {
return AjaxResult.error("数据为空");
}
// 统计结果
int successCount = 0;
int skippedCount = 0;
int errorCount = 0;
// 处理每一行
for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
JSONArray row = rows.getJSONArray(rowIndex);
if (row == null || row.size() <= Math.max(orderNoColumn, logisticsLinkColumn)) {
skippedCount++;
continue;
}
int actualRow = startRow + rowIndex;
String logisticsLink = row.getString(logisticsLinkColumn);
String orderNoFromDoc = row.getString(orderNoColumn);
// 跳过物流链接为空的行
if (logisticsLink == null || logisticsLink.trim().isEmpty()) {
log.debug("跳过第 {} 行:物流链接为空", actualRow);
skippedCount++;
logOperation(batchId, fileId, sheetId, "REVERSE_SYNC", null, actualRow, null,
"SKIPPED", "物流链接为空");
continue;
}
// 跳过单号为空的行
if (orderNoFromDoc == null || orderNoFromDoc.trim().isEmpty()) {
log.debug("跳过第 {} 行:单号为空", actualRow);
skippedCount++;
logOperation(batchId, fileId, sheetId, "REVERSE_SYNC", null, actualRow, logisticsLink,
"SKIPPED", "单号为空");
continue;
}
// 清理物流链接(去除空格等)
logisticsLink = logisticsLink.trim();
try {
// 通过物流链接查找订单
JDOrder order = jdOrderService.selectJDOrderByLogisticsLink(logisticsLink);
if (order == null) {
log.warn("未找到匹配的订单 - 行: {}, 物流链接: {}", actualRow, logisticsLink);
errorCount++;
logOperation(batchId, fileId, sheetId, "REVERSE_SYNC", null, actualRow, logisticsLink,
"FAILED", "未找到匹配的订单");
continue;
}
// 检查订单是否已有第三方单号(如果已有且与文档中的不同,跳过)
if (order.getThirdPartyOrderNo() != null && !order.getThirdPartyOrderNo().trim().isEmpty()) {
if (!order.getThirdPartyOrderNo().trim().equals(orderNoFromDoc.trim())) {
log.info("跳过第 {} 行:订单已有第三方单号且不同 - 现有: {}, 文档: {}",
actualRow, order.getThirdPartyOrderNo(), orderNoFromDoc);
skippedCount++;
logOperation(batchId, fileId, sheetId, "REVERSE_SYNC", order.getRemark(), actualRow, logisticsLink,
"SKIPPED", "订单已有第三方单号且不同");
continue;
}
// 如果相同,继续执行(清除物流链接)
}
// 更新订单的第三方单号
order.setThirdPartyOrderNo(orderNoFromDoc.trim());
int updateResult = jdOrderService.updateJDOrder(order);
if (updateResult <= 0) {
log.error("更新订单失败 - 行: {}, 订单ID: {}, 单号: {}",
actualRow, order.getId(), order.getRemark());
errorCount++;
logOperation(batchId, fileId, sheetId, "REVERSE_SYNC", order.getRemark(), actualRow, logisticsLink,
"FAILED", "更新订单失败");
continue;
}
log.info("✓ 更新订单成功 - 行: {}, 订单: {}, 第三方单号: {}",
actualRow, order.getRemark(), orderNoFromDoc);
successCount++;
// 记录成功日志
logOperation(batchId, fileId, sheetId, "REVERSE_SYNC", order.getRemark(), actualRow, logisticsLink,
"SUCCESS", String.format("已将单号 %s 写入订单的第三方单号字段", orderNoFromDoc));
} catch (Exception e) {
log.error("处理第 {} 行失败", actualRow, e);
errorCount++;
logOperation(batchId, fileId, sheetId, "REVERSE_SYNC", null, actualRow, logisticsLink,
"FAILED", "处理异常: " + e.getMessage());
}
// 添加延迟避免API调用频率过高
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
JSONObject result = new JSONObject();
result.put("batchId", batchId);
result.put("startRow", startRow);
result.put("endRow", endRow);
result.put("successCount", successCount);
result.put("skippedCount", skippedCount);
result.put("errorCount", errorCount);
String message = String.format(
"✓ 反向同步完成:成功 %d 条,跳过 %d 条,错误 %d 条\n" +
" 处理范围:第 %d-%d 行\n" +
" 批次ID%s",
successCount, skippedCount, errorCount, startRow, endRow, batchId);
result.put("message", message);
log.info("反向同步第三方单号完成 - {}", message);
return AjaxResult.success("反向同步完成", result);
} catch (Exception e) {
log.error("反向同步第三方单号失败", e);
return AjaxResult.error("反向同步失败: " + e.getMessage());
}
}
}