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 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 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 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 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 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 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 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 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 getThreadPoolStatus() { Map 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")) : "未执行" ); } } }