diff --git a/app/admin/model/SystemTimerConfig.php b/app/admin/model/SystemTimerConfig.php new file mode 100644 index 0000000..c32d62a --- /dev/null +++ b/app/admin/model/SystemTimerConfig.php @@ -0,0 +1,12 @@ +writeln('站点域名:' . $host); $site_host = parse_url($host, PHP_URL_HOST); + // 同步配置到数据库 + TimerService::syncConfigToDatabase(); + // 设置配置的任务 $timer_service = new TimerService(); $request_list = $timer_service->generateAllRequestList(); diff --git a/extend/base/common/service/HostServiceBase.php b/extend/base/common/service/HostServiceBase.php index 4dc9ad9..1d5c807 100644 --- a/extend/base/common/service/HostServiceBase.php +++ b/extend/base/common/service/HostServiceBase.php @@ -75,6 +75,23 @@ class HostServiceBase } $host->save($data); + + // 主节点自动选举:若无在线主节点,当前节点自动成为主节点 + $master = SystemHost::where('is_master', 1)->where('status', 1)->find(); + if (empty($master)) { + $host->is_master = 1; + $host->save(); + Log::info("节点 [{$nodeId}] 自动当选为主节点。"); + } + + // 过期节点检测:超过90秒无心跳的节点标记为离线 + $staleTime = date('Y-m-d H:i:s', time() - 90); + $staleCount = SystemHost::where('status', 1) + ->where('last_heartbeat_at', '<', $staleTime) + ->update(['status' => 0]); + if ($staleCount > 0) { + Log::info("已将 {$staleCount} 个过期节点标记为离线。"); + } } catch (\Exception $e) { throw $e; Log::error("节点 [{$nodeId}] 心跳更新失败: " . $e->getMessage()); @@ -110,4 +127,38 @@ class HostServiceBase 'php_version' => PHP_VERSION, ]; } + + /** + * 获取当前主节点的node_id. + * + * @return string|null 返回主节点ID,无主节点时返回null + */ + public static function getMasterNode(): ?string + { + $master = SystemHost::where('is_master', 1)->where('status', 1)->find(); + return $master ? $master->node_id : null; + } + + /** + * 手动切换主节点. + * + * @param string $nodeId 目标节点的node_id + * @return bool 切换成功返回true,节点不存在返回false + */ + public static function setMasterNode(string $nodeId): bool + { + // 先清除所有主节点标记 + SystemHost::where('is_master', 1)->update(['is_master' => 0]); + + // 设置新主节点 + $host = SystemHost::where('node_id', $nodeId)->find(); + if ($host) { + $host->is_master = 1; + $host->save(); + + return true; + } + + return false; + } } diff --git a/extend/base/common/service/TimerServiceBase.php b/extend/base/common/service/TimerServiceBase.php index e553b27..1bc7a23 100644 --- a/extend/base/common/service/TimerServiceBase.php +++ b/extend/base/common/service/TimerServiceBase.php @@ -2,12 +2,69 @@ namespace base\common\service; +use app\admin\model\SystemTimerConfig; use app\common\model\VirtualModel; class TimerServiceBase { protected $taskList = null; + /** + * 将配置文件中的定时任务同步到数据库. + * 仅新增不存在的记录,标记孤立记录,不覆盖 run_type/status. + */ + public static function syncConfigToDatabase(): void + { + $config_list = include app_file_path('common/command/timer/config.php'); + + // 提取唯一的任务名称(并发扩展会产生同名配置,需去重) + $config_names = []; + foreach ($config_list as $config_item) { + $item = static::initConfigItem($config_item); + $name = (string) $item['name']; + if ($name !== '' && !in_array($name, $config_names, true)) { + $config_names[] = $name; + } + } + + // 查询已有记录 + $existing = SystemTimerConfig::column('task_name'); + $now = time(); + + // 新增:配置中有但数据库中没有的 + $new_names = array_diff($config_names, $existing); + foreach ($new_names as $name) { + SystemTimerConfig::create([ + 'task_name' => $name, + 'run_type' => 'auto', + 'status' => 1, + 'is_synced' => 1, + 'create_time' => $now, + 'update_time' => $now, + ]); + } + + // 已存在且仍在配置中:仅更新 is_synced=1,不动 run_type/status + $still_exists = array_intersect($existing, $config_names); + foreach ($still_exists as $name) { + SystemTimerConfig::where('task_name', $name) + ->update([ + 'is_synced' => 1, + 'update_time' => $now, + ]); + } + + // 已存在但不在配置中:标记为孤立 + $orphaned = array_diff($existing, $config_names); + foreach ($orphaned as $name) { + SystemTimerConfig::where('task_name', $name) + ->update([ + 'is_synced' => 0, + 'update_time' => $now, + ]); + } + } + public static function generateAllTaskInstanceList() { $config_list = include app_file_path('common/command/timer/config.php');