This commit is contained in:
van
2026-04-11 22:35:39 +08:00
parent 5205d8c155
commit 94f319514e
14 changed files with 296 additions and 30 deletions

View File

@@ -41,5 +41,10 @@ public interface ITencentDocBatchPushService {
* 获取推送状态和倒计时信息
*/
Map<String, Object> getPushStatusAndCountdown();
/**
* 将长时间仍处于 RUNNING 的批次归档为 INTERRUPTED并可选发企微告警见实现类配置
*/
void reconcileStaleRunningRecords(String fileId);
}

View File

@@ -5,10 +5,15 @@ package com.ruoyi.jarvis.service;
*/
public interface IWxSendService {
/**
* 检查微信推送服务健康状态
* 检查微信推送服务健康状态(会真实下发一条测试消息,仅用于服务监控页「手动测试」)
* @return 健康状态信息,包含是否健康、状态描述等
*/
HealthCheckResult checkHealth();
/**
* 已配置的微信推送健康检查 URL展示用不发起请求
*/
String getHealthCheckServiceUrl();
/**
* 健康检测结果

View File

@@ -73,7 +73,11 @@ public class GoofishOrderPipeline {
ErpGoofishOrder row = upsertFromNotify(appKey, item, lastNotifyJson, "LIST_UPSERT");
tryLinkJdOrder(row);
mergeSummaryFromOrderDetailShape(row, item, "LIST");
refreshDetail(row);
// 仅待发货等需判物流/发货的状态拉详情;其它状态由推送回调更新,减轻开放平台与本地压力
List<Integer> awaitingShip = resolveAutoShipOrderStatuses();
if (row.getOrderStatus() != null && awaitingShip.contains(row.getOrderStatus())) {
refreshDetail(row);
}
syncWaybillFromRedis(row);
tryAutoShip(row);
}
@@ -903,6 +907,36 @@ public class GoofishOrderPipeline {
log.warn("闲管家拉单: pull-max-pages 与 pull-page-size 乘积超过 10000已收敛 maxPages={}", maxPages);
}
int saved = 0;
List<Integer> listStatusFilters = goofishProperties.isPullListOnlyAutoShipStatuses()
? resolveAutoShipOrderStatuses()
: null;
if (listStatusFilters != null && listStatusFilters.isEmpty()) {
listStatusFilters = null;
}
if (listStatusFilters == null) {
saved += pullForAppKeyUpdateTimeRangeOnceForStatuses(appKey, cred, authorizeIds, updateTimeStartSec,
updateTimeEndSec, pageSize, maxPages, null);
} else {
for (Integer st : listStatusFilters) {
saved += pullForAppKeyUpdateTimeRangeOnceForStatuses(appKey, cred, authorizeIds, updateTimeStartSec,
updateTimeEndSec, pageSize, maxPages, st);
try {
Thread.sleep(100);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
break;
}
}
}
return saved;
}
/**
* @param orderStatusFilter null 表示列表请求不带 order_status拉全状态
*/
private int pullForAppKeyUpdateTimeRangeOnceForStatuses(String appKey, IERPAccount cred, List<Long> authorizeIds,
long updateTimeStartSec, long updateTimeEndSec, int pageSize, int maxPages, Integer orderStatusFilter) {
int saved = 0;
for (Long aid : authorizeIds) {
int page = 1;
while (page <= maxPages) {
@@ -910,6 +944,9 @@ public class GoofishOrderPipeline {
q.setAuthorizeId(aid);
q.setUpdateTime(updateTimeStartSec, updateTimeEndSec);
q.setPage(page, pageSize);
if (orderStatusFilter != null) {
q.setOrderStatus(orderStatusFilter);
}
String resp;
try {
resp = q.getResponseBody();
@@ -936,8 +973,8 @@ public class GoofishOrderPipeline {
break;
}
if (page == maxPages) {
log.warn("闲管家拉单已达最大页数 appKey={} aid={} 区间[{},{}];若订单更多请缩小 pull-time-chunk-seconds",
appKey, aid, updateTimeStartSec, updateTimeEndSec);
log.warn("闲管家拉单已达最大页数 appKey={} aid={} 区间[{},{}] orderStatus={};若订单更多请缩小 pull-time-chunk-seconds",
appKey, aid, updateTimeStartSec, updateTimeEndSec, orderStatusFilter);
break;
}
page++;

View File

@@ -6,6 +6,10 @@ import com.ruoyi.jarvis.domain.TencentDocOperationLog;
import com.ruoyi.jarvis.mapper.TencentDocBatchPushRecordMapper;
import com.ruoyi.jarvis.mapper.TencentDocOperationLogMapper;
import com.ruoyi.jarvis.service.ITencentDocBatchPushService;
import com.ruoyi.jarvis.wecom.WxSendGoofishNotifyClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@@ -18,6 +22,10 @@ import java.util.concurrent.TimeUnit;
@Service
public class TencentDocBatchPushServiceImpl implements ITencentDocBatchPushService {
private static final Logger log = LoggerFactory.getLogger(TencentDocBatchPushServiceImpl.class);
private static final String REDIS_STALE_BATCH_NOTIFY_KEY = "tendoc:batch:stale-notified:";
@Resource
private TencentDocBatchPushRecordMapper batchPushRecordMapper;
@@ -27,6 +35,13 @@ public class TencentDocBatchPushServiceImpl implements ITencentDocBatchPushServi
@Resource
private RedisCache redisCache;
@Resource
private WxSendGoofishNotifyClient wxSendGoofishNotifyClient;
/** 仍为 RUNNING 超过该分钟数则归档为 INTERRUPTED可配置 */
@Value("${jarvis.tencent-doc.batch-push.stale-running-threshold-minutes:45}")
private int staleRunningThresholdMinutes;
private static final String DELAYED_PUSH_TASK_KEY = "tendoc:delayed_push:task_scheduled";
private static final String DELAYED_PUSH_SCHEDULE_TIME_KEY = "tendoc:delayed_push:next_time";
@@ -81,6 +96,10 @@ public class TencentDocBatchPushServiceImpl implements ITencentDocBatchPushServi
@Override
public TencentDocBatchPushRecord getBatchPushRecord(String batchId) {
TencentDocBatchPushRecord record = batchPushRecordMapper.selectByBatchId(batchId);
if (record != null && record.getFileId() != null && !record.getFileId().trim().isEmpty()) {
reconcileStaleRunningRecords(record.getFileId());
record = batchPushRecordMapper.selectByBatchId(batchId);
}
if (record != null) {
// 加载关联的操作日志
List<TencentDocOperationLog> logs = operationLogMapper.selectLogsByBatchId(batchId);
@@ -91,6 +110,8 @@ public class TencentDocBatchPushServiceImpl implements ITencentDocBatchPushServi
@Override
public List<TencentDocBatchPushRecord> getBatchPushRecordListWithLogs(String fileId, String sheetId, Integer limit) {
reconcileStaleRunningRecords(fileId);
TencentDocBatchPushRecord query = new TencentDocBatchPushRecord();
query.setFileId(fileId);
query.setSheetId(sheetId);
@@ -156,5 +177,47 @@ public class TencentDocBatchPushServiceImpl implements ITencentDocBatchPushServi
return result;
}
@Override
public void reconcileStaleRunningRecords(String fileId) {
if (fileId == null || fileId.trim().isEmpty() || staleRunningThresholdMinutes <= 0) {
return;
}
Date before = new Date(System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(staleRunningThresholdMinutes));
List<TencentDocBatchPushRecord> stale = batchPushRecordMapper.selectRunningRecordsBefore(fileId, before);
if (stale == null || stale.isEmpty()) {
return;
}
for (TencentDocBatchPushRecord r : stale) {
String bid = r.getBatchId();
if (bid == null || bid.isEmpty()) {
continue;
}
TencentDocBatchPushRecord fresh = batchPushRecordMapper.selectByBatchId(bid);
if (fresh == null || !"RUNNING".equals(fresh.getStatus())) {
continue;
}
String resultMsg = String.format(
"任务已中断:超过 %d 分钟仍处于「执行中」(可能请求超时、进程退出或服务重启),系统已自动标记为结束。批次: %s",
staleRunningThresholdMinutes, bid);
String errMsg = "长时间未完成,自动归档为已中断";
try {
updateBatchPushRecord(bid, "INTERRUPTED", 0, 0, 0, resultMsg, errMsg);
log.warn("归档超时未结束的批量推送记录 batchId={} thresholdMinutes={}", bid, staleRunningThresholdMinutes);
} catch (Exception e) {
log.error("归档超时批量推送记录失败 batchId={}", bid, e);
continue;
}
String dedupeKey = REDIS_STALE_BATCH_NOTIFY_KEY + bid;
if (redisCache.getCacheObject(dedupeKey) != null) {
continue;
}
String pushText = "【腾讯文档推送】批次长时间未结束,已标记为「已中断」\n" + resultMsg;
boolean ok = wxSendGoofishNotifyClient.pushGoofishAgentText(null, pushText);
if (ok) {
redisCache.setCacheObject(dedupeKey, "1", 7, TimeUnit.DAYS);
}
}
}
}

View File

@@ -1,7 +1,9 @@
package com.ruoyi.jarvis.service.impl;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.jarvis.config.TencentDocConfig;
import com.ruoyi.jarvis.wecom.WxSendGoofishNotifyClient;
import com.ruoyi.jarvis.service.ITencentDocBatchPushService;
import com.ruoyi.jarvis.service.ITencentDocDelayedPushService;
import com.ruoyi.jarvis.service.ITencentDocTokenService;
@@ -45,6 +47,9 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS
@Autowired
private ITencentDocBatchPushService batchPushService;
@Autowired
private WxSendGoofishNotifyClient wxSendGoofishNotifyClient;
@Autowired
private TencentDocConfig tencentDocConfig;
@@ -342,13 +347,25 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS
Object result = method.invoke(controller, params);
log.info("✓ 批量同步执行完成,结果: {}", result);
if (result instanceof AjaxResult) {
AjaxResult ar = (AjaxResult) result;
if (!ar.isSuccess() && batchId != null) {
Object msgObj = ar.get(AjaxResult.MSG_TAG);
String msg = msgObj != null ? String.valueOf(msgObj) : "同步接口返回失败";
batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg);
wxSendGoofishNotifyClient.pushGoofishAgentText(null,
"【腾讯文档推送】定时批量同步失败\n批次: " + batchId + "\n" + msg);
}
}
// 不再将 nextStartRow 写入 Redis下次定时执行时从接口获取 rowCount 决定范围
} catch (Exception ex) {
log.error("批量同步调用失败", ex);
if (batchId != null) {
batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0,
null, "批量同步调用失败: " + ex.getMessage());
String msg = "批量同步调用失败: " + (ex.getMessage() != null ? ex.getMessage() : ex.getClass().getSimpleName());
batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg);
wxSendGoofishNotifyClient.pushGoofishAgentText(null,
"【腾讯文档推送】定时批量同步异常\n批次: " + batchId + "\n" + msg);
}
}
@@ -357,8 +374,10 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS
// 更新批量推送记录为失败状态
if (batchId != null) {
try {
batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0,
null, "执行批量同步失败: " + e.getMessage());
String msg = "执行批量同步失败: " + (e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName());
batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0, null, msg);
wxSendGoofishNotifyClient.pushGoofishAgentText(null,
"【腾讯文档推送】定时批量同步异常\n批次: " + batchId + "\n" + msg);
} catch (Exception ex) {
log.error("更新批量推送记录失败", ex);
}

View File

@@ -32,6 +32,11 @@ public class WxSendServiceImpl implements IWxSendService {
healthCheckUrl = wxSendBaseUrl + wxSendHealthPath;
logger.info("微信推送服务健康检查地址已初始化: {}", healthCheckUrl);
}
@Override
public String getHealthCheckServiceUrl() {
return healthCheckUrl;
}
@Override
public IWxSendService.HealthCheckResult checkHealth() {