Kernel.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <?php
  2. namespace App\Console;
  3. use Illuminate\Console\Scheduling\Schedule;
  4. use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
  5. use Illuminate\Support\Facades\Log;
  6. class Kernel extends ConsoleKernel
  7. {
  8. /**
  9. * The Artisan commands provided by your application.
  10. *
  11. * @var array
  12. */
  13. protected $commands = [
  14. //
  15. ];
  16. /**
  17. * Define the application's command schedule.
  18. *
  19. * @param \Illuminate\Console\Scheduling\Schedule $schedule
  20. * @return void
  21. */
  22. protected function schedule(Schedule $schedule)
  23. {
  24. // 获取环境模式,默认为 'test'
  25. $appMode = env('MY_APP_MODE', 'test');
  26. if ($appMode === 'production') {
  27. $this->production($schedule);
  28. }else{
  29. $this->test($schedule);
  30. }
  31. }
  32. public function production($schedule)
  33. {
  34. // 1. Windows 特有任务块
  35. if (str_contains(PHP_OS, 'WIN')) {
  36. // Redis 重启:最容易报错(10054),必须独立捕获
  37. // try {
  38. // $this->redisTask($schedule);
  39. // } catch (\Throwable $e) {
  40. // Log::channel('u8_daily')->error("【系统中断】redisTask 注册异常: " . $e->getMessage());
  41. // }
  42. // 队列守卫:非常重要,独立捕获防止受 Redis 影响
  43. try {
  44. $this->queueTask($schedule);
  45. } catch (\Throwable $e) {
  46. Log::channel('u8_daily')->error("【系统中断】queueTask 注册异常: " . $e->getMessage());
  47. }
  48. }
  49. // 2. 核心业务同步块
  50. try {
  51. $this->businessSettle($schedule);
  52. } catch (\Throwable $e) {
  53. Log::channel('u8_daily')->error("【系统中断】businessSettle 注册异常: " . $e->getMessage());
  54. }
  55. }
  56. public function redisTask($schedule){
  57. $schedule->call(function () {
  58. // 找到你小皮面板里 redis-cli.exe 的绝对路径
  59. $redisCli = 'D:\phpstudy_pro\Extensions\redis3.0.504\redis-cli.exe';
  60. Log::channel('u8_daily')->info("【定时维护】正在通过 SHUTDOWN 命令重启 Redis...");
  61. // 执行关闭并保存数据
  62. // 如果你设置了密码,需要加上 -a your_password
  63. exec("\"$redisCli\" SHUTDOWN SAVE", $output, $returnVar);
  64. if ($returnVar === 0) {
  65. Log::channel('u8_daily')->info("【调试】Redis 重启成功");
  66. } else {
  67. Log::channel('u8_daily')->error("【调试】失败", ['out' => $output]);
  68. }
  69. })
  70. ->name('redis-restart-task')
  71. ->between('6:00', '6:10')
  72. ->everyMinute()
  73. ->withoutOverlapping(20);
  74. }
  75. public function queueTask($schedule){
  76. $schedule->call(function () {
  77. $queueName = "sync_wms_order";
  78. $phpPath = "D:/phpstudy_pro/Extensions/php/php7.4.3nts/php.exe";
  79. $artisan = base_path('artisan');
  80. // 修改点:增加一个 AND 过滤,排除掉包含 "schedule:run" 的进程
  81. // 并且确保匹配的是 "queue:work"
  82. $checkCmd = 'wmic process where "name=\'php.exe\' and commandline like \'%queue:work%--queue=' . $queueName . '%\' and not commandline like \'%schedule:run%\'" get processid /format:list';
  83. $output = shell_exec($checkCmd);
  84. if (empty(trim($output)) || str_contains($output, 'No Instance')) {
  85. // 使用绝对路径启动
  86. $runCmd = "start /B \"\" \"$phpPath\" \"$artisan\" queue:work redis --queue=$queueName --memory=512";
  87. pclose(popen($runCmd, "r"));
  88. Log::channel('queue_daily')->info("单进程守卫:未检测到队列[$queueName],已尝试拉起。");
  89. } else {
  90. Log::channel('queue_daily')->info('单进程守卫:队列正在运行中,无需拉起。', ['pid' => trim($output)]);
  91. }
  92. })
  93. ->name('queue-task')
  94. ->everyMinute()
  95. ->between('7:00', '23:59');
  96. }
  97. public function businessSettle($schedule){
  98. //先同步物料 再同步单据
  99. $schedule->command('command:u8_settle_inventory')
  100. ->between('7:00', '23:59') // 关键点:限制执行时间段 只在这段时间内运行
  101. ->everyMinute()
  102. ->withoutOverlapping(10) // 锁长时间一点,给第一次同步留足时间
  103. ->onSuccess(function () {
  104. // 2. 只有当 U8SettleInventory 的 handle() 返回 0 时,才执行单据同步
  105. \Illuminate\Support\Facades\Artisan::call('command:u8_settle');
  106. })
  107. ->onFailure(function(){
  108. Log::channel('u8_daily')->warning('物料同步失败,单据同步已挂起。');
  109. });
  110. }
  111. public function test($schedule){
  112. //开启队列
  113. if (str_contains(PHP_OS, 'WIN')) {
  114. $schedule->call(function () {
  115. $queueName = "sync_wms_order";
  116. $phpPath = "D:/phpstudy_pro/Extensions/php/php7.4.3nts/php.exe";
  117. $artisan = base_path('artisan');
  118. // 修改点:增加一个 AND 过滤,排除掉包含 "schedule:run" 的进程
  119. // 并且确保匹配的是 "queue:work"
  120. $checkCmd = 'wmic process where "name=\'php.exe\' and commandline like \'%queue:work%--queue=' . $queueName . '%\' and not commandline like \'%schedule:run%\'" get processid /format:list';
  121. $output = shell_exec($checkCmd);
  122. if (empty(trim($output)) || str_contains($output, 'No Instance')) {
  123. // 使用绝对路径启动
  124. $runCmd = "start /B \"\" \"$phpPath\" \"$artisan\" queue:work redis --queue=$queueName --memory=512";
  125. pclose(popen($runCmd, "r"));
  126. Log::channel('queue_daily')->info("单进程守卫:未检测到队列[$queueName],已尝试拉起。");
  127. } else {
  128. Log::channel('queue_daily')->info('单进程守卫:队列正在运行中,无需拉起。', ['pid' => trim($output)]);
  129. }
  130. })->everyMinute();
  131. }
  132. $schedule->call(function () {
  133. Log::channel('u8_daily')->info('测试环境:正在跳过物料同步,直接执行单据同步...');
  134. // 直接调用单据同步命令
  135. \Illuminate\Support\Facades\Artisan::call('command:u8_settle');
  136. })->everyMinute();
  137. }
  138. /**
  139. * Register the commands for the application.
  140. *
  141. * @return void
  142. */
  143. protected function commands()
  144. {
  145. $this->load(__DIR__.'/Commands');
  146. require base_path('routes/console.php');
  147. }
  148. }