success('请求成功'); } // 向量引擎配置 private $baseUrl = "https://api.vectorengine.ai/v1"; private $apiKey = "sk-P877pnXMk2erRS2an7qEa3Kdb3rIb7JVAWZ39lhA8HeN71gZ"; // 从控制台获取 private $timeout = 120; // 超时时间(秒),视频生成需要更长 /** * 文生图接口 * POST /api/index/textToImage * 参数: prompt (string) 提示词 */ public function textToImage() { $params = $this->request->param(); if (empty($params)) { $this->error('提示词不能为空'); } $data = [ "model" => "gemini-3-pro-image-preview", // 可替换为 dall-e-3 等 "prompt" => $params['prompt'], "n" => 1, "size" => "1024x1024" ]; $result = $this->requestVectorEngine("/images/generations", $data); if ($result['code'] === 0) { $this->success('生成成功', $result['data']); } else { $this->error($result['msg'], $result['data']); } } /** * 图生文接口 * POST /api/index/imageToText * 参数: image_url (string) 公网图片URL, prompt (string) 提问指令 */ public function imageToText() { $imageUrl = $this->request->post('image_url'); $prompt = $this->request->post('prompt', '描述这张图片的内容'); if (empty($imageUrl)) { $this->error('图片URL不能为空'); } $data = [ "model" => "gpt-4-vision-preview", "messages" => [ [ "role" => "user", "content" => [ ["type" => "text", "text" => $prompt], ["type" => "image_url", "image_url" => ["url" => $imageUrl]] ] ] ], "max_tokens" => 1000 ]; $result = $this->requestVectorEngine("/chat/completions", $data); if ($result['code'] === 0) { $this->success('识别成功', $result['data']); } else { $this->error($result['msg'], $result['data']); } } /** * 文生视频接口 * POST /api/index/textToVideo * 参数: prompt (string) 提示词, duration (int) 时长(秒), resolution (string) 分辨率 */ public function textToVideo() { $prompt = $this->request->post('prompt'); $duration = $this->request->post('duration', 5); $resolution = $this->request->post('resolution', '720p'); if (empty($prompt)) { $this->error('提示词不能为空'); } $data = [ "model" => "kling-1.6", // 可替换为 seedance-2.0 等 "prompt" => $prompt, "duration" => (int)$duration, "resolution" => $resolution ]; $result = $this->requestVectorEngine("/videos/generations", $data); if ($result['code'] === 0) { $this->success('生成成功', $result['data']); } else { $this->error($result['msg'], $result['data']); } } /** * 封装向量引擎通用CURL请求 */ private function requestVectorEngine($endpoint, $data) { $url = $this->baseUrl . $endpoint; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ "Content-Type: application/json", "Authorization: Bearer " . $this->apiKey ], CURLOPT_SSL_VERIFYPEER => false, // 生产环境建议开启 CURLOPT_TIMEOUT => $this->timeout ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { return ['code' => -1, 'msg' => '请求失败: ' . $error, 'data' => null]; } if ($httpCode !== 200) { return ['code' => $httpCode, 'msg' => 'API返回异常', 'data' => json_decode($response, true)]; } return ['code' => 0, 'msg' => '成功', 'data' => json_decode($response, true)]; } public function GetTxtToImg(){ $params = $this->request->param(); $prompt = $params['prompt'];//提示词 $model = $params['model'];//模型 $size = $params['size'];//尺寸 // 调用AI生成图片 $aiGateway = new AIGatewayService(); $res = $aiGateway->callDalleApi($prompt, $model, $size); // 提取base64图片数据 $imageData = ''; $imageType = 'png'; // 默认图片类型 if (isset($res['candidates'][0]['content']['parts'][0]['text'])) { $text_content = $res['candidates'][0]['content']['parts'][0]['text']; // 匹配base64图片数据和类型 if (preg_match('/data:image\/([a-zA-Z0-9]+);base64,([^\s]+)/', $text_content, $matches)) { $imageType = strtolower($matches[1]); $base64Data = $matches[2]; // 解码base64数据 $imageData = base64_decode($base64Data); } } if (!$imageData) { return json(['code' => 1, 'msg' => '图片生成失败,未找到有效图片数据']); } // 创建保存目录(public/uploads/log/YYYY-MM/) $yearMonth = date('Ym'); $saveDir = ROOT_PATH . 'public/uploads/ceshi/' . $yearMonth . '/'; if (!is_dir($saveDir)) { mkdir($saveDir, 0755, true); } // 生成唯一文件名 $fileName = uniqid() . '.' . $imageType; $filePath = $saveDir . $fileName; // 保存图片到本地文件 if (file_put_contents($filePath, $imageData) === false) { return json(['code' => 1, 'msg' => '图片保存失败']); } // 生成前端可访问的URL $imageUrl = '/uploads/ceshi/' . $yearMonth . '/' . $fileName; // 返回标准JSON响应 return json([ 'code' => 0, 'msg' => '图片生成成功', 'data' => [ 'url' => $imageUrl ] ]); } /** * 学生端文生视频接口 - 用于生成视频 */ public function GetTxtToVideo(){ $aiGateway = new AIGatewayService(); $apiUrl = $aiGateway->config['videos']['api_url']; $apiKey = $aiGateway->config['videos']['api_key']; // 获取并验证参数 $params = $this->request->param(); // echo "
";
        //    print_r($params);
        //    echo "
";die;

        $postData = [
            'prompt' => $params['prompt'],
            'model' => $params['model'],
            'seconds' => $params['duration'],
            'size' => $params['size'],
        ];

        // 初始化CURL
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $apiUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Authorization: Bearer ' . $apiKey
        ]);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        curl_setopt($ch, CURLOPT_HEADER, true); // 获取响应头
        curl_setopt($ch, CURLOPT_VERBOSE, true); // 启用详细输出以进行调试
        // 创建临时文件来捕获详细的cURL输出
        $verbose = fopen('php://temp', 'w+');
        curl_setopt($ch, CURLOPT_STDERR, $verbose);
        // 执行请求
        $response = curl_exec($ch);
        //HTTP状态码
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        // 获取详细的cURL调试信息
        rewind($verbose);
        //CURL调试信息
        $verboseLog = stream_get_contents($verbose);
        fclose($verbose);
        // 分离头部和主体
        $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        //响应头部
        $header = substr($response, 0, $header_size);
        //响应主体
        $body = substr($response, $header_size);
        // 检查CURL错误
        $curlError = curl_error($ch);
        curl_close($ch);

        $responseData = json_decode($body, true);

        // 检查API是否返回了错误信息
        if (isset($responseData['error'])) {
            $errorMessage = isset($responseData['error']['message']) ? $responseData['error']['message'] : 'API请求失败';
            return json([
                'code' => 1,
                'msg' => '视频生成请求失败',
                'data' => [
                    'error_type' => isset($responseData['error']['type']) ? $responseData['error']['type'] : 'unknown',
                    'error_code' => isset($responseData['error']['code']) ? $responseData['error']['code'] : 'unknown',
                    'error_message' => $errorMessage
                ]
            ]);
        }

        // 检查是否有自定义错误格式
        if (isset($responseData['code']) && $responseData['code'] === 'fail_to_fetch_task' && isset($responseData['message'])) {
            return json([
                'code' => 1,
                'msg' => '视频生成请求失败',
                'data' => [
                    'error_code' => $responseData['code'],
                    'error_message' => $responseData['message']
                ]
            ]);
        }

        // 检查是否存在id字段
        if (!isset($responseData['id'])) {
            return json([
                'code' => 1,
                'msg' => '无法获取视频ID',
                'data' => [
                    'response_data' => $responseData,
                    'http_code' => $httpCode
                ]
            ]);
        }

        // 1. 先检查视频状态
        $statusUrl = 'https://chatapi.onechats.ai/v1/videos/' . $responseData['id'];
        $statusData = $this->fetchVideoStatus($statusUrl, $apiKey);

        // 检查视频状态
        if ($statusData['status'] !== 'completed') {
            return json([
                'code' => 202,
                'msg' => '视频尚未生成完成',
                'data' => [
                    'video_id' => $responseData['id'],
                    'status' => $statusData['status'],
                    'progress' => $statusData['progress'],
                    'created_at' => $statusData['created_at'],
                    'message' => '请稍后再试,视频仍在' . ($statusData['status'] === 'queued' ? '排队中' : '处理中')
                ]
            ]);
        }

        // 2. 视频生成完成,准备下载
        $apiUrl = 'https://chatapi.onechats.ai/v1/videos/' . $responseData['id'] . '/content';

        // 获取可选的variant参数
        $variant = $this->request->get('variant', '');
        if (!empty($variant)) {
            $apiUrl .= '?variant=' . urlencode($variant);
        }

        // 创建保存目录
        $saveDir = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'videos' . DS . date('Ymd');
        if (!is_dir($saveDir)) {
            mkdir($saveDir, 0755, true);
        }

        // 生成唯一文件名
        $filename = $responseData['id'] . '.mp4';
        $localPath = DS . 'uploads' . DS . 'videos' . DS . date('Ymd') . DS . $filename;
        $fullPath = $saveDir . DS . $filename;

        // 3. 下载视频
        $videoData = $this->downloadVideo($apiUrl, $apiKey);

        // 4. 保存视频文件
        if (file_put_contents($fullPath, $videoData) === false) {
            throw new Exception('视频保存失败');
        }

        // 确保路径使用正斜杠,并只保存相对路径部分
        $localPath = str_replace('\\', '/', $localPath);
        // 移除开头的斜杠,确保路径格式为uploads/videos/...
        $savePath = ltrim($localPath, '/');

        // 返回成功响应
        return json([
            'code' => 0,
            'msg' => '视频下载成功',
            'data' => [
                'video_id' => $responseData['id'],
                'web_url' => $savePath
            ]
        ]);
    }

    /**
     * 学生端视频状态查询接口 - 用于通过video_id查询视频状态
     */
    public function Getvideo_id(){
        // 获取并验证参数
        $params = $this->request->param();
        $videoId = $params['video_id'] ?? '';

        // 验证video_id参数
        if (empty($videoId)) {
            return json([
                'code' => 1,
                'msg' => '缺少必要参数:video_id',
                'data' => []
            ]);
        }

        $aiGateway = new AIGatewayService();
//        $apiUrl = $aiGateway->config['videos']['api_url'];
        $apiKey = $aiGateway->config['videos']['api_key'];

        $apiUrl = 'https://chatapi.onechats.ai/v1/videos/' . $videoId;

        // 先检查本地是否已经有该视频
        $localVideoPath = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'videos' . DS . date('Ymd') . DS . $videoId . '.mp4';
        if (file_exists($localVideoPath)) {
            // 视频已存在本地,直接返回
            $webPath = '/uploads/videos/' . date('Ymd') . '/' . $videoId . '.mp4';
            return json([
                'code' => 0,
                'msg' => '视频下载成功',
                'data' => [
                    'video_id' => $videoId,
                    'web_url' => substr($webPath, 1) // 移除开头的斜杠
                ]
            ]);
        }

        // 检查视频状态
        $statusData = $this->fetchVideoStatus($apiUrl, $apiKey);

        // 检查视频状态
        if ($statusData['status'] !== 'completed') {
            return json([
                'code' => 202,
                'msg' => '视频尚未生成完成',
                'data' => [
                    'video_id' => $videoId,
                    'status' => $statusData['status'],
                    'progress' => isset($statusData['progress']) ? $statusData['progress'] : 0,
                    'created_at' => $statusData['created_at'],
                    'message' => '请稍后再试,视频仍在' . ($statusData['status'] === 'queued' ? '排队中' : '处理中')
                ]
            ]);
        }

        // 视频生成完成,下载视频
        $downloadUrl = 'https://chatapi.onechats.ai/v1/videos/' . $videoId . '/content';

        // 获取可选的variant参数
        $variant = $this->request->get('variant', '');
        if (!empty($variant)) {
            $downloadUrl .= '?variant=' . urlencode($variant);
        }

        // 创建保存目录
        $saveDir = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'videos' . DS . date('Ymd');
        if (!is_dir($saveDir)) {
            mkdir($saveDir, 0755, true);
        }

        // 生成唯一文件名
        $filename = $videoId . '.mp4';
        $localPath = DS . 'uploads' . DS . 'videos' . DS . date('Ymd') . DS . $filename;
        $fullPath = $saveDir . DS . $filename;

        // 下载视频
        $videoData = $this->downloadVideo($downloadUrl, $apiKey);

        // 保存视频文件
        if (file_put_contents($fullPath, $videoData) === false) {
            throw new Exception('视频保存失败');
        }

        // 确保路径使用正斜杠,并只保存相对路径部分
        $localPath = str_replace(DIRECTORY_SEPARATOR, '/', $localPath);
        // 移除开头的斜杠,确保路径格式为uploads/videos/...
        $savePath = ltrim($localPath, '/');

        // 返回成功响应
        return json([
            'code' => 0,
            'msg' => '视频下载成功',
            'data' => [
                'video_id' => $videoId,
                'web_url' => $savePath
            ]
        ]);
    }

    /**
     * 九个分镜头生成流程
     * 模型:gemini-3-pro-image-preview
     * 说明:
     * 第一步:(提示词 + 原始图片 = 九个分镜头图片) 或 (提示词 = 九个分镜头图片)
     * 第二步:使用九个分镜头图进行裁剪单图生成连贯视频
     * 第三步:在通过分镜头视频拼接成一个完整的视频(列如每个分镜头视频为8秒,九个为72秒形成完整视频)
     */
    public function Get_txttonineimg()
    {
        // 发起接口请求
//        $apiUrl = 'https://chatapi.onechats.ai/v1beta/models/gemini-3-pro-image-preview:generateContent';
        $apiUrl = 'https://chatapi.onechats.ai/v1beta/models/gemini-3-pro-image-preview:streamGenerateContent';
        $apiKey = '';

//        $params = $this->request->param();
        $prompt = '生成一个苹果(九个分镜头图片)';

        $requestData = [
            "contents" => [
                [
                    "role" => "user",
                    "parts" => [
                        ["text" => $prompt]
                    ]
                ]
            ],
            "generationConfig" => [
                "responseModalities" => ["TEXT", "IMAGE"],
                "imageConfig" => [
                    "aspectRatio" => "1:1"
                ]
            ]
        ];

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $apiUrl);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($requestData, JSON_UNESCAPED_UNICODE));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $apiKey
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 开发环境临时关闭SSL验证
        curl_setopt($ch, CURLOPT_TIMEOUT, 60); // 生成图片超时时间(建议60秒)

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

        $error = curl_error($ch);
        curl_close($ch);
        $res = json_decode($response, true);

        // 构建URL路径(使用正斜杠)
        $url_path = '/uploads/txtnewimg/';
        // 构建物理路径(使用正斜杠确保统一格式)
        $save_path = ROOT_PATH . 'public' . '/' . 'uploads' . '/' . 'txtnewimg'  . '/';
        // 移除ROOT_PATH中可能存在的反斜杠,确保统一使用正斜杠
        $save_path = str_replace('\\', '/', $save_path);
        // 自动创建文件夹(如果不存在)
        if (!is_dir($save_path)) {
            mkdir($save_path, 0755, true);
        }

        // 提取base64图片数据
        $text_content = $res['candidates'][0]['content']['parts'][0]['inlineData']['data'];
        $str = 'data:image/jpeg;base64,';
        $text_content = $str. $text_content;
        // 匹配base64图片数据
        preg_match('/data:image\/(png|jpg|jpeg);base64,([^"]+)/', $text_content, $matches);
        if (empty($matches)) {
            return '未找到图片数据';
        }
        $image_type = $matches[1];
        $base64_data = $matches[2];

        // 解码base64数据
        $image_data = base64_decode($base64_data);
        if ($image_data === false) {
            return '图片解码失败';
        }

        // 生成唯一文件名(包含扩展名)
        $file_name = uniqid() . '.' . $image_type;
        $full_file_path = $save_path . $file_name;

        // 保存图片到文件系统
        if (!file_put_contents($full_file_path, $image_data)) {
            return '图片保存失败';
        }
        // 生成数据库存储路径(使用正斜杠格式)
        $db_img_path = $url_path . $file_name;
        return  $db_img_path;
    }

}