Selaa lähdekoodia

feat: 新增CronJobAfterEvent和CronJobBeforeEvent

runphp 1 viikko sitten
vanhempi
sitoutus
995ca3f935

+ 0 - 38
src/Cron/SystemCron.php

@@ -1,38 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace SixShop\System\Cron;
-
-use SixShop\Core\Attribute\Cron;
-use think\Cache;
-use Workerman\Crontab\Crontab;
-use Workerman\Worker;
-
-class SystemCron
-{
-    public function __construct(private Cache $cache, private bool $isStart = false)
-    {
-    }
-
-    #[Cron('1 * * * * *', 'system.cron')]
-    public function onWorkerStart(): void
-    {
-        $crontabList = [];
-        foreach (Crontab::getAll() as $item) {
-            /* @var Crontab $item */
-            $item = [
-                'rule' => $item->getRule(),
-                'name' => $item->getName(),
-                'id' => $item->getId(),
-                'time' => date('Y-m-d H:i:s'),
-            ];
-            $crontabList[] = $item;
-            if (!$this->isStart) {
-                Worker::safeEcho(sprintf('[%s] [%s] [%s] [%s]', $item['time'], $item['id'], $item['name'], $item['rule']) . "\n");
-            }
-        }
-        $this->cache->set('crontab_list', $crontabList);
-        $this->isStart = true;
-    }
-}

+ 25 - 0
src/Event/CronJobAfterEvent.php

@@ -0,0 +1,25 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SixShop\System\Event;
+
+use Throwable;
+
+class CronJobAfterEvent
+{
+    public function __construct(
+        public readonly string $name,
+        public readonly string $rule,
+        public readonly string|object $target,
+        public readonly mixed $result = null,
+        public readonly ?float $duration = null,
+        public readonly ?Throwable $throwable = null,
+    ) {
+    }
+
+    public function isSuccessful(): bool
+    {
+        return $this->throwable === null;
+    }
+}

+ 15 - 0
src/Event/CronJobBeforeEvent.php

@@ -0,0 +1,15 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SixShop\System\Event;
+
+class CronJobBeforeEvent
+{
+    public function __construct(
+        public readonly string $name,
+        public readonly string $rule,
+        public readonly string|object $target,
+    ) {
+    }
+}

+ 2 - 8
src/Extension.php

@@ -6,8 +6,8 @@ namespace SixShop\System;
 
 use SixShop\Core\ExtensionAbstract;
 use SixShop\Core\Service\AutoloadService;
-use SixShop\System\Cron\SystemCron;
 use SixShop\System\Enum\ExtensionStatusEnum;
+use SixShop\System\Hook\CronJobHook;
 use SixShop\System\Hook\DebugHook;
 use SixShop\System\Hook\ExtensionStatusHook;
 use SixShop\System\Hook\GatheringCrontabEventHook;
@@ -24,6 +24,7 @@ class Extension extends ExtensionAbstract
     public function getHooks(): array
     {
         $hooks = [
+            CronJobHook::class,
             ExtensionStatusHook::class,
             GatheringCrontabEventHook::class
         ];
@@ -36,13 +37,6 @@ class Extension extends ExtensionAbstract
         return $hooks;
     }
 
-    public function getCronJobs(): array
-    {
-        return [
-            SystemCron::class
-        ];
-    }
-
     public function boot(): void
     {
         if ($this->isBooted) {

+ 37 - 0
src/Hook/CronJobHook.php

@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SixShop\System\Hook;
+
+use SixShop\Core\Attribute\Hook;
+use SixShop\System\Event\CronJobAfterEvent;
+use think\Cache;
+
+class CronJobHook
+{
+    private const CACHE_KEY = 'crontab_list';
+
+    public function __construct(private Cache $cache)
+    {
+        // 清空上次运行的缓存数据
+        $this->cache->delete(self::CACHE_KEY);
+    }
+
+    /**
+     * 每次任务执行后更新最后执行时间和运行状态
+     */
+    #[Hook(CronJobAfterEvent::class)]
+    public function onJobExecuted(CronJobAfterEvent $event): void
+    {
+        $crontabList = $this->cache->get(self::CACHE_KEY, []);
+        $crontabList[$event->name] = [
+            'name' => $event->name,
+            'rule' => $event->rule,
+            'last_time' => date('Y-m-d H:i:s'),
+            'duration' => $event->duration,
+            'success' => $event->isSuccessful(),
+        ];
+        $this->cache->set(self::CACHE_KEY, $crontabList);
+    }
+}

+ 41 - 2
src/Hook/GatheringCrontabEventHook.php

@@ -8,6 +8,8 @@ use ReflectionClass;
 use ReflectionMethod;
 use SixShop\Core\Attribute\Cron;
 use SixShop\Core\Attribute\Hook;
+use SixShop\System\Event\CronJobAfterEvent;
+use SixShop\System\Event\CronJobBeforeEvent;
 use SixShop\System\Event\CrontabWorkerStartEvent;
 use SixShop\System\Event\GetCronJobsEvent;
 use SixShop\System\ExtensionManager;
@@ -23,6 +25,38 @@ class GatheringCrontabEventHook
     {
     }
 
+    /**
+     * 包装回调函数,在任务执行前后触发事件
+     */
+    private function wrapCallback(callable $callback, string $name, string $rule, object $target): callable
+    {
+        return function () use ($callback, $name, $rule, $target) {
+            $this->app->event->trigger(new CronJobBeforeEvent($name, $rule, $target));
+
+            $startTime = microtime(true);
+            $throwable = null;
+            $result = null;
+
+            try {
+                $result = $callback();
+            } catch (\Throwable $e) {
+                $throwable = $e;
+                Log::error("Cron job [{$name}] failed: " . $e->getMessage());
+            }
+
+            $duration = microtime(true) - $startTime;
+
+            $this->app->event->trigger(new CronJobAfterEvent(
+                name: $name,
+                rule: $rule,
+                target: $target,
+                result: $result,
+                duration: round($duration, 4),
+                throwable: $throwable,
+            ));
+        };
+    }
+
     /**
      * @throws \ReflectionException
      */
@@ -48,7 +82,10 @@ class GatheringCrontabEventHook
             foreach ($objectAttts as $attribute) {
                 $cronInstance = $attribute->newInstance();
                 if (is_callable($cronJob)) {
-                    new Crontab($cronInstance->rule, $cronJob, $cronInstance->name ?: $cronJobClass);
+                    $name = $cronInstance->name ?: $cronJobClass;
+                    $callback = $this->wrapCallback($cronJob, $name, $cronInstance->rule, $cronJob);
+                    new Crontab($cronInstance->rule, $callback, $name);
+                    Log::info("Cron job registered [{$name}] rule={$cronInstance->rule}");
                 } else {
                     Log::warning("Cron job {$cronJobClass} is not callable");
                 }
@@ -58,7 +95,9 @@ class GatheringCrontabEventHook
                 foreach ($attributes as $attribute) {
                     $cronInstance = $attribute->newInstance();
                     $name = $cronInstance->name ?: $cronJobClass . '@' . $method->getName();
-                    new Crontab($cronInstance->rule, [$cronJob, $method->getName()], $name);
+                    $callback = $this->wrapCallback([$cronJob, $method->getName()], $name, $cronInstance->rule, $cronJob);
+                    new Crontab($cronInstance->rule, $callback, $name);
+                    Log::info("Cron job registered [{$name}] rule={$cronInstance->rule}");
                 }
             }
         }