aiImageGenerationService = $aiImageGenerationService; } /** * Execute the console command. * * @return int */ public function handle() { dLog('command')->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('command')->info('检查超时,退出循环'); break; } // 检查是否有需要处理的数据 $has_pending_animes = $this->hasPendingAnimes(); $has_pending_episodes = $this->hasPendingEpisodes(); // 如果没有需要检查的内容,直接跳出循环 if (!$has_pending_animes && !$has_pending_episodes) { dLog('command')->info('没有需要检查的内容,退出循环'); break; } // 检查动漫全局角色和场景 if ($has_pending_animes) { $this->checkAnimes(); } // 检查剧集角色和场景 if ($has_pending_episodes) { $this->checkEpisodes(); } // 等待3秒后继续下一次检查 sleep($check_interval); } $time_end = microtime(true); dLog('command')->info('脚本执行时间: '.round($time_end-$time_start, 2).'秒'); dLog('command')->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('command')->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('command')->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('command')->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('command')->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('command')->info("剧集ID:{$episode_id} 角色:{$episode_role_name} 创建图片生成任务,任务ID:{$task_id}"); } } catch (\Exception $e) { dLog('command')->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('command')->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('command')->info("剧集ID:{$episode_id} 场景:{$episode_scene_name} 创建图片生成任务,任务ID:{$task_id}"); } } catch (\Exception $e) { dLog('command')->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); } // 继承URL或创建任务后,将状态改为待执行,等后续检查分集图片成功后再改为成功状态 $update_data['generate_status'] = '待执行'; $update_data['generate_at'] = date('Y-m-d H:i:s'); DB::table('mp_anime_episodes')->where('id', $episode_id)->update($update_data); dLog('command')->info("剧集ID:{$episode_id} 数据已同步更新,状态改为待执行"); } } // 更新状态为已完成 DB::table('mp_animes')->where('id', $id)->update(['generate_status' => '已完成', 'updated_at'=>date('Y-m-d H:i:s')]); dLog('command')->info('动漫对话图片已全部生成完毕'); } // 检查剧集 private function checkEpisodes() { dLog('command')->info('====================开始检查剧集===================='); // 获取待执行和执行中的剧集数据 $data = DB::table('mp_anime_episodes')->whereIn('generate_status', ['待执行', '执行中'])->select('id', 'roles', 'scenes', 'generate_status')->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); $current_status = getProp($item, 'generate_status'); // 如果状态是待执行,先改为执行中 if ($current_status == '待执行') { DB::table('mp_anime_episodes')->where('id', $id)->update([ 'generate_status' => '执行中', 'generate_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ]); dLog('command')->info("剧集ID:{$id} 状态从待执行改为执行中"); } // 处理角色图片状态 $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('command')->info("剧集ID:{$id} 图片已全部生成完毕,状态改为已完成"); } } dLog('command')->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, ]; } }