1155 lines
39 KiB
Java
1155 lines
39 KiB
Java
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.IOrderService;
|
||
import com.ruoyi.system.service.IUsersService;
|
||
import com.ruoyi.system.service.IWorkerMoneyLogService;
|
||
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<>();
|
||
|
||
/**
|
||
* 初始化方法,在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();
|
||
autoResumeWorkerOrderStatus(); // 添加师傅暂停状态自动恢复任务
|
||
log.info("定时任务自动执行完成");
|
||
}
|
||
|
||
// ========================= 定时任务方法 =========================
|
||
|
||
|
||
|
||
|
||
/**
|
||
* 师傅收益7天定时冻结的解冻
|
||
* 每10分钟执行一次
|
||
*/
|
||
@Scheduled(fixedRate = 10 * 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);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* 订单派单超时处理任务
|
||
* 每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);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 系统数据清理任务
|
||
* 每天凌晨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 = 3000) // 每3秒执行一次
|
||
public void testScheduledTask() {
|
||
log.info("测试定时任务执行中... 当前时间: {}", 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")) : "未执行"
|
||
);
|
||
}
|
||
}
|
||
}
|