isConfigured()) { $this->error('LLM 未配置:请先在后台「AI 设置」或 .env 配置 LLM_BASE_URL / LLM_API_KEY'); return self::FAILURE; } $model = $covers->model($this->option('model') ?: null); $style = $this->option('style') ?: null; $query = Post::query()->published()->orderedForList(); if ($ids = trim((string) $this->option('post'))) { $query->whereIn('id', array_filter(array_map('intval', explode(',', $ids)))); } $todo = $query->get() ->filter(fn (Post $post) => $this->option('all') || $post->coverOrFirstImageUrl() === null) ->values(); if ($limit = (int) $this->option('limit')) { $todo = $todo->take($limit)->values(); } if ($todo->isEmpty()) { $this->info('没有需要生成封面的文章。'); return self::SUCCESS; } $this->info("待处理 {$todo->count()} 篇,模型 {$model}"); if ($this->option('dry-run')) { foreach ($todo as $post) { $this->line("[{$post->id}] {$post->title}"); } return self::SUCCESS; } $ok = 0; $fail = 0; foreach ($todo as $post) { try { $covers->generateFor($post, $model, $style); $ok++; $this->info("✔ [{$post->id}] {$post->title}"); } catch (\Throwable $e) { $fail++; $this->warn("✘ [{$post->id}] {$post->title}:{$e->getMessage()}"); Log::warning('covers:generate 失败', ['post_id' => $post->id, 'error' => $e->getMessage()]); } } $this->info("完成:成功 {$ok},失败 {$fail}"); return $ok === 0 && $fail > 0 ? self::FAILURE : self::SUCCESS; } }