javacodeadmin/ruoyi-system/src/main/java/com/ruoyi/system/controllerUtil/ScheduledTaskUtil.java

1212 lines
41 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.ruoyi.system.ControllerUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.Order;
import com.ruoyi.system.domain.Users;
import com.ruoyi.system.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
/**
* 定时任务工具类
*
* 主要功能:
* 1. 订单派单超时处理20分钟自动处理
* 2. 订单状态超时检查
* 3. 系统数据清理
* 4. 任务调度管理
* 5. 异步任务执行
* 6. 师傅暂停状态自动恢复处理
*
* 使用方式:
* - 自动定时执行:通过@Scheduled注解配置的定时任务会自动执行
* - 手动调用:可直接调用相应的方法进行手动处理
* - 异步执行:支持异步执行耗时任务,不阻塞主线程
* - 项目启动时自动执行实现CommandLineRunner接口项目启动时自动调用run方法
*
* @author RuoYi
* @date 2024-01-01
*/
@Component
public class ScheduledTaskUtil implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(ScheduledTaskUtil.class);
// 订单服务通过Spring工具类获取
private static IOrderService orderService;
// 师傅用户服务通过Spring工具类获取
private static IUsersService usersService;
// 订单服务通过Spring工具类获取
private static IWorkerMoneyLogService workerMoneyLogService;
// 线程池,用于异步执行任务
private ThreadPoolExecutor executorService;
// 任务执行统计
private final Map<String, TaskStatistics> taskStats = new ConcurrentHashMap<>();
private static ICouponsService couponsService = SpringUtils.getBean(ICouponsService.class);
private static ICouponUserService couponUserService = SpringUtils.getBean(ICouponUserService.class);
/**
* 初始化方法在Bean创建后执行
*/
@PostConstruct
public void init() {
// 获取订单服务
try {
orderService = SpringUtils.getBean(IOrderService.class);
} catch (Exception e) {
log.warn("获取订单服务失败,部分功能可能不可用: {}", e.getMessage());
}
// 获取师傅用户服务
try {
usersService = SpringUtils.getBean(IUsersService.class);
} catch (Exception e) {
log.warn("获取师傅用户服务失败,部分功能可能不可用: {}", e.getMessage());
}
// 获取订单服务
try {
workerMoneyLogService = SpringUtils.getBean(IWorkerMoneyLogService.class);
} catch (Exception e) {
log.warn("获取订单服务失败,部分功能可能不可用: {}", e.getMessage());
}
// 初始化线程池
initThreadPool();
log.info("定时任务工具类初始化完成");
}
/**
* 初始化线程池
*/
private void initThreadPool() {
executorService = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 线程空闲时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 工作队列
new ThreadFactory() { // 线程工厂
private int counter = 0;
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "ScheduledTask-" + counter++);
t.setDaemon(true);
return t;
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
}
@Override
public void run(String... args) throws Exception {
log.info("项目启动,自动执行定时任务...");
handleDispatchTimeout();
checkOrderStatusTimeout();
cleanupSystemData();
healthCheck();
updateWorkerMoneyLook();
DispatchWorkerForOrder();
autoResumeWorkerOrderStatus(); // 添加师傅暂停状态自动恢复任务
log.info("定时任务自动执行完成");
}
// ========================= 定时任务方法 =========================
/**
* 师傅收益7天定时冻结的解冻
* 每10分钟执行一次
*/
@Scheduled(fixedRate = 20 * 60 * 1000) // 每10分钟执行一次
public void updateWorkerMoneyLook() {
String taskName = "订单状态超时检查";
long startTime = System.currentTimeMillis();
try {
workerMoneyLogService=SpringUtils.getBean(IWorkerMoneyLogService.class);
workerMoneyLogService.updateWorkerMoneyLogStatusIfEndlookAfterNow();
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
}
/**
* 派单10分钟一次对没有派单的进行派单
* 每10分钟执行一次
*/
@Scheduled(fixedRate = 10 * 60 * 1000) // 每10分钟执行一次
public void DispatchWorkerForOrder() {
String taskName = "订单派单检查并重新派单";
long startTime = System.currentTimeMillis();
try {
Order order = new Order();
order.setStatus(1L);
order.setIsAccept(0);
order.setQiangdan("1");
List<Order> orders = orderService.selectOrderList(order);
for (Order orderdata : orders) {
DispatchUtil.dispatchOrder(orderdata.getId());
}
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
}
/**
* 订单派单超时处理任务
* 每5分钟执行一次检查派单超过20分钟的订单
*
* 使用说明:
* - 自动执行,无需手动调用
* - 处理逻辑:将超时未接单的订单重新派发或标记为超时
* - 执行频率每5分钟可根据业务需求调整
*/
@Scheduled(fixedRate = 5 * 60 * 1000) // 每5分钟执行一次
public void handleDispatchTimeout() {
String taskName = "订单派单超时处理";
long startTime = System.currentTimeMillis();
log.info("开始执行{}任务", taskName);
try {
// 获取派单超过20分钟的订单
List<Order> timeoutOrders = getDispatchTimeoutOrders();
if (timeoutOrders.isEmpty()) {
log.info("{}任务执行完成,无超时订单", taskName);
return;
}
log.info("发现{}个派单超时订单,开始处理", timeoutOrders.size());
// 异步处理超时订单
processTimeoutOrdersAsync(timeoutOrders);
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
log.info("{}任务执行完成,耗时{}毫秒", taskName, System.currentTimeMillis() - startTime);
}
/**
* 订单状态超时检查任务
* 每10分钟执行一次检查各种状态的订单是否超时
*
* 使用说明:
* - 检查服务中订单是否超过预期时间
* - 检查待支付订单是否超时
* - 处理异常状态订单
*/
@Scheduled(fixedRate = 10 * 60 * 1000) // 每10分钟执行一次
public void checkOrderStatusTimeout() {
String taskName = "订单状态超时检查";
long startTime = System.currentTimeMillis();
try {
log.info("开始执行{}任务", taskName);
// 检查服务中超时订单
checkServiceTimeoutOrders();
// 检查待支付超时订单
checkPaymentTimeoutOrders();
// 检查其他异常状态订单
checkAbnormalStatusOrders();
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
}
/**
* 订单状态超时检查任务
* 每10分钟执行一次检查各种状态的订单是否超时
*
* 使用说明:
* - 检查服务中订单是否超过预期时间
* - 检查待支付订单是否超时
* - 处理异常状态订单
*/
@Scheduled(fixedRate = 60 * 60 * 1000) // 每60分钟执行一次
public void checkcouponsStatusTimeout() {
String taskName = "优惠券过期检查时检查";
long startTime = System.currentTimeMillis();
try {
log.info("开始执行{}任务", taskName);
Map<String, Object> stat = CouponUtil.processExpiredCoupons(couponsService, couponUserService);
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
}
/**
* 系统数据清理任务
* 每天凌晨2点执行清理过期数据
*
* 使用说明:
* - 清理30天前的日志数据
* - 清理临时文件
* - 优化数据库性能
*/
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
public void cleanupSystemData() {
String taskName = "系统数据清理";
long startTime = System.currentTimeMillis();
try {
log.info("开始执行{}任务", taskName);
// 清理过期订单日志
cleanupExpiredOrderLogs();
// 清理临时文件
cleanupTempFiles();
// 清理任务统计数据
cleanupTaskStatistics();
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
log.info("{}任务执行完成", taskName);
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
}
/**
* 健康检查任务
* 每30分钟执行一次检查系统健康状态
*/
@Scheduled(fixedRate = 30 * 60 * 1000) // 每30分钟执行一次
public void healthCheck() {
String taskName = "系统健康检查";
log.info("开始执行{}任务", taskName);
long startTime = System.currentTimeMillis();
try {
log.info("开始执行{}任务", taskName);
// 检查数据库连接
checkDatabaseConnection();
// 检查线程池状态
checkThreadPoolStatus();
// 检查系统资源
checkSystemResources();
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
}
/**
* 测试定时任务
* 每3秒执行一次打印测试信息
*
* 使用说明:
* - 用于测试定时任务的执行情况
*/
@Scheduled(fixedRate = 3 * 60 * 1000) // // 每3分钟执行一次
public void TheCallWordercheduledTask() throws Exception {
OrderUtil.TheCallWorder();
log.info("每3分钟执行一次师傅的语音通知... 当前时间: {}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
/**
* 师傅暂停状态自动恢复处理任务
* 每1小时执行一次检查暂停时间已过期的师傅并自动恢复接单状态
*
* 处理逻辑:
* 1. 查询type=2且is_stop=1的师傅数据
* 2. 判断prohibit_time是否小于当前时间即暂停时间已过期
* 3. 将过期的师傅is_stop设置为0恢复接单状态
*
* 使用说明:
* - 自动执行,无需手动调用
* - 执行频率每1小时
* - 确保师傅暂停时间到期后能自动恢复接单
*/
@Scheduled(fixedRate = 60 * 60 * 1000) // 每1小时执行一次
public void autoResumeWorkerOrderStatus() {
String taskName = "师傅暂停状态自动恢复";
long startTime = System.currentTimeMillis();
log.info("开始执行{}任务", taskName);
try {
// 确保usersService已初始化
if (usersService == null) {
usersService = SpringUtils.getBean(IUsersService.class);
}
if (usersService == null) {
log.warn("{}任务跳过执行,师傅用户服务未初始化", taskName);
return;
}
// 查询所有暂停状态的师傅type=2 且 is_stop=1
List<Users> pausedWorkers = usersService.selectPausedWorkers();
if (pausedWorkers.isEmpty()) {
log.info("{}任务执行完成,无暂停状态的师傅", taskName);
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
return;
}
log.info("发现{}个暂停状态的师傅,开始检查恢复条件", pausedWorkers.size());
// 当前时间
Date currentTime = new Date();
int resumedCount = 0;
// 遍历所有暂停的师傅
for (Users worker : pausedWorkers) {
try {
// 检查是否应该恢复接单状态
if (shouldResumeWorker(worker, currentTime)) {
// 恢复师傅接单状态
resumeWorkerOrderStatus(worker);
resumedCount++;
log.info("师傅{}(ID:{})暂停时间已过期,已自动恢复接单状态",
worker.getName(), worker.getId());
}
} catch (Exception e) {
log.error("处理师傅{}(ID:{})暂停状态恢复失败",
worker.getName(), worker.getId(), e);
}
}
log.info("{}任务执行完成,共处理{}个师傅,成功恢复{}个师傅的接单状态",
taskName, pausedWorkers.size(), resumedCount);
updateTaskStatistics(taskName, true, System.currentTimeMillis() - startTime);
} catch (Exception e) {
log.error("{}任务执行失败", taskName, e);
updateTaskStatistics(taskName, false, System.currentTimeMillis() - startTime);
}
log.info("{}任务执行完成,耗时{}毫秒", taskName, System.currentTimeMillis() - startTime);
}
// ========================= 业务处理方法 =========================
/**
* 获取派单超过20分钟的订单
*
* @return 超时订单列表
*/
private List<Order> getDispatchTimeoutOrders() {
try {
// 计算20分钟前的时间
LocalDateTime timeoutThreshold = LocalDateTime.now().minusMinutes(20);
Date thresholdDate = Date.from(timeoutThreshold.atZone(ZoneId.systemDefault()).toInstant());
// 构建查询条件
Order queryOrder = new Order();
queryOrder.setJsonStatus(1); // 假设1表示已派单状态
// 如果orderService可用,执行查询
if (orderService != null) {
// 这里需要根据实际的Service方法进行调用
// 示例return orderService.selectTimeoutDispatchOrders(thresholdDate);
log.debug("查询派单超时订单,截止时间:{}", thresholdDate);
}
// 临时返回空列表,实际使用时需要实现相应的查询方法
return new ArrayList<>();
} catch (Exception e) {
log.error("获取派单超时订单失败", e);
return new ArrayList<>();
}
}
/**
* 异步处理超时订单
*
* @param timeoutOrders 超时订单列表
*/
@Async
public void processTimeoutOrdersAsync(List<Order> timeoutOrders) {
executorService.submit(() -> {
for (Order order : timeoutOrders) {
try {
processTimeoutOrder(order);
} catch (Exception e) {
log.error("处理超时订单失败,订单号:{}", order.getOrderId(), e);
}
}
});
}
/**
* 处理单个超时订单
*
* @param order 超时订单
*/
private void processTimeoutOrder(Order order) {
log.info("处理派单超时订单:{}", order.getOrderId());
try {
// 业务处理逻辑
if (canRedispatch(order)) {
// 重新派单
redispatchOrder(order);
log.info("订单{}重新派单成功", order.getOrderId());
} else {
// 标记为超时
markOrderAsTimeout(order);
log.info("订单{}标记为派单超时", order.getOrderId());
}
// 记录处理日志
recordOrderProcessLog(order, "派单超时处理");
} catch (Exception e) {
log.error("处理超时订单{}失败", order.getOrderId(), e);
}
}
/**
* 检查服务中的超时订单
*/
private void checkServiceTimeoutOrders() {
log.info("检查服务中超时订单");
try {
// 获取服务中超过预期时间的订单
LocalDateTime timeoutThreshold = LocalDateTime.now().minusHours(4); // 假设服务超过4小时为超时
// 处理服务超时订单
processServiceTimeoutOrders(timeoutThreshold);
} catch (Exception e) {
log.error("检查服务中超时订单失败", e);
}
}
/**
* 检查待支付超时订单
*/
private void checkPaymentTimeoutOrders() {
log.info("检查待支付超时订单");
try {
// 获取待支付超过30分钟的订单
LocalDateTime timeoutThreshold = LocalDateTime.now().minusMinutes(30);
// 处理超时订单
processPaymentTimeoutOrders(timeoutThreshold);
} catch (Exception e) {
log.error("检查待支付超时订单失败", e);
}
}
/**
* 检查异常状态订单
*/
private void checkAbnormalStatusOrders() {
log.info("检查异常状态订单");
try {
// 检查状态异常的订单
// 例如:长时间处于某个中间状态的订单
LocalDateTime abnormalThreshold = LocalDateTime.now().minusHours(24);
processAbnormalStatusOrders(abnormalThreshold);
} catch (Exception e) {
log.error("检查异常状态订单失败", e);
}
}
// ========================= 工具方法 =========================
/**
* 判断订单是否可以重新派单
*
* @param order 订单对象
* @return true-可以重新派单false-不能重新派单
*/
private boolean canRedispatch(Order order) {
// 业务判断逻辑
// 例如:检查重派次数、订单类型、订单创建时间等
// 简单示例:检查是否已经重派过多次
// 实际项目中需要根据订单表中的重派次数字段来判断
return true; // 简化实现,实际使用时需要完善判断逻辑
}
/**
* 重新派单
*
* @param order 订单对象
*/
private void redispatchOrder(Order order) {
try {
// 重新派单逻辑
order.setJsonStatus(1); // 重新设为派单状态
order.setUpdateTime(new Date());
// 如果有重派次数字段,需要增加重派次数
// order.setRedispatchCount(order.getRedispatchCount() + 1);
// 更新订单
if (orderService != null) {
// orderService.updateOrder(order);
log.info("订单{}重新派单操作完成", order.getOrderId());
}
} catch (Exception e) {
log.error("重新派单失败,订单号:{}", order.getOrderId(), e);
}
}
/**
* 标记订单为超时
*
* @param order 订单对象
*/
private void markOrderAsTimeout(Order order) {
try {
// 标记为超时状态
order.setJsonStatus(99); // 假设99表示超时状态
order.setUpdateTime(new Date());
// 更新订单
if (orderService != null) {
// orderService.updateOrder(order);
log.info("订单{}标记为超时状态", order.getOrderId());
}
} catch (Exception e) {
log.error("标记订单超时失败,订单号:{}", order.getOrderId(), e);
}
}
/**
* 记录订单处理日志
*
* @param order 订单对象
* @param operation 操作描述
*/
private void recordOrderProcessLog(Order order, String operation) {
try {
// 使用OrderUtil记录日志
// 注意这里需要根据实际的OrderUtil类路径进行调整
// 如果OrderUtil不存在可以注释掉这部分代码或者实现其他日志记录方式
log.info("订单{}{}处理日志记录", order.getOrderId(), operation);
// 临时注释掉OrderUtil调用避免编译错误
// com.ruoyi.system.controllerUtil.OrderUtil orderUtil = SpringUtils.getBean(com.ruoyi.system.controllerUtil.OrderUtil.class);
// orderUtil.SaveOrderLog(order);
} catch (Exception e) {
log.error("记录订单处理日志失败,订单号:{}", order.getOrderId(), e);
}
}
/**
* 处理服务中超时订单
*
* @param timeoutThreshold 超时时间阈值
*/
private void processServiceTimeoutOrders(LocalDateTime timeoutThreshold) {
try {
log.debug("处理服务中超时订单,超时阈值:{}", timeoutThreshold);
// 查询服务中超时的订单
// 这里需要根据实际的数据库结构和Service方法来实现
// 示例处理逻辑:
// 1. 查询服务状态且创建时间早于阈值的订单
// 2. 发送提醒通知
// 3. 或者自动标记为异常状态
log.debug("服务中超时订单处理完成");
} catch (Exception e) {
log.error("处理服务中超时订单失败", e);
}
}
/**
* 处理待支付超时订单
*
* @param timeoutThreshold 超时时间阈值
*/
private void processPaymentTimeoutOrders(LocalDateTime timeoutThreshold) {
try {
log.debug("处理待支付超时订单,超时阈值:{}", timeoutThreshold);
// 查询待支付超时的订单
// 示例处理逻辑:
// 1. 查询待支付状态且创建时间早于阈值的订单
// 2. 自动取消订单
// 3. 释放相关资源
log.debug("待支付超时订单处理完成");
} catch (Exception e) {
log.error("处理待支付超时订单失败", e);
}
}
/**
* 处理异常状态订单
*
* @param abnormalThreshold 异常时间阈值
*/
private void processAbnormalStatusOrders(LocalDateTime abnormalThreshold) {
try {
log.debug("处理异常状态订单,异常阈值:{}", abnormalThreshold);
// 查询异常状态的订单
// 示例处理逻辑:
// 1. 查询长时间处于中间状态的订单
// 2. 发送告警通知
// 3. 人工介入处理
log.debug("异常状态订单处理完成");
} catch (Exception e) {
log.error("处理异常状态订单失败", e);
}
}
/**
* 清理过期订单日志
*/
private void cleanupExpiredOrderLogs() {
try {
log.info("开始清理过期订单日志");
// 删除30天前的日志
LocalDateTime expireTime = LocalDateTime.now().minusDays(30);
Date expireDate = Date.from(expireTime.atZone(ZoneId.systemDefault()).toInstant());
// 执行清理
// 这里需要调用相应的日志服务方法
// int deletedCount = orderLogService.deleteExpiredLogs(expireDate);
// log.info("清理过期订单日志完成,删除{}条记录", deletedCount);
log.info("清理过期订单日志完成");
} catch (Exception e) {
log.error("清理过期订单日志失败", e);
}
}
/**
* 清理临时文件
*/
private void cleanupTempFiles() {
try {
log.info("开始清理临时文件");
// 清理临时目录中的过期文件
// 具体实现根据项目需求
String tempDir = System.getProperty("java.io.tmpdir");
log.debug("临时文件目录:{}", tempDir);
// 这里可以添加具体的文件清理逻辑
log.info("清理临时文件完成");
} catch (Exception e) {
log.error("清理临时文件失败", e);
}
}
/**
* 清理任务统计数据
*/
private void cleanupTaskStatistics() {
try {
// 只保留最近7天的统计数据
LocalDateTime expireTime = LocalDateTime.now().minusDays(7);
taskStats.entrySet().removeIf(entry -> {
TaskStatistics stats = entry.getValue();
return stats.getLastExecuteTime() != null && stats.getLastExecuteTime().isBefore(expireTime);
});
log.info("清理任务统计数据完成");
} catch (Exception e) {
log.error("清理任务统计数据失败", e);
}
}
/**
* 检查数据库连接
*/
private void checkDatabaseConnection() {
try {
// 执行简单查询检查数据库连接
if (orderService != null) {
// 这里可以执行一个简单的查询来验证数据库连接
// orderService.selectOrderById(1L);
log.debug("数据库连接检查正常");
} else {
log.warn("订单服务未初始化,跳过数据库连接检查");
}
} catch (Exception e) {
log.error("数据库连接检查失败", e);
}
}
/**
* 检查线程池状态
*/
private void checkThreadPoolStatus() {
if (executorService != null) {
int activeCount = executorService.getActiveCount();
int queueSize = executorService.getQueue().size();
int poolSize = executorService.getPoolSize();
long completedTaskCount = executorService.getCompletedTaskCount();
log.debug("线程池状态 - 活跃线程数: {}, 池大小: {}, 队列大小: {}, 已完成任务: {}",
activeCount, poolSize, queueSize, completedTaskCount);
// 如果队列积压过多,记录警告
if (queueSize > 80) {
log.warn("线程池队列积压过多,当前队列大小: {}", queueSize);
}
// 如果活跃线程数过多,记录警告
if (activeCount > 8) {
log.warn("线程池活跃线程数过多,当前活跃线程数: {}", activeCount);
}
}
}
/**
* 检查系统资源
*/
private void checkSystemResources() {
try {
// 检查JVM内存使用情况
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
long maxMemory = runtime.maxMemory();
double memoryUsagePercent = (double) usedMemory / totalMemory * 100;
double maxMemoryUsagePercent = (double) usedMemory / maxMemory * 100;
log.debug("内存使用情况 - 总内存: {}MB, 已用: {}MB, 最大内存: {}MB, 当前使用率: {:.2f}%, 最大使用率: {:.2f}%",
totalMemory / 1024 / 1024, usedMemory / 1024 / 1024, maxMemory / 1024 / 1024,
memoryUsagePercent, maxMemoryUsagePercent);
// 内存使用率过高时记录警告
if (maxMemoryUsagePercent > 80) {
log.warn("内存使用率过高: {:.2f}%", maxMemoryUsagePercent);
}
// 检查垃圾回收情况
System.gc(); // 建议进行垃圾回收(实际生产环境中谨慎使用)
} catch (Exception e) {
log.error("检查系统资源失败", e);
}
}
/**
* 更新任务统计信息
*
* @param taskName 任务名称
* @param success 是否成功
* @param duration 执行时长(毫秒)
*/
private void updateTaskStatistics(String taskName, boolean success, long duration) {
taskStats.compute(taskName, (key, stats) -> {
if (stats == null) {
stats = new TaskStatistics(taskName);
}
stats.updateStats(success, duration);
return stats;
});
}
// ========================= 公共方法 =========================
/**
* 手动触发派单超时处理
*
* 使用说明:
* - 可在特殊情况下手动调用此方法
* - 例如系统维护后需要立即检查超时订单
*
* @return 处理结果描述
*/
public String manualDispatchTimeoutCheck() {
try {
log.info("手动触发派单超时处理");
handleDispatchTimeout();
return "派单超时处理执行成功";
} catch (Exception e) {
log.error("手动派单超时处理失败", e);
return "派单超时处理执行失败: " + e.getMessage();
}
}
/**
* 手动触发订单状态检查
*
* @return 处理结果描述
*/
public String manualOrderStatusCheck() {
try {
log.info("手动触发订单状态检查");
checkOrderStatusTimeout();
return "订单状态检查执行成功";
} catch (Exception e) {
log.error("手动订单状态检查失败", e);
return "订单状态检查执行失败: " + e.getMessage();
}
}
/**
* 手动触发系统数据清理
*
* @return 处理结果描述
*/
public String manualDataCleanup() {
try {
log.info("手动触发系统数据清理");
cleanupSystemData();
return "系统数据清理执行成功";
} catch (Exception e) {
log.error("手动系统数据清理失败", e);
return "系统数据清理执行失败: " + e.getMessage();
}
}
/**
* 手动触发健康检查
*
* @return 处理结果描述
*/
public String manualHealthCheck() {
try {
log.info("手动触发健康检查");
healthCheck();
return "健康检查执行成功";
} catch (Exception e) {
log.error("手动健康检查失败", e);
return "健康检查执行失败: " + e.getMessage();
}
}
/**
* 手动触发师傅暂停状态自动恢复
*
* 使用说明:
* - 可在特殊情况下手动调用此方法
* - 例如系统维护后需要立即检查并恢复过期的师傅暂停状态
*
* @return 处理结果描述
*/
public String manualWorkerStatusResume() {
try {
log.info("手动触发师傅暂停状态自动恢复");
autoResumeWorkerOrderStatus();
return "师傅暂停状态自动恢复执行成功";
} catch (Exception e) {
log.error("手动师傅暂停状态自动恢复失败", e);
return "师傅暂停状态自动恢复执行失败: " + e.getMessage();
}
}
/**
* 获取任务执行统计信息
*
* 使用说明:
* - 可用于监控各个定时任务的执行情况
* - 包含执行次数、成功率、平均执行时间等信息
*
* @return 任务统计信息
*/
public Map<String, TaskStatistics> getTaskStatistics() {
return new HashMap<>(taskStats);
}
/**
* 获取格式化的任务统计报告
*
* @return 格式化的统计报告
*/
public String getTaskStatisticsReport() {
StringBuilder report = new StringBuilder();
report.append("=== 定时任务执行统计报告 ===\n");
report.append(String.format("报告生成时间: %s\n",
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
report.append(String.format("线程池状态: 活跃线程=%d, 队列大小=%d\n",
executorService.getActiveCount(), executorService.getQueue().size()));
report.append("\n");
if (taskStats.isEmpty()) {
report.append("暂无任务执行记录\n");
} else {
for (TaskStatistics stats : taskStats.values()) {
report.append(stats.toString()).append("\n");
}
}
return report.toString();
}
/**
* 获取线程池状态信息
*
* @return 线程池状态信息
*/
public Map<String, Object> getThreadPoolStatus() {
Map<String, Object> status = new HashMap<>();
if (executorService != null) {
status.put("activeCount", executorService.getActiveCount());
status.put("poolSize", executorService.getPoolSize());
status.put("queueSize", executorService.getQueue().size());
status.put("completedTaskCount", executorService.getCompletedTaskCount());
status.put("isShutdown", executorService.isShutdown());
status.put("isTerminated", executorService.isTerminated());
}
return status;
}
/**
* 停止所有定时任务
*
* 使用说明:
* - 用于系统维护时临时停止定时任务
* - 注意:这只是停止线程池,@Scheduled注解的任务仍会执行
*/
public void stopAllTasks() {
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdown();
try {
// 等待60秒让任务完成
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
log.error("线程池无法正常关闭");
}
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
log.info("定时任务线程池已停止");
}
}
/**
* 重启任务线程池
*/
public void restartTaskPool() {
stopAllTasks();
initThreadPool();
log.info("定时任务线程池已重启");
}
/**
* 判断师傅是否应该恢复接单状态
*
* @param worker 师傅用户对象
* @param currentTime 当前时间
* @return true-应该恢复false-不应该恢复
*/
private boolean shouldResumeWorker(Users worker, Date currentTime) {
try {
// 检查必要字段
if (worker.getProhibitTime() == null) {
log.debug("师傅{}(ID:{})的prohibit_time为空跳过处理",
worker.getName(), worker.getId());
return false;
}
// 判断暂停时间是否已过期prohibit_time < 当前时间)
boolean isExpired = worker.getProhibitTime().before(currentTime);
if (isExpired) {
log.debug("师傅{}(ID:{})的暂停时间已过期prohibit_time: {}, 当前时间: {}",
worker.getName(), worker.getId(),
worker.getProhibitTime(), currentTime);
} else {
log.debug("师傅{}(ID:{})的暂停时间未过期prohibit_time: {}, 当前时间: {}",
worker.getName(), worker.getId(),
worker.getProhibitTime(), currentTime);
}
return isExpired;
} catch (Exception e) {
log.error("判断师傅{}(ID:{})是否应该恢复接单状态时发生异常",
worker.getName(), worker.getId(), e);
return false;
}
}
/**
* 恢复师傅接单状态
*
* @param worker 师傅用户对象
*/
private void resumeWorkerOrderStatus(Users worker) {
try {
// 创建更新对象,只更新必要字段
Users updateUser = new Users();
updateUser.setId(worker.getId());
updateUser.setIsStop(0); // 恢复接单状态
updateUser.setProhibitTimeNum(0); // 清空暂停时长
// 注意这里不清空prohibit_time保留历史记录
// 执行更新
int updateResult = usersService.updateUsers(updateUser);
if (updateResult > 0) {
log.info("师傅{}(ID:{})接单状态恢复成功", worker.getName(), worker.getId());
} else {
log.warn("师傅{}(ID:{})接单状态恢复失败,更新结果: {}",
worker.getName(), worker.getId(), updateResult);
}
} catch (Exception e) {
log.error("恢复师傅{}(ID:{})接单状态时发生异常",
worker.getName(), worker.getId(), e);
throw e; // 重新抛出异常,让上层处理
}
}
// ========================= 内部类 =========================
/**
* 任务统计信息类
*/
public static class TaskStatistics {
private final String taskName;
private int totalCount = 0;
private int successCount = 0;
private long totalDuration = 0;
private LocalDateTime lastExecuteTime;
private LocalDateTime firstExecuteTime;
private String lastErrorMessage;
public TaskStatistics(String taskName) {
this.taskName = taskName;
this.firstExecuteTime = LocalDateTime.now();
}
public void updateStats(boolean success, long duration) {
totalCount++;
if (success) {
successCount++;
lastErrorMessage = null;
}
totalDuration += duration;
lastExecuteTime = LocalDateTime.now();
}
public void setLastErrorMessage(String errorMessage) {
this.lastErrorMessage = errorMessage;
}
// Getter methods
public String getTaskName() {
return taskName;
}
public int getTotalCount() {
return totalCount;
}
public int getSuccessCount() {
return successCount;
}
public int getFailureCount() {
return totalCount - successCount;
}
public LocalDateTime getLastExecuteTime() {
return lastExecuteTime;
}
public LocalDateTime getFirstExecuteTime() {
return firstExecuteTime;
}
public String getLastErrorMessage() {
return lastErrorMessage;
}
public double getSuccessRate() {
return totalCount == 0 ? 0 : (double) successCount / totalCount * 100;
}
public long getAverageDuration() {
return totalCount == 0 ? 0 : totalDuration / totalCount;
}
public long getTotalDuration() {
return totalDuration;
}
@Override
public String toString() {
return String.format(
"任务名称: %-20s | 执行次数: %-6d | 成功次数: %-6d | 失败次数: %-6d | 成功率: %6.2f%% | 平均耗时: %-6dms | 总耗时: %-8dms | 最后执行: %s",
taskName, totalCount, successCount, getFailureCount(), getSuccessRate(), getAverageDuration(), totalDuration,
lastExecuteTime != null ? lastExecuteTime.format(DateTimeFormatter.ofPattern("MM-dd HH:mm:ss")) : "未执行"
);
}
}
}