فهرست منبع

初步完成可灵主体管理功能

lh 4 ماه پیش
والد
کامیت
e7882d5597

+ 353 - 1
app/Http/Controllers/AIGeneration/VideoGenerationController.php

@@ -10,6 +10,7 @@ use Illuminate\Http\Request;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Support\Facades\Validator;
 use Illuminate\Support\Facades\Validator;
 use App\Libs\ApiResponse;
 use App\Libs\ApiResponse;
+use App\Models\AIElement;
 
 
 class VideoGenerationController extends BaseController
 class VideoGenerationController extends BaseController
 {
 {
@@ -755,4 +756,355 @@ class VideoGenerationController extends BaseController
         $result = $this->aiVideoGenerationService->getTaskList($data);
         $result = $this->aiVideoGenerationService->getTaskList($data);
         return $this->success($result, [new AIGenerationTransformer(), 'newBuildTaskList']);
         return $this->success($result, [new AIGenerationTransformer(), 'newBuildTaskList']);
     }
     }
-}
+
+    // ==================== 主体管理相关接口 ====================
+
+    /**
+     * 创建主体
+     *
+     * @param Request $request
+     * @return JsonResponse
+     */
+    public function createElement(Request $request): JsonResponse
+    {
+        try {
+            // 验证请求参数
+            $validated = $request->validate([
+                'element_name' => 'required|string|max:20',
+                'element_description' => 'required|string|max:100',
+                'reference_type' => 'required|in:image_refer,video_refer',
+                'element_image_list' => 'required_if:reference_type,image_refer|array',
+                'element_image_list.frontal_image' => 'required_if:reference_type,image_refer|string',
+                'element_image_list.refer_images' => 'required_if:reference_type,image_refer|array|min:1|max:3',
+                'element_image_list.refer_images.*.image_url' => 'required|string',
+                'element_video_list' => 'required_if:reference_type,video_refer|array',
+                'element_video_list.refer_videos' => 'required_if:reference_type,video_refer|array|size:1',
+                'element_video_list.refer_videos.*.video_url' => 'required|string',
+                'element_voice_id' => 'nullable|string',
+                'tag_list' => 'nullable|array',
+                'tag_list.*.tag_id' => 'required|in:o_101,o_102,o_103,o_104,o_105,o_106,o_107,o_108',
+                'callback_url' => 'nullable|url',
+                'external_task_id' => 'nullable|string',
+            ]);
+
+            // 调用服务层创建主体
+            $element = $this->aiVideoGenerationService->createElement($validated);
+
+            return response()->json([
+                'code' => 0,
+                'message' => '主体创建任务已提交',
+                'request_id' => uniqid(),
+                'data' => [
+                    'task_id' => $element->task_id,
+                    'task_info' => [
+                        'external_task_id' => $element->external_task_id,
+                    ],
+                    'task_status' => $element->task_status,
+                    'created_at' => $element->created_at->timestamp * 1000,
+                    'updated_at' => $element->updated_at->timestamp * 1000,
+                ]
+            ]);
+
+        } catch (\Illuminate\Validation\ValidationException $e) {
+            return response()->json([
+                'code' => 400,
+                'message' => '参数验证失败',
+                'request_id' => uniqid(),
+                'errors' => $e->errors()
+            ], 400);
+        } catch (\Exception $e) {
+            dLog('generate')->error('创建主体失败: ' . $e->getMessage());
+            return response()->json([
+                'code' => 500,
+                'message' => '创建主体失败: ' . $e->getMessage(),
+                'request_id' => uniqid(),
+            ], 500);
+        }
+    }
+
+    /**
+     * 查询单个主体状态
+     *
+     * @param string $taskId
+     * @param Request $request
+     * @return JsonResponse
+     */
+    public function getElementStatus(string $taskId, Request $request): JsonResponse
+    {
+        try {
+            $externalTaskId = $request->get('external_task_id');
+            
+            // 先查询本地数据库
+            $query = AIElement::where('task_id', $taskId);
+            if ($externalTaskId) {
+                $query->orWhere('external_task_id', $externalTaskId);
+            }
+            
+            $localElement = $query->first();
+            
+            if (!$localElement) {
+                return response()->json([
+                    'code' => 404,
+                    'message' => '主体任务不存在',
+                    'request_id' => uniqid(),
+                ], 404);
+            }
+
+            // 如果任务还在处理中,查询最新状态
+            if ($localElement->task_status === AIElement::STATUS_PROCESSING) {
+                $apiResult = $this->aiVideoGenerationService->queryElementStatus($taskId, $externalTaskId);
+                if ($apiResult['success'] && isset($apiResult['data'])) {
+                    $localElement->refresh(); // 重新加载数据
+                }
+            }
+
+            // 构建响应数据
+            $responseData = [
+                'task_id' => $localElement->task_id,
+                'task_status' => $localElement->task_status,
+                'task_status_msg' => $localElement->task_status_msg,
+                'task_info' => [
+                    'external_task_id' => $localElement->external_task_id,
+                ],
+                'created_at' => $localElement->created_at->timestamp * 1000,
+                'updated_at' => $localElement->updated_at->timestamp * 1000,
+            ];
+
+            // 如果任务成功,添加主体结果信息
+            if ($localElement->task_status === AIElement::STATUS_SUCCEED && $localElement->element_id) {
+                $responseData['task_result'] = [
+                    'elements' => [
+                        [
+                            'element_id' => $localElement->element_id,
+                            'element_name' => $localElement->element_name,
+                            'element_description' => $localElement->element_description,
+                            'reference_type' => $localElement->reference_type,
+                            'element_image_list' => $localElement->element_image_list,
+                            'element_video_list' => $localElement->element_video_list,
+                            'element_voice_info' => $localElement->element_voice_info,
+                            'tag_list' => $localElement->tag_list,
+                            'owned_by' => $localElement->owned_by,
+                            'status' => $localElement->status,
+                        ]
+                    ]
+                ];
+                $responseData['final_unit_deduction'] = $localElement->final_unit_deduction;
+            }
+
+            return response()->json([
+                'code' => 0,
+                'message' => 'success',
+                'request_id' => uniqid(),
+                'data' => $responseData
+            ]);
+
+        } catch (\Exception $e) {
+            dLog('generate')->error('查询主体状态失败: ' . $e->getMessage());
+            return response()->json([
+                'code' => 500,
+                'message' => '查询主体状态失败: ' . $e->getMessage(),
+                'request_id' => uniqid(),
+            ], 500);
+        }
+    }
+
+    /**
+     * 查询所有主体列表(包含自定义和官方)
+     *
+     * @param Request $request
+     * @return JsonResponse
+     */
+    public function getElementsList(Request $request): JsonResponse
+    {
+        try {
+            $pageNum = (int)$request->get('pageNum', 1);
+            $pageSize = (int)$request->get('pageSize', 30);
+            $type = $request->get('type', 'all'); // all, custom, official
+            
+            // 限制分页参数范围
+            $pageNum = max(1, min($pageNum, 1000));
+            $pageSize = max(1, min($pageSize, 500));
+
+            $customElements = [];
+            $officialElements = [];
+
+            // 查询自定义主体
+            if (in_array($type, ['all', 'custom'])) {
+                $customResult = $this->aiVideoGenerationService->queryCustomElementsList($pageNum, $pageSize);
+                if ($customResult['success']) {
+                    $customElements = $customResult['data'];
+                }
+            }
+
+            // 查询官方主体
+            if (in_array($type, ['all', 'official'])) {
+                $officialResult = $this->aiVideoGenerationService->queryOfficialElementsList($pageNum, $pageSize);
+                if ($officialResult['success']) {
+                    $officialElements = $officialResult['data'];
+                }
+            }
+
+            // 合并结果
+            $allElements = array_merge($customElements, $officialElements);
+
+            return response()->json([
+                'code' => 0,
+                'message' => 'success',
+                'request_id' => uniqid(),
+                'data' => $allElements
+            ]);
+
+        } catch (\Exception $e) {
+            dLog('generate')->error('查询主体列表失败: ' . $e->getMessage());
+            return response()->json([
+                'code' => 500,
+                'message' => '查询主体列表失败: ' . $e->getMessage(),
+                'request_id' => uniqid(),
+            ], 500);
+        }
+    }
+
+    /**
+     * 删除自定义主体
+     *
+     * @param Request $request
+     * @return JsonResponse
+     */
+    public function deleteElement(Request $request): JsonResponse
+    {
+        try {
+            // 验证请求参数
+            $validated = $request->validate([
+                'element_id' => 'required|string',
+            ]);
+
+            // 检查主体是否存在且为自定义主体
+            $element = AIElement::where('element_id', $validated['element_id'])->first();
+            if (!$element) {
+                return response()->json([
+                    'code' => 404,
+                    'message' => '主体不存在',
+                    'request_id' => uniqid(),
+                ], 404);
+            }
+
+            if ($element->owned_by === 'kling') {
+                return response()->json([
+                    'code' => 400,
+                    'message' => '不能删除官方主体',
+                    'request_id' => uniqid(),
+                ], 400);
+            }
+
+            // 调用服务层删除主体
+            $result = $this->aiVideoGenerationService->deleteElement($validated['element_id']);
+
+            if ($result['success']) {
+                return response()->json([
+                    'code' => 0,
+                    'message' => $result['message'],
+                    'request_id' => uniqid(),
+                    'data' => $result['data']
+                ]);
+            } else {
+                return response()->json([
+                    'code' => 500,
+                    'message' => $result['message'],
+                    'request_id' => uniqid(),
+                ], 500);
+            }
+
+        } catch (\Illuminate\Validation\ValidationException $e) {
+            return response()->json([
+                'code' => 400,
+                'message' => '参数验证失败',
+                'request_id' => uniqid(),
+                'errors' => $e->errors()
+            ], 400);
+        } catch (\Exception $e) {
+            dLog('generate')->error('删除主体失败: ' . $e->getMessage());
+            return response()->json([
+                'code' => 500,
+                'message' => '删除主体失败: ' . $e->getMessage(),
+                'request_id' => uniqid(),
+            ], 500);
+        }
+    }
+
+    /**
+     * 获取本地主体列表(用于内部管理)
+     *
+     * @param Request $request
+     * @return JsonResponse
+     */
+    public function getLocalElementsList(Request $request): JsonResponse
+    {
+        try {
+            // 构建筛选条件
+            $filters = [
+                'owned_by' => $request->get('owned_by'),
+                'reference_type' => $request->get('reference_type'),
+                'status' => $request->get('status'),
+                'task_status' => $request->get('task_status'),
+                'keyword' => $request->get('keyword'),
+                'page_size' => (int)$request->get('page_size', 20),
+            ];
+
+            // 移除空值
+            $filters = array_filter($filters, function($value) {
+                return $value !== null && $value !== '';
+            });
+
+            // 调用服务层获取列表
+            $elements = $this->aiVideoGenerationService->getLocalElementsList($filters);
+
+            // 转换数据格式
+            $data = $elements->map(function ($element) {
+                return [
+                    'id' => $element->id,
+                    'task_id' => $element->task_id,
+                    'external_task_id' => $element->external_task_id,
+                    'task_status' => $element->task_status,
+                    'task_status_msg' => $element->task_status_msg,
+                    'element_id' => $element->element_id,
+                    'element_name' => $element->element_name,
+                    'element_description' => $element->element_description,
+                    'reference_type' => $element->reference_type,
+                    'element_image_list' => $element->element_image_list,
+                    'element_video_list' => $element->element_video_list,
+                    'element_voice_id' => $element->element_voice_id,
+                    'element_voice_info' => $element->element_voice_info,
+                    'tag_list' => $element->tag_list,
+                    'tag_names' => $element->tag_names, // 使用模型的访问器
+                    'owned_by' => $element->owned_by,
+                    'status' => $element->status,
+                    'final_unit_deduction' => $element->final_unit_deduction,
+                    'created_at' => $element->created_at->format('Y-m-d H:i:s'),
+                    'updated_at' => $element->updated_at->format('Y-m-d H:i:s'),
+                ];
+            });
+
+            return response()->json([
+                'code' => 0,
+                'message' => 'success',
+                'request_id' => uniqid(),
+                'data' => [
+                    'data' => $data,
+                    'current_page' => $elements->currentPage(),
+                    'last_page' => $elements->lastPage(),
+                    'per_page' => $elements->perPage(),
+                    'total' => $elements->total(),
+                ]
+            ]);
+
+        } catch (\Exception $e) {
+            dLog('generate')->error('获取本地主体列表失败: ' . $e->getMessage());
+            return response()->json([
+                'code' => 500,
+                'message' => '获取本地主体列表失败: ' . $e->getMessage(),
+                'request_id' => uniqid(),
+            ], 500);
+        }
+    }
+}
+    

+ 115 - 0
app/Models/AIElement.php

@@ -0,0 +1,115 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+
+class AIElement extends Model
+{
+    use HasFactory;
+
+    protected $table = 'mp_ai_elements';
+
+    protected $fillable = [
+        'task_id',
+        'external_task_id',
+        'task_status',
+        'task_status_msg',
+        'element_id',
+        'element_name',
+        'element_description',
+        'reference_type',
+        'element_image_list',
+        'element_video_list',
+        'element_voice_id',
+        'element_voice_info',
+        'tag_list',
+        'owned_by',
+        'status',
+        'callback_url',
+        'final_unit_deduction',
+    ];
+
+    protected $casts = [
+        'element_image_list' => 'array',
+        'element_video_list' => 'array',
+        'element_voice_info' => 'array',
+        'tag_list' => 'array',
+    ];
+
+    /**
+     * 任务状态常量
+     */
+    const STATUS_SUBMITTED = 'submitted';
+    const STATUS_PROCESSING = 'processing';
+    const STATUS_SUCCEED = 'succeed';
+    const STATUS_FAILED = 'failed';
+
+    /**
+     * 参考类型常量
+     */
+    const REFERENCE_TYPE_IMAGE = 'image_refer';
+    const REFERENCE_TYPE_VIDEO = 'video_refer';
+
+    /**
+     * 主体状态常量
+     */
+    const ELEMENT_STATUS_SUCCEED = 'succeed';
+    const ELEMENT_STATUS_DELETED = 'deleted';
+
+    /**
+     * 标签映射
+     */
+    const TAG_MAP = [
+        'o_101' => '热梗',
+        'o_102' => '人物',
+        'o_103' => '动物',
+        'o_104' => '道具',
+        'o_105' => '服饰',
+        'o_106' => '场景',
+        'o_107' => '特效',
+        'o_108' => '其他',
+    ];
+
+    /**
+     * 获取标签名称
+     */
+    public function getTagNamesAttribute()
+    {
+        if (!$this->tag_list) {
+            return [];
+        }
+
+        return collect($this->tag_list)->map(function ($tag) {
+            return [
+                'tag_id' => $tag['tag_id'],
+                'tag_name' => self::TAG_MAP[$tag['tag_id']] ?? '未知',
+            ];
+        })->toArray();
+    }
+
+    /**
+     * 是否为自定义主体
+     */
+    public function isCustomElement()
+    {
+        return $this->owned_by !== 'kling';
+    }
+
+    /**
+     * 是否任务成功
+     */
+    public function isTaskSucceed()
+    {
+        return $this->task_status === self::STATUS_SUCCEED;
+    }
+
+    /**
+     * 是否主体可用
+     */
+    public function isElementAvailable()
+    {
+        return $this->status === self::ELEMENT_STATUS_SUCCEED;
+    }
+}

+ 454 - 3
app/Services/AIGeneration/AIVideoGenerationService.php

@@ -4,6 +4,7 @@ namespace App\Services\AIGeneration;
 
 
 use App\Libs\Utils;
 use App\Libs\Utils;
 use App\Models\MpGenerateVideoTask;
 use App\Models\MpGenerateVideoTask;
+use App\Models\AIElement;
 use App\Services\VolcEngineService;
 use App\Services\VolcEngineService;
 use GuzzleHttp\Client;
 use GuzzleHttp\Client;
 
 
@@ -773,7 +774,7 @@ class AIVideoGenerationService
                 $apiParams['image_list'] = $extraParams['image_list'];
                 $apiParams['image_list'] = $extraParams['image_list'];
             }
             }
             if (!empty($extraParams['element_list'])) {
             if (!empty($extraParams['element_list'])) {
-                $apiParams['element_list'] = $extraParams['element_list'];
+                $apiParams['element_list'] = json_decode($extraParams['element_list'], true);
             }
             }
             if (isset($extraParams['sound'])) {
             if (isset($extraParams['sound'])) {
                 $apiParams['sound'] = $extraParams['sound'];
                 $apiParams['sound'] = $extraParams['sound'];
@@ -783,7 +784,7 @@ class AIVideoGenerationService
             // }
             // }
             
             
             dLog('generate')->info('可灵AI Omni视频生成参数: ', $apiParams);
             dLog('generate')->info('可灵AI Omni视频生成参数: ', $apiParams);
-            
+
             // 调用可灵AI Omni API
             // 调用可灵AI Omni API
             $response = $client->post('https://api-beijing.klingai.com/v1/videos/omni-video', [
             $response = $client->post('https://api-beijing.klingai.com/v1/videos/omni-video', [
                 'headers' => [
                 'headers' => [
@@ -1000,4 +1001,454 @@ class AIVideoGenerationService
                 return 'unknown';
                 return 'unknown';
         }
         }
     }
     }
-}
+    // ==================== 主体管理相关方法 ====================
+
+    /**
+     * 创建主体
+     *
+     * @param array $params
+     * @return AIElement
+     */
+    public function createElement(array $params): AIElement
+    {
+        // 生成唯一的任务ID
+        $taskId = 'element_' . time() . '_' . uniqid();
+
+        // 创建主体记录
+        $element = AIElement::create([
+            'task_id' => $taskId,
+            'external_task_id' => $params['external_task_id'] ?? null,
+            'element_name' => $params['element_name'],
+            'element_description' => $params['element_description'],
+            'reference_type' => $params['reference_type'],
+            'element_image_list' => $params['element_image_list'] ?? null,
+            'element_video_list' => $params['element_video_list'] ?? null,
+            'element_voice_id' => $params['element_voice_id'] ?? null,
+            'tag_list' => $params['tag_list'] ?? null,
+            'callback_url' => $params['callback_url'] ?? null,
+            'task_status' => AIElement::STATUS_SUBMITTED,
+        ]);
+
+        // 提交到可灵AI API
+        $this->submitElementToKelingApi($element);
+
+        return $element;
+    }
+
+    /**
+     * 提交主体创建任务到可灵AI API
+     *
+     * @param AIElement $element
+     * @return void
+     */
+    private function submitElementToKelingApi(AIElement $element): void
+    {
+        $client = new Client(['verify' => false, 'timeout' => 120]);
+
+        try {
+            // 生成JWT token
+            $jwtToken = $this->generateKelingJwtToken();
+
+            // 构建API请求参数
+            $apiParams = [
+                'element_name' => $element->element_name,
+                'element_description' => $element->element_description,
+                'reference_type' => $element->reference_type,
+            ];
+
+            // 添加图片参考
+            if ($element->reference_type === AIElement::REFERENCE_TYPE_IMAGE && $element->element_image_list) {
+                $apiParams['element_image_list'] = $element->element_image_list;
+            }
+
+            // 添加视频参考
+            if ($element->reference_type === AIElement::REFERENCE_TYPE_VIDEO && $element->element_video_list) {
+                $apiParams['element_video_list'] = $element->element_video_list;
+            }
+
+            // 添加音色ID
+            if ($element->element_voice_id) {
+                $apiParams['element_voice_id'] = $element->element_voice_id;
+            }
+
+            // 添加标签
+            if ($element->tag_list) {
+                $apiParams['tag_list'] = $element->tag_list;
+            }
+
+            // 添加回调URL
+            if ($element->callback_url) {
+                $apiParams['callback_url'] = $element->callback_url;
+            }
+
+            // 添加外部任务ID
+            if ($element->external_task_id) {
+                $apiParams['external_task_id'] = $element->external_task_id;
+            }
+
+            dLog('generate')->info('可灵AI创建主体参数: ', $apiParams);
+
+            // 调用可灵AI创建主体API
+            $response = $client->post('https://api-beijing.klingai.com/v1/general/advanced-custom-elements', [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $jwtToken,
+                    'Content-Type' => 'application/json',
+                ],
+                'json' => $apiParams
+            ]);
+
+            $responseData = json_decode($response->getBody(), true);
+            dLog('generate')->info('可灵AI创建主体API响应: ', $responseData);
+
+            if (isset($responseData['error']) || (isset($responseData['code']) && $responseData['code'] !== 0)) {
+                // API返回错误
+                $errorMessage = $responseData['error']['message'] ?? $responseData['message'] ?? 'API Error';
+                $element->update([
+                    'task_status' => AIElement::STATUS_FAILED,
+                    'task_status_msg' => $errorMessage,
+                ]);
+            } elseif (isset($responseData['data']['task_id'])) {
+                // 任务提交成功,更新状态为处理中
+                $element->update([
+                    'task_id' => $responseData['data']['task_id'], // 使用API返回的任务ID
+                    'task_status' => AIElement::STATUS_PROCESSING,
+                    'created_at' => isset($responseData['data']['created_at']) ?
+                        date('Y-m-d H:i:s', $responseData['data']['created_at'] / 1000) : now(),
+                    'updated_at' => isset($responseData['data']['updated_at']) ?
+                        date('Y-m-d H:i:s', $responseData['data']['updated_at'] / 1000) : now(),
+                ]);
+            } else {
+                $element->update([
+                    'task_status' => AIElement::STATUS_FAILED,
+                    'task_status_msg' => '未知的API响应格式',
+                ]);
+            }
+        } catch (\Exception $e) {
+            // 记录错误
+            $element->update([
+                'task_status' => AIElement::STATUS_FAILED,
+                'task_status_msg' => 'API请求失败: ' . $e->getMessage(),
+            ]);
+            dLog('generate')->error('可灵AI创建主体API请求异常: ' . $e->getMessage());
+        }
+    }
+
+    /**
+     * 查询单个主体状态
+     *
+     * @param string $taskId
+     * @param string|null $externalTaskId
+     * @return array
+     */
+    public function queryElementStatus(string $taskId, string $externalTaskId = null): array
+    {
+        $client = new Client(['verify' => false, 'timeout' => 120]);
+
+        try {
+            // 生成JWT token
+            $jwtToken = $this->generateKelingJwtToken();
+
+            // 构建查询URL
+            $url = 'https://api-beijing.klingai.com/v1/general/advanced-custom-elements/' . $taskId;
+            $queryParams = [];
+
+            if ($externalTaskId) {
+                $queryParams['external_task_id'] = $externalTaskId;
+            }
+
+            if (!empty($queryParams)) {
+                $url .= '?' . http_build_query($queryParams);
+            }
+
+            $response = $client->get($url, [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $jwtToken,
+                    'Content-Type' => 'application/json',
+                ]
+            ]);
+
+            $responseData = json_decode($response->getBody(), true);
+            dLog('generate')->info('可灵AI查询主体状态响应: ', $responseData);
+
+            if (isset($responseData['error']) || (isset($responseData['code']) && $responseData['code'] !== 0)) {
+                return [
+                    'success' => false,
+                    'message' => $responseData['error']['message'] ?? $responseData['message'] ?? 'API查询失败',
+                    'data' => null
+                ];
+            }
+
+            // 更新本地数据库记录
+            if (isset($responseData['data'])) {
+                $this->updateLocalElementFromApi($responseData['data']);
+            }
+
+            return [
+                'success' => true,
+                'data' => $responseData['data']
+            ];
+        } catch (\Exception $e) {
+            dLog('generate')->error('可灵AI查询主体状态异常: ' . $e->getMessage());
+            return [
+                'success' => false,
+                'message' => '查询失败: ' . $e->getMessage(),
+                'data' => null
+            ];
+        }
+    }
+
+    /**
+     * 根据API响应更新本地主体记录
+     *
+     * @param array $apiData
+     * @return void
+     */
+    private function updateLocalElementFromApi(array $apiData): void
+    {
+        $element = AIElement::where('task_id', $apiData['task_id'])->first();
+        if (!$element) {
+            return;
+        }
+
+        $updateData = [
+            'task_status' => $apiData['task_status'],
+            'task_status_msg' => $apiData['task_status_msg'] ?? null,
+            'final_unit_deduction' => $apiData['final_unit_deduction'] ?? null,
+            'updated_at' => isset($apiData['updated_at']) ?
+                date('Y-m-d H:i:s', $apiData['updated_at'] / 1000) : now(),
+        ];
+
+        // 如果任务成功,更新主体信息
+        if ($apiData['task_status'] === AIElement::STATUS_SUCCEED && isset($apiData['task_result']['elements'])) {
+            $elementData = $apiData['task_result']['elements'][0];
+            $updateData = array_merge($updateData, [
+                'element_id' => $elementData['element_id'],
+                'element_voice_info' => $elementData['element_voice_info'] ?? null,
+                'owned_by' => $elementData['owned_by'] ?? 'custom',
+                'status' => $elementData['status'] ?? AIElement::ELEMENT_STATUS_SUCCEED,
+            ]);
+        }
+
+        $element->update($updateData);
+    }
+
+    /**
+     * 查询自定义主体列表
+     *
+     * @param int $pageNum
+     * @param int $pageSize
+     * @return array
+     */
+    public function queryCustomElementsList(int $pageNum = 1, int $pageSize = 30): array
+    {
+        $client = new Client(['verify' => false, 'timeout' => 120]);
+
+        try {
+            // 生成JWT token
+            $jwtToken = $this->generateKelingJwtToken();
+
+            // 构建查询URL
+            $url = 'https://api-beijing.klingai.com/v1/general/advanced-custom-elements';
+            $queryParams = [
+                'pageNum' => $pageNum,
+                'pageSize' => $pageSize,
+            ];
+
+            $url .= '?' . http_build_query($queryParams);
+
+            $response = $client->get($url, [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $jwtToken,
+                    'Content-Type' => 'application/json',
+                ]
+            ]);
+
+            $responseData = json_decode($response->getBody(), true);
+            dLog('generate')->info('可灵AI查询自定义主体列表响应: ', $responseData);
+
+            if (isset($responseData['error']) || (isset($responseData['code']) && $responseData['code'] !== 0)) {
+                return [
+                    'success' => false,
+                    'message' => $responseData['error']['message'] ?? $responseData['message'] ?? 'API查询失败',
+                    'data' => []
+                ];
+            }
+
+            // 更新本地数据库记录
+            if (isset($responseData['data']) && is_array($responseData['data'])) {
+                foreach ($responseData['data'] as $elementData) {
+                    $this->updateLocalElementFromApi($elementData);
+                }
+            }
+
+            return [
+                'success' => true,
+                'data' => $responseData['data'] ?? []
+            ];
+        } catch (\Exception $e) {
+            dLog('generate')->error('可灵AI查询自定义主体列表异常: ' . $e->getMessage());
+            return [
+                'success' => false,
+                'message' => '查询失败: ' . $e->getMessage(),
+                'data' => []
+            ];
+        }
+    }
+
+    /**
+     * 查询官方主体列表
+     *
+     * @param int $pageNum
+     * @param int $pageSize
+     * @return array
+     */
+    public function queryOfficialElementsList(int $pageNum = 1, int $pageSize = 30): array
+    {
+        $client = new Client(['verify' => false, 'timeout' => 120]);
+
+        try {
+            // 生成JWT token
+            $jwtToken = $this->generateKelingJwtToken();
+
+            // 构建查询URL
+            $url = 'https://api-beijing.klingai.com/v1/general/advanced-presets-elements';
+            $queryParams = [
+                'pageNum' => $pageNum,
+                'pageSize' => $pageSize,
+            ];
+
+            $url .= '?' . http_build_query($queryParams);
+
+            $response = $client->get($url, [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $jwtToken,
+                    'Content-Type' => 'application/json',
+                ]
+            ]);
+
+            $responseData = json_decode($response->getBody(), true);
+            dLog('generate')->info('可灵AI查询官方主体列表响应: ', $responseData);
+
+            if (isset($responseData['error']) || (isset($responseData['code']) && $responseData['code'] !== 0)) {
+                return [
+                    'success' => false,
+                    'message' => $responseData['error']['message'] ?? $responseData['message'] ?? 'API查询失败',
+                    'data' => []
+                ];
+            }
+
+            return [
+                'success' => true,
+                'data' => $responseData['data'] ?? []
+            ];
+        } catch (\Exception $e) {
+            dLog('generate')->error('可灵AI查询官方主体列表异常: ' . $e->getMessage());
+            return [
+                'success' => false,
+                'message' => '查询失败: ' . $e->getMessage(),
+                'data' => []
+            ];
+        }
+    }
+
+    /**
+     * 删除自定义主体
+     *
+     * @param string $elementId
+     * @return array
+     */
+    public function deleteElement(string $elementId): array
+    {
+        $client = new Client(['verify' => false, 'timeout' => 120]);
+
+        try {
+            // 生成JWT token
+            $jwtToken = $this->generateKelingJwtToken();
+
+            // 构建API请求参数
+            $apiParams = [
+                'element_id' => $elementId,
+            ];
+
+            dLog('generate')->info('可灵AI删除主体参数: ', $apiParams);
+
+            // 调用可灵AI删除主体API
+            $response = $client->post('https://api-beijing.klingai.com/v1/general/delete-elements', [
+                'headers' => [
+                    'Authorization' => 'Bearer ' . $jwtToken,
+                    'Content-Type' => 'application/json',
+                ],
+                'json' => $apiParams
+            ]);
+
+            $responseData = json_decode($response->getBody(), true);
+            dLog('generate')->info('可灵AI删除主体API响应: ', $responseData);
+
+            if (isset($responseData['error']) || (isset($responseData['code']) && $responseData['code'] !== 0)) {
+                // API返回错误
+                return [
+                    'success' => false,
+                    'message' => $responseData['error']['message'] ?? $responseData['message'] ?? 'API Error'
+                ];
+            } else {
+                // 删除成功,更新本地记录状态
+                AIElement::where('element_id', $elementId)->update([
+                    'status' => AIElement::ELEMENT_STATUS_DELETED,
+                    'updated_at' => now(),
+                ]);
+
+                return [
+                    'success' => true,
+                    'message' => '主体删除成功',
+                    'data' => $responseData['data'] ?? []
+                ];
+            }
+        } catch (\Exception $e) {
+            dLog('generate')->error('可灵AI删除主体API请求异常: ' . $e->getMessage());
+            return [
+                'success' => false,
+                'message' => 'API请求失败: ' . $e->getMessage()
+            ];
+        }
+    }
+
+    /**
+     * 获取本地主体列表(包含自定义和官方)
+     *
+     * @param array $filters
+     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
+     */
+    public function getLocalElementsList(array $filters = [])
+    {
+        $query = AIElement::query();
+
+        // 筛选条件
+        if (isset($filters['owned_by'])) {
+            $query->where('owned_by', $filters['owned_by']);
+        }
+
+        if (isset($filters['reference_type'])) {
+            $query->where('reference_type', $filters['reference_type']);
+        }
+
+        if (isset($filters['status'])) {
+            $query->where('status', $filters['status']);
+        }
+
+        if (isset($filters['task_status'])) {
+            $query->where('task_status', $filters['task_status']);
+        }
+
+        // 搜索关键词
+        if (isset($filters['keyword']) && !empty($filters['keyword'])) {
+            $query->where(function ($q) use ($filters) {
+                $q->where('element_name', 'like', '%' . $filters['keyword'] . '%')
+                  ->orWhere('element_description', 'like', '%' . $filters['keyword'] . '%');
+            });
+        }
+
+        return $query->orderBy('created_at', 'desc')
+                    ->paginate($filters['page_size'] ?? 20);
+    }
+}
+    

+ 7 - 0
routes/api.php

@@ -115,6 +115,13 @@ Route::group(['middleware' => ['bindToken', 'bindExportToken', 'checkLogin']], f
         // 可灵Omni视频生成路由
         // 可灵Omni视频生成路由
         Route::post('video/createKelingTask', [VideoGenerationController::class, 'createKelingTask']);
         Route::post('video/createKelingTask', [VideoGenerationController::class, 'createKelingTask']);
         Route::get('video/keling/taskStatus/{taskId}', [VideoGenerationController::class, 'getKelingTaskStatus']);
         Route::get('video/keling/taskStatus/{taskId}', [VideoGenerationController::class, 'getKelingTaskStatus']);
+        
+        // 主体管理相关路由
+        Route::post('element/create', [VideoGenerationController::class, 'createElement']);
+        Route::get('element/status/{taskId}', [VideoGenerationController::class, 'getElementStatus']);
+        Route::get('element/list', [VideoGenerationController::class, 'getElementsList']);
+        Route::post('element/delete', [VideoGenerationController::class, 'deleteElement']);
+        Route::get('element/local/list', [VideoGenerationController::class, 'getLocalElementsList']);
     });
     });
     
     
 });
 });