| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- <?php
- namespace App\Console\Anime;
- use Illuminate\Console\Command;
- use Illuminate\Support\Facades\DB;
- use App\Services\AIGeneration\AIImageGenerationService;
- class AnimeCheckImgUrlCommand extends Command
- {
- /**
- * The name and signature of the console command.
- *
- * @var string
- */
- protected $signature = 'Anime:checkImgUrl';
- /**
- * The console command description.
- *
- * @var string
- */
- protected $description = '检查动漫对话角色和场景图片生成状态';
- protected $aiImageGenerationService;
- public function __construct(AIImageGenerationService $aiImageGenerationService)
- {
- parent::__construct();
- $this->aiImageGenerationService = $aiImageGenerationService;
- }
- /**
- * Execute the console command.
- *
- * @return int
- */
- public function handle()
- {
- dLog('generate')->info('====================开始检查动漫对话角色和场景图片生成状态====================');
- $time_start = microtime(true);
- $timeout = 50; // 超时时间50秒
- $check_interval = 3; // 每3秒检查一次
-
- while (true) {
- // 检查是否超时
- $elapsed_time = microtime(true) - $time_start;
- if ($elapsed_time >= $timeout) {
- dLog('generate')->info('检查超时,退出循环');
- break;
- }
- // 检查是否有需要处理的数据
- $has_pending_animes = $this->hasPendingAnimes();
- $has_pending_episodes = $this->hasPendingEpisodes();
- // 如果没有需要检查的内容,直接跳出循环
- if (!$has_pending_animes && !$has_pending_episodes) {
- dLog('generate')->info('没有需要检查的内容,退出循环');
- break;
- }
- // 检查动漫全局角色和场景
- if ($has_pending_animes) {
- $this->checkAnimes();
- }
- // 检查剧集角色和场景
- if ($has_pending_episodes) {
- $this->checkEpisodes();
- }
- // 等待3秒后继续下一次检查
- sleep($check_interval);
- }
- $time_end = microtime(true);
- dLog('generate')->info('脚本执行时间: '.round($time_end-$time_start, 2).'秒');
- dLog('generate')->info('====================结束检查动漫对话角色和场景图片生成状态====================');
- }
- // 检查动漫全局执行中的任务
- private function hasPendingAnimes() {
- $time_expire_datetime = date('Y-m-d H:i:s', strtotime('-12 hours'));
- DB::table('mp_animes')->where('generate_status', '执行中')->where('generate_at', '<', $time_expire_datetime)->update([
- 'generate_status' => '失败',
- 'updated_at'=>date('Y-m-d H:i:s'),
- ]);
- return DB::table('mp_animes')->where('generate_status', '执行中')->exists();
- }
- // 检查剧集执行中的任务
- private function hasPendingEpisodes() {
- $time_expire_datetime = date('Y-m-d H:i:s', strtotime('-12 hours'));
- DB::table('mp_anime_episodes')->whereIn('generate_status', ['执行中'])->where('generate_at', '<', $time_expire_datetime)->update([
- 'generate_status' => '失败',
- 'updated_at'=>date('Y-m-d H:i:s'),
- ]);
- return DB::table('mp_anime_episodes')->whereIn('generate_status', ['执行中', '待执行'])->exists();
- }
- // 检查全局
- private function checkAnimes() {
- dLog('generate')->info('====================开始检查动漫全局====================');
- // 获取待执行的动漫对话数据
- $data = DB::table('mp_animes')->where('generate_status', '执行中')->select('id', 'roles', 'scenes')->get();
-
- foreach ($data as $item) {
- $id = getProp($item, 'id');
- $roles = json_decode(getProp($item, 'roles'), true);
- $scenes = json_decode(getProp($item, 'scenes'), true);
- $role_count = count($roles);
- $scene_count = count($scenes);
- // 处理角色图片状态
- $role_result = $this->checkRoles($id, $roles);
- // 处理场景图片状态
- $scene_result = $this->checkScenes($id, $scenes);
- if ($role_result['success_count'] == $role_count && $scene_result['success_count'] == $scene_count) {
- // 同步更新该动漫对话剧集的所有角色或场景
- $this->syncRolesAndScenes($id, $role_result['roles'], $scene_result['scenes']);
- }
- }
- dLog('generate')->info('====================结束检查动漫全局====================');
- }
- // 检查全局角色
- private function checkRoles($id, $roles) {
- $role_success_count = 0;
- foreach ($roles as &$role) {
- $task_id = getProp($role, 'task_id');
- $task_status = getProp($role, 'task_status');
- if (!$task_id || $task_status == 'success' || getProp($role, 'role') == '旁白') {
- $role_success_count++;
- continue;
- }
- $task = DB::table('mp_generate_pic_tasks')->where('id', $task_id)->select('result_url', 'status')->first();
- if (in_array(getProp($task, 'status'), ['success', 'failed'])) {
- $role['task_status'] = 'success';
- $role['url'] = '';
- $result_url = getProp($task, 'result_url');
- if ($result_url && is_json($result_url)) {
- $role['url'] = json_decode($result_url, true)[0];
- }
- $role_success_count++;
- }
- }
- if ($role_success_count > 0) {
- DB::table('mp_animes')->where('id', $id)->update(['roles' => json_encode($roles, 256)]);
- }
- return [
- 'roles' => $roles,
- 'success_count' => $role_success_count,
- ];
- }
- // 检查全局场景
- private function checkScenes($id, $scenes) {
- $scene_success_count = 0;
- foreach ($scenes as &$scene) {
- $task_id = getProp($scene, 'task_id');
- $task_status = getProp($scene, 'task_status');
- if (!$task_id || $task_status == 'success') {
- $scene_success_count++;
- continue;
- }
- $task = DB::table('mp_generate_pic_tasks')->where('id', $task_id)->select('result_url', 'status')->first();
- if (in_array(getProp($task, 'status'), ['success', 'failed'])) {
- $scene['task_status'] = 'success';
- $scene['url'] = '';
- $result_url = getProp($task, 'result_url');
- if ($result_url && is_json($result_url)) {
- $scene['url'] = json_decode($result_url, true)[0];
- }
- $scene_success_count++;
- }
- }
- if ($scene_success_count > 0) {
- DB::table('mp_animes')->where('id', $id)->update(['scenes' => json_encode($scenes, 256)]);
- }
- return [
- 'scenes' => $scenes,
- 'success_count' => $scene_success_count,
- ];
- }
- // 剧集中同步继承全局角色和场景
- private function syncRolesAndScenes($id, $roles, $scenes) {
- // 获取该动漫对话对应的所有剧集
- $episodes = DB::table('mp_anime_episodes')->where('anime_id', $id)->get();
-
- if ($episodes->isEmpty()) {
- dLog('generate')->info("动漫对话ID:{$id} 没有关联的剧集");
- return;
- }
- foreach ($episodes as $episode) {
- $episode_id = getProp($episode, 'id');
- $episode_roles = json_decode(getProp($episode, 'roles'), true) ?: [];
- $episode_scenes = json_decode(getProp($episode, 'scenes'), true) ?: [];
-
- $roles_updated = false;
- $scenes_updated = false;
- // 更新剧集中的角色URL
- foreach ($episode_roles as &$episode_role) {
- $episode_role_name = getProp($episode_role, 'role');
- $episode_description = getProp($episode_role, 'description');
- $episode_url = getProp($episode_role, 'url');
- $episode_task_id = getProp($episode_role, 'task_id');
- // 如果已有URL或已有任务ID,跳过
- if (!empty($episode_url) || !empty($episode_task_id)) {
- continue;
- }
- $url_inherited = false;
- // 遍历方法中的角色数据
- foreach ($roles as $role) {
- $role_name = getProp($role, 'role');
- $role_description = getProp($role, 'description');
- $role_url = getProp($role, 'url');
- $role_task_id = getProp($role, 'task_id');
- $role_task_status = getProp($role, 'task_status');
- // 满足三个条件:1.角色名相同 2.描述相同 3.剧集中URL为空且方法中URL不为空
- if ($episode_role_name == $role_name
- && $episode_description == $role_description
- && empty($episode_url)
- && !empty($role_url)) {
- // 只继承 task_id、task_status 和 url,不继承其他创建图片的参数
- $episode_role['task_id'] = $role_task_id;
- $episode_role['task_status'] = $role_task_status;
- $episode_role['url'] = $role_url;
- $roles_updated = true;
- $url_inherited = true;
- dLog('generate')->info("剧集ID:{$episode_id} 角色:{$role_name} 继承全局数据 (task_id:{$role_task_id}, task_status:{$role_task_status}, url:{$role_url})");
- break;
- }
- }
- // 如果无法继承URL,则创建图片生成任务
- if (!$url_inherited && $episode_role_name != '旁白') {
- try {
- // 获取剧集的美术风格
- $art_style = getProp($episode, 'art_style');
- $description = $episode_description;
- if ($art_style) {
- $description = "美术风格:\n$art_style\n主体描述:$description";
- }
- $params = [
- 'prompt' => $description,
- 'ref_img_urls' => []
- ];
-
- $task = $this->aiImageGenerationService->createImageGenerationTask($params);
- $task_id = $task->id;
-
- if ($task_id) {
- $episode_role['task_id'] = $task_id;
- $episode_role['task_status'] = 'processing';
- $roles_updated = true;
- dLog('generate')->info("剧集ID:{$episode_id} 角色:{$episode_role_name} 创建图片生成任务,任务ID:{$task_id}");
- }
- } catch (\Exception $e) {
- dLog('generate')->error("剧集ID:{$episode_id} 角色:{$episode_role_name} 创建图片生成任务失败: " . $e->getMessage());
- }
- }
- }
- // 更新剧集中的场景URL
- foreach ($episode_scenes as &$episode_scene) {
- $episode_scene_name = getProp($episode_scene, 'scene');
- $episode_description = getProp($episode_scene, 'description');
- $episode_url = getProp($episode_scene, 'url');
- $episode_task_id = getProp($episode_scene, 'task_id');
- // 如果已有URL或已有任务ID,跳过
- if (!empty($episode_url) || !empty($episode_task_id)) {
- continue;
- }
- $url_inherited = false;
- // 遍历方法中的场景数据
- foreach ($scenes as $scene) {
- $scene_name = getProp($scene, 'scene');
- $scene_description = getProp($scene, 'description');
- $scene_url = getProp($scene, 'url');
- $scene_task_id = getProp($scene, 'task_id');
- $scene_task_status = getProp($scene, 'task_status');
- // 满足三个条件:1.场景名相同 2.描述相同 3.剧集中URL为空且方法中URL不为空
- if ($episode_scene_name == $scene_name
- && $episode_description == $scene_description
- && empty($episode_url)
- && !empty($scene_url)) {
- // 只继承 task_id、task_status 和 url,不继承其他创建图片的参数
- $episode_scene['task_id'] = $scene_task_id;
- $episode_scene['task_status'] = $scene_task_status;
- $episode_scene['url'] = $scene_url;
- $scenes_updated = true;
- $url_inherited = true;
- dLog('generate')->info("剧集ID:{$episode_id} 场景:{$scene_name} 继承全局数据 (task_id:{$scene_task_id}, task_status:{$scene_task_status}, url:{$scene_url})");
- break;
- }
- }
- // 如果无法继承URL,则创建图片生成任务
- if (!$url_inherited) {
- try {
- // 获取剧集的美术风格
- $art_style = getProp($episode, 'art_style');
- $description = $episode_description;
- if ($art_style) {
- $description = "美术风格:\n$art_style\n场景描述:$description";
- }
- $params = [
- 'prompt' => $description,
- 'ref_img_urls' => []
- ];
-
- $task = $this->aiImageGenerationService->createImageGenerationTask($params);
- $task_id = $task->id;
-
- if ($task_id) {
- $episode_scene['task_id'] = $task_id;
- $episode_scene['task_status'] = 'processing';
- $scenes_updated = true;
- dLog('generate')->info("剧集ID:{$episode_id} 场景:{$episode_scene_name} 创建图片生成任务,任务ID:{$task_id}");
- }
- } catch (\Exception $e) {
- dLog('generate')->error("剧集ID:{$episode_id} 场景:{$episode_scene_name} 创建图片生成任务失败: " . $e->getMessage());
- }
- }
- }
- // 如果有更新,则保存到数据库
- if ($roles_updated || $scenes_updated) {
- $update_data = ['updated_at' => date('Y-m-d H:i:s')];
-
- if ($roles_updated) {
- $update_data['roles'] = json_encode($episode_roles, 256);
- }
-
- if ($scenes_updated) {
- $update_data['scenes'] = json_encode($episode_scenes, 256);
- }
- DB::table('mp_anime_episodes')->where('id', $episode_id)->update($update_data);
- dLog('generate')->info("剧集ID:{$episode_id} 数据已同步更新");
- }
- }
- // 更新状态为已完成
- DB::table('mp_animes')->where('id', $id)->update(['generate_status' => '已完成', 'updated_at'=>date('Y-m-d H:i:s')]);
- dLog('generate')->info('动漫对话图片已全部生成完毕');
- }
- // 检查剧集
- private function checkEpisodes() {
- dLog('generate')->info('====================开始检查剧集====================');
- // 获取待执行的剧集数据
- $data = DB::table('mp_anime_episodes')->where('generate_status', '执行中')->select('id', 'roles', 'scenes')->get();
-
- foreach ($data as $item) {
- $id = getProp($item, 'id');
- $roles = json_decode(getProp($item, 'roles'), true);
- $scenes = json_decode(getProp($item, 'scenes'), true);
- $role_count = count($roles);
- $scene_count = count($scenes);
- // 处理角色图片状态
- $role_result = $this->checkEpisodeRoles($id, $roles);
- // 处理场景图片状态
- $scene_result = $this->checkEpisodeScenes($id, $scenes);
- if ($role_result['success_count'] == $role_count && $scene_result['success_count'] == $scene_count) {
- // 更新状态为已完成
- DB::table('mp_anime_episodes')->where('id', $id)->update(['generate_status' => '已完成', 'updated_at'=>date('Y-m-d H:i:s')]);
- dLog('generate')->info("剧集ID:{$id} 图片已全部生成完毕");
- }
- }
- dLog('generate')->info('====================结束检查剧集====================');
- }
- // 检查剧集角色
- private function checkEpisodeRoles($id, $roles) {
- $role_success_count = 0;
- foreach ($roles as &$role) {
- $task_id = getProp($role, 'task_id');
- $task_status = getProp($role, 'task_status');
- if (!$task_id || $task_status == 'success' || getProp($role, 'role') == '旁白') {
- $role_success_count++;
- continue;
- }
- $task = DB::table('mp_generate_pic_tasks')->where('id', $task_id)->select('result_url', 'status')->first();
- if (in_array(getProp($task, 'status'), ['success', 'failed'])) {
- $role['task_status'] = 'success';
- $role['url'] = '';
- $result_url = getProp($task, 'result_url');
- if ($result_url && is_json($result_url)) {
- $role['url'] = json_decode($result_url, true)[0];
- }
- $role_success_count++;
- }
- }
- if ($role_success_count > 0) {
- DB::table('mp_anime_episodes')->where('id', $id)->update(['roles' => json_encode($roles, 256)]);
- }
- return [
- 'roles' => $roles,
- 'success_count' => $role_success_count,
- ];
- }
- // 检查剧集场景
- private function checkEpisodeScenes($id, $scenes) {
- $scene_success_count = 0;
- foreach ($scenes as &$scene) {
- $task_id = getProp($scene, 'task_id');
- $task_status = getProp($scene, 'task_status');
- if (!$task_id || $task_status == 'success') {
- $scene_success_count++;
- continue;
- }
- $task = DB::table('mp_generate_pic_tasks')->where('id', $task_id)->select('result_url', 'status')->first();
- if (in_array(getProp($task, 'status'), ['success', 'failed'])) {
- $scene['task_status'] = 'success';
- $scene['url'] = '';
- $result_url = getProp($task, 'result_url');
- if ($result_url && is_json($result_url)) {
- $scene['url'] = json_decode($result_url, true)[0];
- }
- $scene_success_count++;
- }
- }
- if ($scene_success_count > 0) {
- DB::table('mp_anime_episodes')->where('id', $id)->update(['scenes' => json_encode($scenes, 256)]);
- }
- return [
- 'scenes' => $scenes,
- 'success_count' => $scene_success_count,
- ];
- }
- }
|