|
|
@@ -7862,7 +7862,7 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
}
|
|
|
|
|
|
// 获取最后一集序号
|
|
|
- $end_episode_sequence = getProp($anime, 'end_episode_sequence');
|
|
|
+ $end_episode_sequence = getProp($anime, 'end_episode_sequence', 0);
|
|
|
if ($end_episode_sequence && $episode_number > $end_episode_sequence) Utils::throwError("20003:总集数只有{$end_episode_sequence}集,不允许继续策划下一集!");
|
|
|
|
|
|
// 转换主体列表和场景列表的格式
|
|
|
@@ -8441,16 +8441,15 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
$model = getProp($data, 'model');
|
|
|
if (!$model) {
|
|
|
// 用户没有输入,从anime表获取
|
|
|
- $anime = DB::table('mp_animes')->where('id', $anime_id)->first();
|
|
|
$model = getProp($anime, 'model');
|
|
|
if (!$model) {
|
|
|
// anime表也没有,使用默认值
|
|
|
- $model = 'doubao-seed-2-0-mini-260215';
|
|
|
+ $model = 'deepseek-v4-pro';
|
|
|
}
|
|
|
}
|
|
|
// 验证模型是否在可用模型表中
|
|
|
if (!DB::table('mp_text_models')->where('model', $model)->where('is_enabled', 1)->exists()) {
|
|
|
- $model = 'doubao-seed-2-0-mini-260215';
|
|
|
+ $model = 'deepseek-v4-pro';
|
|
|
}
|
|
|
|
|
|
// 模型兼容性处理:deepseek-chat 和 deepseek-reasoner 映射到 deepseek-v4-flash
|
|
|
@@ -8906,6 +8905,9 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
$episode['episode_id'] = $episode_id;
|
|
|
$episode['roles'] = $merged_roles;
|
|
|
$episode['scenes'] = $merged_scenes;
|
|
|
+ $episode['props'] = $merged_props;
|
|
|
+ $episode['end_episode_sequence'] = $end_episode_sequence;
|
|
|
+
|
|
|
// $episode['acts'] = isset($episode_arr['acts']) && is_array($episode_arr['acts']) ? $episode_arr['acts'] : [];
|
|
|
yield [
|
|
|
'type' => 'done',
|
|
|
@@ -8917,33 +8919,69 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * chatForAce的非流式版本 - 用于队列任务
|
|
|
- * 与chatForAce逻辑完全一致,但不使用流式输出
|
|
|
+ * chatForAce的非流式版本 - 用于定时任务
|
|
|
+ * 只获取最终 type=done 的数据,并验证必要字段
|
|
|
*
|
|
|
* @param array $data 请求数据
|
|
|
* @return array 生成结果
|
|
|
*/
|
|
|
public function chatForAceNonStream($data) {
|
|
|
- $allChunks = [];
|
|
|
$finalResult = null;
|
|
|
|
|
|
- // 调用流式方法并收集所有数据
|
|
|
+ // 调用流式方法,只保存 type=done 的数据
|
|
|
foreach ($this->chatForAce($data) as $chunk) {
|
|
|
- $allChunks[] = $chunk;
|
|
|
-
|
|
|
- // 保存最终结果
|
|
|
+ // 只关注最终结果
|
|
|
if (isset($chunk['type']) && $chunk['type'] === 'done') {
|
|
|
$finalResult = $chunk;
|
|
|
+ break; // 找到 done 就退出循环
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 返回最终结果
|
|
|
- if ($finalResult) {
|
|
|
+ // 验证最终结果
|
|
|
+ if (!$finalResult) {
|
|
|
+ return ['error' => '生成失败:未获取到最终结果'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有错误
|
|
|
+ if (isset($finalResult['error']) && $finalResult['error']) {
|
|
|
return $finalResult;
|
|
|
}
|
|
|
|
|
|
- // 如果没有done类型的数据,返回最后一个chunk
|
|
|
- return end($allChunks) ?: ['error' => '生成失败'];
|
|
|
+ // 验证必要字段
|
|
|
+ $episode = $finalResult['episode'] ?? [];
|
|
|
+
|
|
|
+ // 验证 roles
|
|
|
+ if (empty($episode['roles']) || !is_array($episode['roles'])) {
|
|
|
+ return ['error' => '生成失败:主体列表(roles)为空或格式错误'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证 scenes
|
|
|
+ if (empty($episode['scenes']) || !is_array($episode['scenes'])) {
|
|
|
+ return ['error' => '生成失败:场景列表(scenes)为空或格式错误'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证 props(道具可以为空,但必须存在且为数组)
|
|
|
+ if (!isset($episode['props']) || !is_array($episode['props'])) {
|
|
|
+ $episode['props'] = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证 acts(分镜剧本)
|
|
|
+ if (empty($episode['acts']) || !is_array($episode['acts'])) {
|
|
|
+ return ['error' => '生成失败:分镜剧本(acts)为空或格式错误'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新验证后的 episode 数据
|
|
|
+ $finalResult['episode'] = $episode;
|
|
|
+
|
|
|
+ // 记录验证成功日志
|
|
|
+ dLog('deepseek')->info('chatForAceNonStream 验证通过', [
|
|
|
+ 'roles_count' => count($episode['roles']),
|
|
|
+ 'scenes_count' => count($episode['scenes']),
|
|
|
+ 'props_count' => count($episode['props']),
|
|
|
+ 'acts_count' => count($episode['acts'])
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return $finalResult;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -8972,6 +9010,11 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
Utils::throwError('20003:该剧集不存在');
|
|
|
}
|
|
|
|
|
|
+ // 检查是否是全能模式
|
|
|
+ if ((int)getProp($anime, 'ace_mode') !== 1) {
|
|
|
+ Utils::throwError('20003:只有全能模式才支持批量生成');
|
|
|
+ }
|
|
|
+
|
|
|
// 检查是否是多剧集模式
|
|
|
if ((int)getProp($anime, 'is_multi') !== 1) {
|
|
|
Utils::throwError('20003:单剧集不支持批量生成');
|
|
|
@@ -8988,15 +9031,6 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
// 确定起始集数
|
|
|
$startEpisodeNumber = $currentMaxEpisode + 1;
|
|
|
|
|
|
- // 不支持指定起始集数(必须按现有集数循序渐进)
|
|
|
- // // 如果用户指定了起始集数(通过episode_number参数)
|
|
|
- // if (isset($data['episode_number'])) {
|
|
|
- // $userStartEpisode = (int)getProp($data, 'episode_number');
|
|
|
- // if ($userStartEpisode > $currentMaxEpisode) {
|
|
|
- // $startEpisodeNumber = $userStartEpisode;
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
// 计算结束集数
|
|
|
$endEpisodeNumber = $startEpisodeNumber + $generate_episode_number - 1;
|
|
|
|
|
|
@@ -9006,68 +9040,78 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
Utils::throwError("20003:总集数只有{$end_episode_sequence}集,无法生成到第{$endEpisodeNumber}集");
|
|
|
}
|
|
|
|
|
|
- // 检查是否已有进行中的任务
|
|
|
- $existingTask = DB::table('mp_batch_episode_generation_tasks')
|
|
|
- ->where('anime_id', $anime_id)
|
|
|
- ->whereIn('status', ['pending', 'processing'])
|
|
|
- ->first();
|
|
|
-
|
|
|
- if ($existingTask) {
|
|
|
- Utils::throwError('20003:该剧集已有批量生成任务正在进行中,请等待完成后再试');
|
|
|
- }
|
|
|
-
|
|
|
$now = date('Y-m-d H:i:s');
|
|
|
+ $createdTasks = [];
|
|
|
+ $skippedTasks = [];
|
|
|
|
|
|
- // 准备任务数据
|
|
|
- $taskData = [
|
|
|
- 'anime_id' => $anime_id,
|
|
|
- 'uid' => $uid,
|
|
|
- 'cpid' => $cpid,
|
|
|
- 'start_episode' => $startEpisodeNumber,
|
|
|
- 'end_episode' => $endEpisodeNumber,
|
|
|
- 'total_episodes' => $generate_episode_number,
|
|
|
- 'current_episode' => 0,
|
|
|
- 'completed_episodes' => 0,
|
|
|
- 'status' => 'pending',
|
|
|
- 'request_data' => json_encode($data, JSON_UNESCAPED_UNICODE),
|
|
|
- 'created_at' => $now,
|
|
|
- 'updated_at' => $now
|
|
|
- ];
|
|
|
-
|
|
|
- // 创建批量生成任务记录
|
|
|
- $taskId = DB::table('mp_batch_episode_generation_tasks')->insertGetId($taskData);
|
|
|
-
|
|
|
- if (!$taskId) {
|
|
|
- Utils::throwError('20003:创建批量生成任务失败');
|
|
|
- }
|
|
|
-
|
|
|
- // 更新任务状态为processing
|
|
|
- DB::table('mp_batch_episode_generation_tasks')
|
|
|
- ->where('id', $taskId)
|
|
|
- ->update([
|
|
|
- 'status' => 'processing',
|
|
|
+ // 为每一集创建详情记录
|
|
|
+ for ($episodeNumber = $startEpisodeNumber; $episodeNumber <= $endEpisodeNumber; $episodeNumber++) {
|
|
|
+ // 检查是否已有该集的任务记录
|
|
|
+ $existingTask = DB::table('mp_batch_episode_generation_details')
|
|
|
+ ->where('anime_id', $anime_id)
|
|
|
+ ->where('episode_number', $episodeNumber)
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if ($existingTask) {
|
|
|
+ // 如果已存在且未完成,跳过
|
|
|
+ if (in_array($existingTask->status, ['pending', 'processing'])) {
|
|
|
+ $skippedTasks[] = $episodeNumber;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是失败状态,可以重新创建(删除旧记录)
|
|
|
+ if ($existingTask->status === 'failed') {
|
|
|
+ DB::table('mp_batch_episode_generation_details')
|
|
|
+ ->where('id', $existingTask->id)
|
|
|
+ ->delete();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是已完成状态,也跳过
|
|
|
+ if ($existingTask->status === 'completed') {
|
|
|
+ $skippedTasks[] = $episodeNumber;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 准备任务数据
|
|
|
+ $taskData = [
|
|
|
+ 'anime_id' => $anime_id,
|
|
|
+ 'uid' => $uid,
|
|
|
+ 'cpid' => $cpid,
|
|
|
+ 'episode_number' => $episodeNumber,
|
|
|
+ 'status' => 'pending',
|
|
|
+ 'request_data' => json_encode($data, JSON_UNESCAPED_UNICODE),
|
|
|
+ 'retry_count' => 0,
|
|
|
+ 'created_at' => $now,
|
|
|
'updated_at' => $now
|
|
|
- ]);
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 创建任务记录
|
|
|
+ $taskId = DB::table('mp_batch_episode_generation_details')->insertGetId($taskData);
|
|
|
+
|
|
|
+ if ($taskId) {
|
|
|
+ $createdTasks[] = $episodeNumber;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 分发到队列
|
|
|
- \App\Jobs\ProcessBatchEpisodeGenerationJob::dispatch(
|
|
|
- $taskId,
|
|
|
- $anime_id,
|
|
|
- $startEpisodeNumber,
|
|
|
- $generate_episode_number,
|
|
|
- $data,
|
|
|
- $uid,
|
|
|
- $cpid
|
|
|
- );
|
|
|
+ if (empty($createdTasks)) {
|
|
|
+ if (!empty($skippedTasks)) {
|
|
|
+ Utils::throwError('20003:所有集数都已存在任务记录,无需重复创建。已跳过集数:' . implode(',', $skippedTasks));
|
|
|
+ } else {
|
|
|
+ Utils::throwError('20003:创建批量生成任务失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return [
|
|
|
- 'task_id' => $taskId,
|
|
|
'anime_id' => $anime_id,
|
|
|
'start_episode' => $startEpisodeNumber,
|
|
|
'end_episode' => $endEpisodeNumber,
|
|
|
'total_episodes' => $generate_episode_number,
|
|
|
- 'status' => 'processing',
|
|
|
- 'message' => "已创建批量生成任务,将生成第{$startEpisodeNumber}集到第{$endEpisodeNumber}集,共{$generate_episode_number}集"
|
|
|
+ 'created_episodes' => $createdTasks,
|
|
|
+ 'skipped_episodes' => $skippedTasks,
|
|
|
+ 'message' => "已创建批量生成任务,将生成第{$startEpisodeNumber}集到第{$endEpisodeNumber}集,共{$generate_episode_number}集。" .
|
|
|
+ "已创建: " . implode(',', $createdTasks) .
|
|
|
+ (!empty($skippedTasks) ? ";已跳过: " . implode(',', $skippedTasks) : "")
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -9078,67 +9122,85 @@ Q版卡通风格,头大身小,造型圆润可爱,线条简单,色彩明
|
|
|
* @return array 任务状态信息
|
|
|
*/
|
|
|
public function getBatchGenerationTaskStatus($data) {
|
|
|
- $task_id = getProp($data, 'task_id');
|
|
|
$anime_id = getProp($data, 'anime_id');
|
|
|
|
|
|
- if (!$task_id && !$anime_id) {
|
|
|
- Utils::throwError('20003:task_id或anime_id参数缺失');
|
|
|
- }
|
|
|
-
|
|
|
- $query = DB::table('mp_batch_episode_generation_tasks');
|
|
|
-
|
|
|
- if ($task_id) {
|
|
|
- $query->where('id', $task_id);
|
|
|
- } else {
|
|
|
- // 获取该anime最新的任务
|
|
|
- $query->where('anime_id', $anime_id)
|
|
|
- ->orderByDesc('id')
|
|
|
- ->limit(1);
|
|
|
- }
|
|
|
-
|
|
|
- $task = $query->first();
|
|
|
-
|
|
|
- if (!$task) {
|
|
|
- Utils::throwError('20003:任务不存在');
|
|
|
+ if (!$anime_id) {
|
|
|
+ Utils::throwError('20003:anime_id参数缺失');
|
|
|
}
|
|
|
|
|
|
- // 获取任务详情
|
|
|
- $details = DB::table('mp_batch_episode_generation_details')
|
|
|
- ->where('task_id', $task->id)
|
|
|
+ // 获取该anime的所有任务
|
|
|
+ $tasks = DB::table('mp_batch_episode_generation_details')
|
|
|
+ ->where('anime_id', $anime_id)
|
|
|
->orderBy('episode_number')
|
|
|
->get()
|
|
|
->map(function($item) {
|
|
|
return [
|
|
|
+ 'id' => $item->id,
|
|
|
'episode_number' => $item->episode_number,
|
|
|
'status' => $item->status,
|
|
|
'error_message' => $item->error_message,
|
|
|
+ 'retry_count' => $item->retry_count,
|
|
|
'created_at' => $item->created_at,
|
|
|
- 'updated_at' => $item->updated_at
|
|
|
+ 'updated_at' => $item->updated_at,
|
|
|
+ 'completed_at' => $item->completed_at
|
|
|
];
|
|
|
})
|
|
|
->toArray();
|
|
|
|
|
|
+ if (empty($tasks)) {
|
|
|
+ Utils::throwError('20003:该剧集没有批量生成任务');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 统计各状态数量
|
|
|
+ $totalCount = count($tasks);
|
|
|
+ $pendingCount = 0;
|
|
|
+ $processingCount = 0;
|
|
|
+ $completedCount = 0;
|
|
|
+ $failedCount = 0;
|
|
|
+
|
|
|
+ foreach ($tasks as $task) {
|
|
|
+ switch ($task['status']) {
|
|
|
+ case 'pending':
|
|
|
+ $pendingCount++;
|
|
|
+ break;
|
|
|
+ case 'processing':
|
|
|
+ $processingCount++;
|
|
|
+ break;
|
|
|
+ case 'completed':
|
|
|
+ $completedCount++;
|
|
|
+ break;
|
|
|
+ case 'failed':
|
|
|
+ $failedCount++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 计算进度百分比
|
|
|
$progress = 0;
|
|
|
- if ($task->total_episodes > 0) {
|
|
|
- $progress = round(($task->completed_episodes / $task->total_episodes) * 100, 2);
|
|
|
+ if ($totalCount > 0) {
|
|
|
+ $progress = round(($completedCount / $totalCount) * 100, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断整体状态
|
|
|
+ $overallStatus = 'processing';
|
|
|
+ if ($completedCount === $totalCount) {
|
|
|
+ $overallStatus = 'completed';
|
|
|
+ } elseif ($pendingCount === $totalCount) {
|
|
|
+ $overallStatus = 'pending';
|
|
|
+ } elseif ($failedCount > 0 && ($pendingCount + $processingCount) === 0) {
|
|
|
+ $overallStatus = 'failed';
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
- 'task_id' => $task->id,
|
|
|
- 'anime_id' => $task->anime_id,
|
|
|
- 'start_episode' => $task->start_episode,
|
|
|
- 'end_episode' => $task->end_episode,
|
|
|
- 'total_episodes' => $task->total_episodes,
|
|
|
- 'current_episode' => $task->current_episode,
|
|
|
- 'completed_episodes' => $task->completed_episodes,
|
|
|
+ 'anime_id' => $anime_id,
|
|
|
+ 'total_episodes' => $totalCount,
|
|
|
+ 'pending_count' => $pendingCount,
|
|
|
+ 'processing_count' => $processingCount,
|
|
|
+ 'completed_count' => $completedCount,
|
|
|
+ 'failed_count' => $failedCount,
|
|
|
'progress' => $progress,
|
|
|
- 'status' => $task->status,
|
|
|
- 'error_message' => $task->error_message,
|
|
|
- 'details' => $details,
|
|
|
- 'created_at' => $task->created_at,
|
|
|
- 'updated_at' => $task->updated_at,
|
|
|
- 'completed_at' => $task->completed_at
|
|
|
+ 'overall_status' => $overallStatus,
|
|
|
+ 'tasks' => $tasks
|
|
|
];
|
|
|
}
|
|
|
|