Procházet zdrojové kódy

完成动漫剧集对话和副本功能等接口

lh před 4 měsíci
rodič
revize
a5e8976ba8

+ 10 - 5
app/Console/Anime/AnimeCheckImgUrlCommand.php

@@ -28,10 +28,15 @@ class AnimeCheckImgUrlCommand extends Command
     public function handle()
     public function handle()
     {
     {
         dLog('generate')->info('====================开始检查动漫对话角色和场景图片生成状态====================');
         dLog('generate')->info('====================开始检查动漫对话角色和场景图片生成状态====================');
+        $time_expire_datetime = date('Y-m-d H:i:s', strtotime('-12 hours'));
+        DB::table('mp_anime_episodes')->where('generate_status', '执行中')->where('generate_at', '<', $time_expire_datetime)->update([
+            'generate_status' => '失败',
+            'updated_at'=>date('Y-m-d H:i:s'),
+        ]);
         $time_start = microtime(true);
         $time_start = microtime(true);
 
 
         // 获取待执行的动漫对话数据
         // 获取待执行的动漫对话数据
-        $data = DB::table('mp_animes')->where('generate_status', '执行中')->select('id', 'roles', 'scenes')->get();
+        $data = DB::table('mp_anime_episodes')->where('generate_status', '执行中')->select('id', 'roles', 'scenes')->get();
         
         
         foreach ($data as $item) {
         foreach ($data as $item) {
             $id = getProp($item, 'id');
             $id = getProp($item, 'id');
@@ -50,8 +55,8 @@ class AnimeCheckImgUrlCommand extends Command
 
 
             if ($role_success_count == $role_count && $scene_success_count == $scene_count) {
             if ($role_success_count == $role_count && $scene_success_count == $scene_count) {
                 // 更新状态为已完成
                 // 更新状态为已完成
-                DB::table('mp_animes')->where('id', $id)->update(['generate_status' => '已完成', 'updated_at'=>date('Y-m-d H:i:s')]);
-                dLog('generate')->info('动漫对话图片已生成完成');
+                DB::table('mp_anime_episodes')->where('id', $id)->update(['generate_status' => '已完成', 'updated_at'=>date('Y-m-d H:i:s')]);
+                dLog('generate')->info('动漫对话图片已全部生成完毕');
             }
             }
         }
         }
 
 
@@ -83,7 +88,7 @@ class AnimeCheckImgUrlCommand extends Command
         }
         }
 
 
         if ($role_success_count > 0) {
         if ($role_success_count > 0) {
-            DB::table('mp_animes')->where('id', $id)->update(['roles' => json_encode($roles, 256)]);
+            DB::table('mp_anime_episodes')->where('id', $id)->update(['roles' => json_encode($roles, 256)]);
         }
         }
 
 
         return $role_success_count;
         return $role_success_count;
@@ -112,7 +117,7 @@ class AnimeCheckImgUrlCommand extends Command
         }
         }
 
 
         if ($scene_success_count > 0) {
         if ($scene_success_count > 0) {
-            DB::table('mp_animes')->where('id', $id)->update(['scenes' => json_encode($scenes, 256)]);
+            DB::table('mp_anime_episodes')->where('id', $id)->update(['scenes' => json_encode($scenes, 256)]);
         }
         }
 
 
         return $scene_success_count;
         return $scene_success_count;

+ 24 - 0
app/Http/Controllers/Anime/AnimeController.php

@@ -80,6 +80,30 @@ class AnimeController extends BaseController
         return $this->success($result);
         return $this->success($result);
     }
     }
 
 
+    // 复制剧集副本
+    public function copyEpisodeVersion(Request $request) {
+        $data = $request->all();
+
+        $result = $this->AnimeService->copyEpisodeVersion($data);
+        return $this->success(['success' => $result ? 1 : 0]);
+    }
+
+    // 剧集副本列表
+    public function episodeVersions(Request $request) {
+        $data = $request->all();
+
+        $result = $this->AnimeService->episodeVersions($data);
+        return $this->success($result);
+    }
+
+    // 绑定剧集副本
+    public function bindEpisodeVersion(Request $request) {
+        $data = $request->all();
+
+        $result = $this->AnimeService->bindEpisodeVersion($data);
+        return $this->success(['success' => $result ? 1 : 0]);
+    }
+
     // public function scriptList(Request $request) {
     // public function scriptList(Request $request) {
     //     $data = $request->all();
     //     $data = $request->all();
     //     $result = $this->AnimeService->scriptList($data);
     //     $result = $this->AnimeService->scriptList($data);

+ 10 - 10
app/Libs/Helpers.php

@@ -2550,18 +2550,18 @@ function handleEpisodeContent($originalContent) {
         $result['episode_title'] = trim($titleMatch[1]);
         $result['episode_title'] = trim($titleMatch[1]);
     }
     }
     
     
-    // 提取故事梗概 - 使用更精确的匹配
-    if (preg_match('/###[  ]+故事梗概\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $summaryMatch)) {
+    // 提取故事梗概 - 兼容多种格式:###故事梗概、### 故事梗概、###     故事梗概
+    if (preg_match('/###\s*故事梗概\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $summaryMatch)) {
         $result['intro'] = trim($summaryMatch[1]);
         $result['intro'] = trim($summaryMatch[1]);
     }
     }
     
     
-    // 提取美术风格
-    if (preg_match('/###[  ]+美术风格\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $styleMatch)) {
+    // 提取美术风格 - 兼容多种格式
+    if (preg_match('/###\s*美术风格\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $styleMatch)) {
         $result['art_style'] = trim($styleMatch[1]);
         $result['art_style'] = trim($styleMatch[1]);
     }
     }
     
     
-    // 提取主体列表
-    if (preg_match('/###[  ]+主体列表\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $charactersMatch)) {
+    // 提取主体列表 - 兼容多种格式
+    if (preg_match('/###\s*主体列表\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $charactersMatch)) {
         $charactersText = trim($charactersMatch[1]);
         $charactersText = trim($charactersMatch[1]);
         $characterLines = explode("\n", $charactersText);
         $characterLines = explode("\n", $charactersText);
         
         
@@ -2579,8 +2579,8 @@ function handleEpisodeContent($originalContent) {
         }
         }
     }
     }
     
     
-    // 提取场景列表
-    if (preg_match('/###[  ]+场景列表\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $scenesMatch)) {
+    // 提取场景列表 - 兼容多种格式
+    if (preg_match('/###\s*场景列表\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $scenesMatch)) {
         $scenesText = trim($scenesMatch[1]);
         $scenesText = trim($scenesMatch[1]);
         $sceneLines = explode("\n", $scenesText);
         $sceneLines = explode("\n", $scenesText);
         
         
@@ -2598,8 +2598,8 @@ function handleEpisodeContent($originalContent) {
         }
         }
     }
     }
     
     
-    // 提取分镜剧本
-    if (preg_match('/###[  ]+分镜剧本\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $storyboardMatch)) {
+    // 提取分镜剧本 - 兼容多种格式
+    if (preg_match('/###\s*分镜剧本\s*\n(.*?)(?=\n\s*###[^#]|\z)/s', $originalContent, $storyboardMatch)) {
         $storyboardText = trim($storyboardMatch[1]);
         $storyboardText = trim($storyboardMatch[1]);
         
         
         // 按幕分割 - 修复第1幕识别和乱码问题
         // 按幕分割 - 修复第1幕识别和乱码问题

+ 104 - 30
app/Services/Anime/AnimeService.php

@@ -69,9 +69,10 @@ class AnimeService
         }
         }
         
         
         // 保存角色信息
         // 保存角色信息
-        $boolen = DB::table('mp_anime_episodes')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->update([
+        $boolen = DB::table('mp_anime_episodes')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->where('is_default', 1)->update([
             'roles'             => json_encode($roles, 256),
             'roles'             => json_encode($roles, 256),
             'generate_status'   => '执行中',
             'generate_status'   => '执行中',
+            'generate_status'   => date('Y-m-d H:i:s'),
             'updated_at'        => date('Y-m-d H:i:s')
             'updated_at'        => date('Y-m-d H:i:s')
         ]);
         ]);
         if (!$boolen) {
         if (!$boolen) {
@@ -217,34 +218,15 @@ class AnimeService
         $anime['roles'] = json_decode($anime['roles']);
         $anime['roles'] = json_decode($anime['roles']);
         $anime['scenes'] = json_decode($anime['scenes']);
         $anime['scenes'] = json_decode($anime['scenes']);
 
 
-        // // 获取分集信息
-        // $episodes = DB::table('mp_anime_episodes')->where('anime_id', $anime_id)->orderBy('episode_number')
-        // ->select('id', 'episode_number', 'title', 'content', 'segment_number', 'segment_content')
-        // ->orderBy('episode_number')->orderBy('segment_number')->get()->map(function ($value) {
-        //     return (array)$value;
-        // })->toArray();
-        // $return_episodes = [];
-        // foreach($episodes as $episode) {
-        //     $episode_number = getProp($episode, 'episode_number');
-        //     if (isset($return_episodes[$episode_number])) {
-        //         $return_episodes[$episode_number]['segments'][] = [
-        //                 'segment_number'    => getProp($episode, 'segment_number'),
-        //                 'segment_content'   => getProp($episode, 'segment_content')
-        //             ];
-        //     }else {
-        //         $return_episodes[$episode_number] = [
-        //             'episode_number'    => $episode_number, 
-        //             'title'             => getProp($episode, 'title'),
-        //             'segments'          => [
-        //                 [
-        //                     'segment_number'    => getProp($episode, 'segment_number'),
-        //                     'segment_content'   => getProp($episode, 'segment_content')
-        //                 ]
-        //             ]
-        //         ];
-        //     }
-        // }
-        // $anime['episodes'] = array_values($return_episodes);
+        // 获取分集信息
+        $episodes = DB::table('mp_anime_episodes')->where('anime_id', $anime_id)->where('is_default', 1)
+        ->select('id as episode_id', 'episode_number', 'title')
+        ->orderBy('episode_number')->get()->map(function ($value) {
+            return (array)$value;
+        })->toArray();
+
+        $anime['episodes'] = $episodes;
+
         return $anime;
         return $anime;
     }
     }
 
 
@@ -255,7 +237,7 @@ class AnimeService
             Utils::throwError('1002:请选择剧集');
             Utils::throwError('1002:请选择剧集');
         }
         }
 
 
-        $episode = DB::table('mp_anime_episodes')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->select('anime_id', 'episode_number', 'title', 'intro', 'art_style', 'roles', 'scenes', 'acts')->first();
+        $episode = DB::table('mp_anime_episodes')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->where('is_default', 1)->select('id as episode_id', 'anime_id', 'episode_number', 'title', 'intro', 'art_style', 'roles', 'scenes', 'acts')->first();
         if (!$episode) Utils::throwError('20003: 该剧集不存在!');
         if (!$episode) Utils::throwError('20003: 该剧集不存在!');
         $episode = (array)$episode;
         $episode = (array)$episode;
         $episode['roles'] = json_decode($episode['roles'], true);
         $episode['roles'] = json_decode($episode['roles'], true);
@@ -264,4 +246,96 @@ class AnimeService
 
 
         return $episode;
         return $episode;
     }
     }
+
+    public function copyEpisodeVersion($data) {
+        $episode_id = getProp($data, 'episode_id');
+
+        $episode = DB::table('mp_anime_episodes')->where('id', $episode_id)->first();
+        if (!$episode) Utils::throwError('20003:该剧集不存在');
+
+        $episode = (array)$episode;
+        $count = DB::table('mp_anime_episodes')->where('anime_id', $episode['anime_id'])->where('episode_number', $episode['episode_number'])->count('id');
+        $sequence = $count + 1;
+        unset($episode['id']);
+        $episode['sequence'] = $sequence;
+        $episode['created_at'] = date('Y-m-d H:i:s');
+        $episode['updated_at'] = date('Y-m-d H:i:s');
+        $episode['is_default'] = 1;
+
+        try {
+            DB::beginTransaction();
+            // 移除is_default标志
+            $boolen = DB::table('mp_anime_episodes')->where('anime_id', $episode['anime_id'])->where('episode_number', $episode['episode_number'])->update(['is_default' => 0, 'updated_at'=>date('Y-m-d H:i:s')]);
+            if (!$boolen) {
+                Utils::throwError('20003:更新失败!');
+            }
+
+            // 新增副本
+            $new_episode_id = DB::table('mp_anime_episodes')->insertGetId($episode);
+            if (!$new_episode_id) {
+                Utils::throwError('20003:创建副本失败!');
+            }
+            $episode['episode_id'] = $new_episode_id;
+        }catch (\Exception $e) {
+            DB::rollBack();
+            Utils::throwError('20003:'.$e->getMessage());
+        }
+
+        DB::commit();
+
+        return $episode;
+    }
+
+    public function episodeVersions($data) {
+        $anime_id = getProp($data, 'anime_id');
+        $episode_number = getProp($data, 'episode_number');
+        if (!$anime_id || !$episode_number) {
+            Utils::throwError('1002:请选择剧集');
+        }
+
+        $episodes = DB::table('mp_anime_episodes')->where('anime_id', $anime_id)->where('episode_number', $episode_number)->select('anime_id', 'episode_number', 'sequence', 'title', 'intro', 'art_style', 'roles', 'scenes', 'acts')->get()->map(function($value) {
+            return (array)$value;
+        })->toArray();
+        if (!$episodes) Utils::throwError('20003: 该剧集不存在!');
+        foreach($episodes as &$episode) {
+            $episode['version'] = 'V'.$episode['sequence'];
+            $episode['roles'] = json_decode($episode['roles'], true);
+            $episode['scenes'] = json_decode($episode['scenes'], true);
+            $episode['acts'] = json_decode($episode['acts'], true);
+        }
+
+        return $episodes;
+    }
+
+    public function bindEpisodeVersion($data) {
+        $episode_id = getProp($data, 'episode_id');
+
+        $episode = DB::table('mp_anime_episodes')->where('id', $episode_id)->first();
+        if (!$episode) Utils::throwError('20003:该剧集不存在');
+        $episode = (array)$episode;
+
+        if ((int)$episode['is_default'] === 1) return true;
+
+        try {
+            DB::beginTransaction();
+            // 移除is_default标志
+            $boolen = DB::table('mp_anime_episodes')->where('anime_id', $episode['anime_id'])->where('episode_number', $episode['episode_number'])->update(['is_default' => 0, 'updated_at'=>date('Y-m-d H:i:s')]);
+            if (!$boolen) {
+                Utils::throwError('20003:更新失败!');
+            }
+
+            // 绑定副本
+            $boolen2 = DB::table('mp_anime_episodes')->where('id', $episode_id)->update(['is_default' => 1, 'updated_at'=>date('Y-m-d H:i:s')]);
+            if (!$boolen2) {
+                Utils::throwError('20003:绑定失败!');
+            }
+        }catch (\Exception $e) {
+            DB::rollBack();
+            Utils::throwError('20003:'.$e->getMessage());
+        }
+
+        DB::commit();
+
+        return true;
+    }
 }
 }

+ 8 - 3
app/Services/DeepSeek/DeepSeekService.php

@@ -2299,7 +2299,7 @@ EOD;
                     4.<分镜剧本>要求包含“分幕标题”以及“分镜信息”: \n\n
                     4.<分镜剧本>要求包含“分幕标题”以及“分镜信息”: \n\n
                     4.1“分幕标题”是根据章节中的场景进行拆分,类似于“第1幕 场景、时间及室内或室外”的信息,其中场景只能在所给出的场景列表中选(不带场景详细描述)。
                     4.1“分幕标题”是根据章节中的场景进行拆分,类似于“第1幕 场景、时间及室内或室外”的信息,其中场景只能在所给出的场景列表中选(不带场景详细描述)。
                     4.2“分镜信息”需同一场景内的对话内容完成分镜(每一幕分镜尽量不得低于5个),需包含“画面描述”、“构图设计”、“运镜调度”、“配音音色”、“台词内容”和“画面类型”这几个部分,其中“画面描述”如果出现主体,只能选择上述主体列表中的一个,主体的名称也需要保持一致(不带主体具体描述)。
                     4.2“分镜信息”需同一场景内的对话内容完成分镜(每一幕分镜尽量不得低于5个),需包含“画面描述”、“构图设计”、“运镜调度”、“配音音色”、“台词内容”和“画面类型”这几个部分,其中“画面描述”如果出现主体,只能选择上述主体列表中的一个,主体的名称也需要保持一致(不带主体具体描述)。
-                    4.3分镜序号用1、2、3显示,如分镜1、分镜2,并且分镜序号需连续(跨分幕中的分镜序号需连续)。
+                    4.3分镜序号用1、2、3显示,如分镜1、分镜2,并且分镜序号需连续(跨分幕中的分镜序号需连续,但跨剧集中的不需要连续,如第2集的第1幕分镜序号从1开始)。
                     4.4“分镜信息”中的“台词内容”需与原文中的台词完全一致,不得修改或添加任何内容。
                     4.4“分镜信息”中的“台词内容”需与原文中的台词完全一致,不得修改或添加任何内容。
                 示例格式:\n\n
                 示例格式:\n\n
                 第1集:隐形的守护者
                 第1集:隐形的守护者
@@ -2581,7 +2581,10 @@ EOD;
                 'roles' => json_encode($episode_arr['roles'], 256),
                 'roles' => json_encode($episode_arr['roles'], 256),
                 'scenes' => json_encode($episode_arr['scenes'], 256),
                 'scenes' => json_encode($episode_arr['scenes'], 256),
                 'acts' => json_encode($episode_arr['acts'], 256),
                 'acts' => json_encode($episode_arr['acts'], 256),
-                // 'sequence' => 0,
+                'sequence' => 1,
+                'generate_status' => '待执行',
+                'generate_at' => date('Y-m-d H:i:s'),
+                'is_default' => 1,
                 'created_at' => date('Y-m-d H:i:s'),
                 'created_at' => date('Y-m-d H:i:s'),
             ];
             ];
 
 
@@ -2590,7 +2593,8 @@ EOD;
                 $episode['updated_at'] = date('Y-m-d H:i:s');
                 $episode['updated_at'] = date('Y-m-d H:i:s');
                 $boolen2 = DB::table('mp_anime_episodes')->where('id', $episode_id)->update($episode);
                 $boolen2 = DB::table('mp_anime_episodes')->where('id', $episode_id)->update($episode);
             }else {
             }else {
-                $boolen2 = DB::table('mp_anime_episodes')->insert($episode);
+                $episode_id = DB::table('mp_anime_episodes')->insertGetId($episode);
+                $boolen2 = $episode_id ? true : false;
             }
             }
             
             
             if (!$boolen2) {
             if (!$boolen2) {
@@ -2639,6 +2643,7 @@ EOD;
         
         
         DB::commit();
         DB::commit();
 
 
+        $episode['episode_id'] = $episode_id;
         $episode['roles'] = json_decode($episode['roles'], true);
         $episode['roles'] = json_decode($episode['roles'], true);
         $episode['scenes'] = json_decode($episode['scenes'], true);
         $episode['scenes'] = json_decode($episode['scenes'], true);
         $episode['acts'] = json_decode($episode['acts'], true);
         $episode['acts'] = json_decode($episode['acts'], true);

+ 3 - 0
routes/api.php

@@ -131,6 +131,9 @@ Route::group(['middleware' => ['bindToken', 'bindExportToken', 'checkLogin']], f
         Route::post('chat', [DeepSeekController::class, 'chat']);
         Route::post('chat', [DeepSeekController::class, 'chat']);
         Route::get('detail', [AnimeController::class, 'animeDetail']);
         Route::get('detail', [AnimeController::class, 'animeDetail']);
         Route::get('episodeInfo', [AnimeController::class, 'episodeInfo']);
         Route::get('episodeInfo', [AnimeController::class, 'episodeInfo']);
+        Route::get('copyEpisodeVersion', [AnimeController::class, 'copyEpisodeVersion']);
+        Route::get('episodeVersions', [AnimeController::class, 'episodeVersions']);
+        Route::get('bindEpisodeVersion', [AnimeController::class, 'bindEpisodeVersion']);
         Route::post('edit', [AnimeController::class, 'editAnime']);
         Route::post('edit', [AnimeController::class, 'editAnime']);
         Route::post('batchUploadImg', [ImageGenerationController::class, 'batchUploadImg']);
         Route::post('batchUploadImg', [ImageGenerationController::class, 'batchUploadImg']);
         Route::post('batchSetRoleImg', [AnimeController::class, 'batchSetRoleImg']);
         Route::post('batchSetRoleImg', [AnimeController::class, 'batchSetRoleImg']);