production($schedule); }else{ $this->test($schedule); } } public function production($schedule) { // 1. Windows 特有任务块 if (str_contains(PHP_OS, 'WIN')) { // Redis 重启:最容易报错(10054),必须独立捕获 // try { // $this->redisTask($schedule); // } catch (\Throwable $e) { // Log::channel('u8_daily')->error("【系统中断】redisTask 注册异常: " . $e->getMessage()); // } // 队列守卫:非常重要,独立捕获防止受 Redis 影响 try { $this->queueTask($schedule); } catch (\Throwable $e) { Log::channel('u8_daily')->error("【系统中断】queueTask 注册异常: " . $e->getMessage()); } } // 2. 核心业务同步块 try { $this->businessSettle($schedule); } catch (\Throwable $e) { Log::channel('u8_daily')->error("【系统中断】businessSettle 注册异常: " . $e->getMessage()); } } public function redisTask($schedule){ $schedule->call(function () { // 找到你小皮面板里 redis-cli.exe 的绝对路径 $redisCli = 'D:\phpstudy_pro\Extensions\redis3.0.504\redis-cli.exe'; Log::channel('u8_daily')->info("【定时维护】正在通过 SHUTDOWN 命令重启 Redis..."); // 执行关闭并保存数据 // 如果你设置了密码,需要加上 -a your_password exec("\"$redisCli\" SHUTDOWN SAVE", $output, $returnVar); if ($returnVar === 0) { Log::channel('u8_daily')->info("【调试】Redis 重启成功"); } else { Log::channel('u8_daily')->error("【调试】失败", ['out' => $output]); } }) ->name('redis-restart-task') ->between('6:00', '6:10') ->everyMinute() ->withoutOverlapping(20); } public function queueTask($schedule){ $schedule->call(function () { $queueName = "sync_wms_order"; $phpPath = "D:/phpstudy_pro/Extensions/php/php7.4.3nts/php.exe"; $artisan = base_path('artisan'); // 修改点:增加一个 AND 过滤,排除掉包含 "schedule:run" 的进程 // 并且确保匹配的是 "queue:work" $checkCmd = 'wmic process where "name=\'php.exe\' and commandline like \'%queue:work%--queue=' . $queueName . '%\' and not commandline like \'%schedule:run%\'" get processid /format:list'; $output = shell_exec($checkCmd); if (empty(trim($output)) || str_contains($output, 'No Instance')) { // 使用绝对路径启动 $runCmd = "start /B \"\" \"$phpPath\" \"$artisan\" queue:work redis --queue=$queueName --memory=512"; pclose(popen($runCmd, "r")); Log::channel('queue_daily')->info("单进程守卫:未检测到队列[$queueName],已尝试拉起。"); } else { Log::channel('queue_daily')->info('单进程守卫:队列正在运行中,无需拉起。', ['pid' => trim($output)]); } }) ->name('queue-task') ->everyMinute() ->between('7:00', '23:59'); } public function businessSettle($schedule){ //先同步物料 再同步单据 $schedule->command('command:u8_settle_inventory') ->between('7:00', '23:59') // 关键点:限制执行时间段 只在这段时间内运行 ->everyMinute() ->withoutOverlapping(10) // 锁长时间一点,给第一次同步留足时间 ->onSuccess(function () { // 2. 只有当 U8SettleInventory 的 handle() 返回 0 时,才执行单据同步 \Illuminate\Support\Facades\Artisan::call('command:u8_settle'); }) ->onFailure(function(){ Log::channel('u8_daily')->warning('物料同步失败,单据同步已挂起。'); }); } public function test($schedule){ //开启队列 if (str_contains(PHP_OS, 'WIN')) { $schedule->call(function () { $queueName = "sync_wms_order"; $phpPath = "D:/phpstudy_pro/Extensions/php/php7.4.3nts/php.exe"; $artisan = base_path('artisan'); // 修改点:增加一个 AND 过滤,排除掉包含 "schedule:run" 的进程 // 并且确保匹配的是 "queue:work" $checkCmd = 'wmic process where "name=\'php.exe\' and commandline like \'%queue:work%--queue=' . $queueName . '%\' and not commandline like \'%schedule:run%\'" get processid /format:list'; $output = shell_exec($checkCmd); if (empty(trim($output)) || str_contains($output, 'No Instance')) { // 使用绝对路径启动 $runCmd = "start /B \"\" \"$phpPath\" \"$artisan\" queue:work redis --queue=$queueName --memory=512"; pclose(popen($runCmd, "r")); Log::channel('queue_daily')->info("单进程守卫:未检测到队列[$queueName],已尝试拉起。"); } else { Log::channel('queue_daily')->info('单进程守卫:队列正在运行中,无需拉起。', ['pid' => trim($output)]); } })->everyMinute(); } $schedule->call(function () { Log::channel('u8_daily')->info('测试环境:正在跳过物料同步,直接执行单据同步...'); // 直接调用单据同步命令 \Illuminate\Support\Facades\Artisan::call('command:u8_settle'); })->everyMinute(); } /** * Register the commands for the application. * * @return void */ protected function commands() { $this->load(__DIR__.'/Commands'); require base_path('routes/console.php'); } }