buildText2TextGeminiFlash($prompt); break; case $status_val === '文生文' && $model === 'gpt-4': $data = $this->buildText2TextGpt4($prompt); break; // 文生图 case $status_val === '文生图' && $model === 'dall-e-3': $data = $this->buildText2ImageDallE3($prompt, $size); break; case $status_val === '文生图' && $model === 'gemini-3-pro-preview': $data = $this->buildText2ImageGemini3Pro($prompt, $size); break; // 图生文 case $status_val === '图生文' && $model === 'gemini-3-pro-preview': $data = $this->buildImage2TextGemini3Pro($prompt, $product_base64Data, $product_mimeType); break; // 图生图 case $status_val === '图生图' && $model === 'gemini-3-pro-image-preview': $data = $this->buildImage2ImageGemini3ProImage($prompt, $size, $product_base64Data, $product_mimeType, $template_base64Data, $template_mimeType); break; case $status_val === '图生图' && $model === 'gemini-3.1-flash-image-preview': $data = $this->buildImage2ImageGemini31Flash($prompt, $size, $product_base64Data, $product_mimeType, $template_base64Data, $template_mimeType); break; // 未匹配的组合 default: throw new \Exception("未配置模型+任务类型组合: {$model}({$status_val})"); } // 3. 统一调用 API(主方法只做分发,不耦合具体逻辑) return $this->callApi($data, $model); } // -------------------------- 细分方法:按「任务类型+模型」拆分 -------------------------- /** * 文生文 - gemini_flash 模型 */ private function buildText2TextGeminiFlash(string $prompt): array { return [ "contents" => [ [ "role" => "user", "parts" => [["text" => $prompt]] ] ], "generationConfig" => [ "responseModalities" => ["TEXT"], "maxOutputTokens" => 1024, "temperature" => 0.7, "language" => "zh-CN" ] ]; } /** * 文生文 - gpt-4 模型 */ private function buildText2TextGpt4(string $prompt): array { return [ 'model' => 'gpt-4', 'messages' => [['role' => 'user', 'content' => $prompt]], 'temperature' => 0.7, 'max_tokens' => 1024 ]; } /** * 文生图 - dall-e-3 模型 */ private function buildText2ImageDallE3(string $prompt, string $size): array { return [ 'group' => 'OpenAI', 'prompt' => $prompt, 'model' => 'dall-e-3', 'n' => 1, 'size' => $size, 'quality' => 'hd', 'style' => 'vivid', 'response_format' => 'url', ]; } /** * 文生图 - gemini-3-pro-preview 模型 */ private function buildText2ImageGemini3Pro(string $prompt, string $size): array { $supportedAspectRatios = ['1:1', '4:3', '3:4', '16:9', '9:16']; $aspectRatio = (in_array($size, $supportedAspectRatios)) ? $size : '1:1'; return [ "model" => "gemini-3-pro-preview", "prompt" => $prompt, "size" => $aspectRatio, "n" => 1, "quality" => "standard", "response_format" => "url" ]; } /** * 图生文 - gemini-3-pro-preview 模型 */ private function buildImage2TextGemini3Pro(string $prompt, string $productBase64, string $productMimeType): array { return [ "contents" => [ [ "role" => "user", "parts" => [ ["inlineData" => ["mimeType" => $productMimeType, "data" => $productBase64]], ["text" => $prompt] ] ] ], "generationConfig" => [ "responseModalities" => ["TEXT"], "maxOutputTokens" => 1000, "temperature" => 0.7, "language" => "zh-CN" ] ]; } /** * 图生图 - gemini-3-pro-image-preview 模型 */ private function buildImage2ImageGemini3ProImage( string $prompt, string $size, string $productBase64, string $productMimeType, string $templateBase64, string $templateMimeType ): array { // 尺寸转标准比例 if (!empty($size) && strpos($size, 'x') !== false) { $parts = explode('x', trim($size), 2); if (count($parts) == 2) { $w = (int)$parts[0]; $h = (int)$parts[1]; if ($w > 0 && $h > 0) { $ratio = $w / $h; $standard = [['1:1', 1], ['4:3', 4/3], ['3:4', 3/4], ['16:9', 16/9], ['9:16', 9/16]]; $minDiff = PHP_FLOAT_MAX; foreach ($standard as $r) { $diff = abs($ratio - $r[1]); if ($diff < $minDiff) { $minDiff = $diff; $size = $r[0]; } } } } } return [ 'contents' => [ [ 'role' => 'user', 'parts' => [ ['text' => $prompt], ['inline_data' => ['mime_type' => $productMimeType, 'data' => $productBase64]], ['inline_data' => ['mime_type' => $templateMimeType, 'data' => $templateBase64]] ] ] ], 'generationConfig' => [ 'responseModalities' => ['IMAGE'], 'imageConfig' => ['aspectRatio' => $size, 'imageSize' => '1k'] ] ]; } /** * 图生图 - gemini-3.1-flash-image-preview 模型 */ private function buildImage2ImageGemini31Flash( string $prompt, string $size, string $productBase64, string $productMimeType, string $templateBase64, string $templateMimeType ): array { return [ 'model' => 'gemini-3.1-flash-image-preview', 'messages' => [ [ 'role' => 'user', 'content' => [ ['type' => 'text', 'text' => $prompt], [ 'type' => 'image_url', 'image_url' => ['url' => 'data:' . $productMimeType . ';base64,' . $productBase64] ], [ 'type' => 'image_url', 'image_url' => ['url' => 'data:' . $templateMimeType . ';base64,' . $templateBase64] ] ] ] ], 'response_modalities' => ['image'], 'image_config' => [ 'aspect_ratio' => $size, 'quality' => 'high', 'width' => '850', 'height' => '1133' ], 'temperature' => 0.3, 'top_p' => 0.8, 'max_tokens' => 2048 ]; } // /** // * 根据模型与任务类型构建 API 请求体 // * @param string $status_val 任务类型:图生文、文生文、文生图、图生图 // * @param string $model 模型名,如 gpt-4、gemini_flash、dall-e-3 等 // * @param string $prompt 提示词 // * @param string $size 尺寸或比例,如 850x1133 或 9:16(文生图/图生图用) // * @param string $product_base64Data 产品图 base64(图生图用) // * @param string $product_mimeType 产品图 MIME 类型(图生图用) // * @param string $template_base64Data 模板图 base64(图生图用) // * @param string $template_mimeType 模板图 MIME 类型(图生图用) // * @return array API 响应 // */ // // public function buildRequestData($status_val,$model,$prompt,$size='',$product_base64Data='',$product_mimeType='',$template_base64Data='',$template_mimeType='') // { // //判断使用哪个模型、在判断此模型使用类型 // if ($model === 'gemini_flash') { // if($status_val == '文生文'){ // // 使用Gemini API的正确参数格式 // $data = [ // "contents" => [ // [ // "role" => "user", // "parts" => [ // ["text" => $prompt] // ] // ] // ], // "generationConfig" => [ // "responseModalities" => ["TEXT"], // "maxOutputTokens" => 1024, // "temperature" => 0.7, // "language" => "zh-CN" // ] // ]; // return $this->callApi($data,$model); // } // }else if ($model === 'gpt-4') { // if($status_val == '文生文'){ // // 使用OpenAI API格式 // $data = [ // 'model' => $model, // 'messages' => [ // ['role' => 'user', 'content' => $prompt] // ], // 'temperature' => 0.7, // 'max_tokens' => 1024 // ]; // return $this->callApi($data,$model); // } // }else if($model == 'dall-e-3'){ // if($status_val == '文生图'){ // $data = [ // 'group' => 'OpenAI', // 'prompt' => $prompt, // 'model' => $model, // 'n' => 1, // 'size' => $size, // 'quality' => 'hd', // 'style' => 'vivid', // 'response_format' => 'url', // ]; // return $this->callApi($data,$model); // } // }else if($model == 'gemini-3-pro-image-preview'){ // if($status_val == '图生图'){ // // 宽高(850x1133)转标准比例(如3:4),模型需 aspectRatio 非尺寸 // if (!empty($size) && strpos($size, 'x') !== false) { // $parts = explode('x', trim($size), 2); // if (count($parts) == 2) { // $w = (int)$parts[0]; // $h = (int)$parts[1]; // if ($w > 0 && $h > 0) { // $ratio = $w / $h; // $standard = [['1:1', 1], ['4:3', 4/3], ['3:4', 3/4], ['16:9', 16/9], ['9:16', 9/16]]; // $minDiff = PHP_FLOAT_MAX; // foreach ($standard as $r) { // $diff = abs($ratio - $r[1]); // if ($diff < $minDiff) { // $minDiff = $diff; // $size = $r[0]; // } // } // } // } // } // $data = [ // 'contents' => [ // [ // 'role' => 'user', // 'parts' => [ // ['text' => $prompt], // [ // 'inline_data' => [ // 'mime_type' => $product_mimeType, // 'data' => $product_base64Data // ] // ], // [ // 'inline_data' => [ // 'mime_type' => $template_mimeType, // 'data' => $template_base64Data // ] // ] // ] // ] // ], // 'generationConfig' => [ // 'responseModalities' => ['IMAGE'], // 'imageConfig' => [ // 'aspectRatio' => $size, // 'imageSize' => '1k' // ] // ] // ]; // return $this->callApi($data,$model); // } // }else if($model == 'gemini-3-pro-preview'){ // if($status_val == '图生文'){ // $data = [ // "contents" => [ // [ // "role" => "user", // "parts" => [ // ["inlineData" => [ // "mimeType" => $product_mimeType, // "data" => $product_base64Data // ]], // ["text" => $prompt] // ] // ] // ], // "generationConfig" => [ // "responseModalities" => ["TEXT"], // "maxOutputTokens" => 1000, // "temperature" => 0.7, // "language" => "zh-CN" // ] // ]; // return $this->callApi($data,$model); // }else if($status_val == '文生图'){ // // 支持的宽高比(与官方保持一致) // $supportedAspectRatios = ['1:1', '4:3', '3:4', '16:9', '9:16']; // // // 解析并验证宽高比 // $size = $_POST['size'] ?? ''; // if (!empty($size) && strpos($size, ':') !== false && in_array($size, $supportedAspectRatios)) { // $aspectRatio = $size; // } else { // $aspectRatio = "1:1"; // 默认宽高比 // } // // // 构建符合 /v1/images/generations 接口规范的请求参数 // $data = [ // "model" => $model, // 直接使用传入的模型名,如 "gemini-3-pro-image-preview" // "prompt" => $prompt, // 提示词 // "size" => $aspectRatio, // 宽高比,如 "9:16" // "n" => 1, // 生成数量 // "quality" => "standard", // 生成质量 // "response_format" => "url" // 输出格式:url 或 b64_json // ]; // return $this->callApi($data,$model); // } // }else if($model == 'gemini-3.1-flash-image-preview'){ // if($status_val == '图生图'){ // $data = [ // 'model' => $model, // 'messages' => [ // [ // 'role' => 'user', // 'content' => [ // ['type' => 'text', 'text' => $prompt], // [ // 'type' => 'image_url', // 'image_url' => [ // 'url' => 'data:' . $product_mimeType . ';base64,' . $product_base64Data // ] // ], // [ // 'type' => 'image_url', // 'image_url' => [ // 'url' => 'data:' . $template_mimeType . ';base64,' . $template_base64Data // ] // ] // ] // ] // ], // 'response_modalities' => ['image'], // 'image_config' => [ // 'aspect_ratio' => $size, // 'quality' => 'high', // 'width' => '850', // 'height' => '1133' // ], // 'temperature' => 0.3, // 'top_p' => 0.8, // 'max_tokens' => 2048 // ]; // return $this->callApi($data,$model); // } // }else{ // // 处理其他模型或抛出异常 // throw new \Exception("未配置模型类型: {$model}"); // } // } /** * 构建视频请求体 * $status_val == 文生视频、图生视频、首图尾图生视频 * $model 模型 * $prompt 提示词 * $size 尺寸大小 * $seconds 时间 */ public function Txt_to_video($status_val,$prompt, $model, $size, $seconds) { //判断使用哪个模型、在判断此模型使用类型 if ($model === 'sora-2') { if ($status_val == '文生视频') { $data = [ 'prompt' => trim($prompt), 'model' => $model ?: 'sora-2', 'seconds' => (string)((int)$seconds ?: 5), 'size' => $size ?: '1920x1080' ]; return self::callApi($this->config['sora-2']['api_url'], $this->config['sora-2']['api_key'], $data, 300); } }else{ throw new \Exception("未配置模型类型: {$model}"); } } /** * 计算最大公约数 */ public function gcd($a, $b) { while ($b != 0) { $temp = $a % $b; $a = $b; $b = $temp; } return $a; } /** * 通用 API 调用方法(支持多接口故障自动切换) * * @param array $data 请求数据(JSON 格式) * @param string $model 模型名 * @param int $timeout 请求超时时间(秒) * @return array 接口响应数据(成功时返回解析后的数组) * @throws \Exception 所有接口都失败时抛出异常 */ public function callApi(array $data, string $model, int $timeout = 60): array { $allErrors = []; $httpCodes = []; $curlErrnos = []; // 1. 预加载该模型的所有API配置(按优先级排序,仅启用状态) $aiModelConfigs = Db::name("ai_model") ->where('model_name', $model) ->where('status', '1') ->order('sort ASC') ->select(); if (empty($aiModelConfigs)) { throw new \Exception("模型配置不存在: {$model}"); } // 2. 提前序列化JSON(只做一次,避免重复编码) $postData = json_encode($data, JSON_UNESCAPED_UNICODE); if (json_last_error() !== JSON_ERROR_NONE) { throw new \Exception("请求数据JSON编码失败: " . json_last_error_msg()); } // 3. 遍历所有接口,逐个尝试调用 foreach ($aiModelConfigs as $index => $config) { $ch = null; try { // 跳过空配置(同时记录到错误数组,避免最终汇总时 $httpCodes/$curlErrnos 为空导致未定义下标) if (empty($config['api_url']) || empty($config['api_key'])) { $allErrors[] = "第" . ($index + 1) . "个接口配置无效(URL/Key为空)"; $httpCodes[] = 0; $curlErrnos[] = 0; continue; } // 初始化curl(每个接口新建连接,避免复用导致的问题) $ch = curl_init(); $curlOptions = [ CURLOPT_URL => $config['api_url'], CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $config['api_key'], 'Connection: keep-alive', 'Keep-Alive: 300' ], CURLOPT_TIMEOUT => $timeout, CURLOPT_CONNECTTIMEOUT => 15, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_FAILONERROR => false, // 生产环境建议开启SSL验证(需配置CA证书) CURLOPT_SSL_VERIFYPEER => true, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_TCP_NODELAY => true, CURLOPT_FORBID_REUSE => false, CURLOPT_FRESH_CONNECT => false ]; curl_setopt_array($ch, $curlOptions); // 执行请求 $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlErrno = curl_errno($ch); $curlError = curl_error($ch); // 检查CURL底层错误 if ($response === false) { $errorMsg = "第" . ($index + 1) . "个接口CURL失败 [{$curlErrno}]: {$curlError}"; $allErrors[] = $errorMsg; $httpCodes[] = $httpCode; $curlErrnos[] = $curlErrno; continue; // 跳过当前接口,尝试下一个 } // 解析响应 $result = empty($response) ? [] : json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE && !empty($response)) { $errorMsg = "第" . ($index + 1) . "个接口响应解析失败: " . json_last_error_msg(); $allErrors[] = $errorMsg; $httpCodes[] = $httpCode; $curlErrnos[] = $curlErrno; continue; } // 检查API业务错误 if (isset($result['error'])) { $apiErrorDetail = $result['error']['message'] ?? ''; $errorType = $result['error']['type'] ?? ''; $errorCode = $result['error']['code'] ?? ''; $errorMessages = [ 'invalid_request_error' => '请求参数错误', 'authentication_error' => '认证失败', 'rate_limit_error' => '请求频率过高', 'insufficient_quota' => '额度不足', 'billing_not_active' => '账户未开通付费', 'content_policy_violation' => '内容违反政策', 'model_not_found' => '模型不存在或无可用渠道' ]; $friendlyMessage = $errorMessages[$errorCode] ?? ($errorMessages[$errorType] ?? 'API服务错误'); $detailedError = "第" . ($index + 1) . "个接口{$friendlyMessage}"; if ($errorCode) $detailedError .= " (错误代码: {$errorCode})"; if ($apiErrorDetail) $detailedError .= ": {$apiErrorDetail}"; $allErrors[] = $detailedError; $httpCodes[] = $httpCode; $curlErrnos[] = $curlErrno; continue; } // 检查HTTP状态码 if ($httpCode !== 200) { $statusMessages = [ 400 => '请求参数不合法', 401 => 'API密钥无效或权限不足', 403 => '访问被拒绝', 404 => 'API端点不存在', 429 => '请求过于频繁,请稍后再试', 500 => '服务器内部错误', 503 => '服务暂时不可用' ]; $statusMessage = $statusMessages[$httpCode] ?? "HTTP错误({$httpCode})"; $allErrors[] = "第" . ($index + 1) . "个接口{$statusMessage}"; $httpCodes[] = $httpCode; $curlErrnos[] = $curlErrno; continue; } // 任意一个接口成功,直接返回结果 curl_close($ch); return $result; } catch (\Exception $e) { // 捕获当前接口的异常,记录后尝试下一个 $allErrors[] = "第" . ($index + 1) . "个接口异常: " . $e->getMessage(); $httpCodes[] = 0; $curlErrnos[] = 0; if (is_resource($ch)) { curl_close($ch); } continue; } } // 所有接口都失败,抛出汇总异常 $finalError = "所有API接口调用失败(共尝试" . count($aiModelConfigs) . "个接口)\n"; $finalError .= "失败详情:\n- " . implode("\n- ", $allErrors) . "\n"; $lastHttpCode = $httpCodes[count($httpCodes) - 1] ?? 0; $lastCurlErrno = $curlErrnos[count($curlErrnos) - 1] ?? 0; $finalError .= "建议解决方案: " . $this->getErrorSolution($lastHttpCode, $lastCurlErrno) . "\n"; throw new \Exception($finalError); } /** * 获取错误原因 */ private function getErrorCause(int $httpCode, string $apiErrorDetail, int $curlErrno): string { $causeMap = [ // HTTP状态码 400 => '参数格式错误/必填参数缺失', 401 => 'API Key无效/过期/无权限', 429 => '超出接口调用频率限制', 500 => '服务端内部故障', 503 => '服务维护/算力不足', // CURL错误码 CURLE_OPERATION_TIMEDOUT => '请求超时(模型处理耗时超过设置值)', CURLE_COULDNT_CONNECT => '无法连接到API服务器', CURLE_SSL_CACERT => 'SSL证书验证失败', // 业务错误关键词 'model_not_found' => '模型渠道未开通/无可用资源', 'invalid_size' => '模型尺寸参数不符合要求', ]; // 优先匹配CURL错误码 if (isset($causeMap[$curlErrno])) { return $causeMap[$curlErrno]; } // 匹配HTTP状态码 if (isset($causeMap[$httpCode])) { return $causeMap[$httpCode]; } // 匹配业务错误关键词 foreach ($causeMap as $key => $cause) { if (is_string($key) && strpos($apiErrorDetail, $key) !== false) { return $cause; } } // 特殊关键词匹配 if (strpos($apiErrorDetail, 'No available capacity') !== false) { return '模型算力不足,无可用资源'; } elseif (strpos($apiErrorDetail, 'size is invalid') !== false) { return '模型尺寸参数无效'; } // 兜底 return $apiErrorDetail ?: '未知原因'; } /** * 获取错误解决方案 */ private function getErrorSolution(int $httpCode, int $curlErrno): string { $solutionMap = [ // HTTP状态码 400 => '1. 检查参数是否完整 2. 确认参数类型 3. 验证尺寸/模型名是否合法', 401 => '1. 检查API Key是否正确 2. 确认Key未过期/有对应模型权限', 429 => '1. 降低请求频率 2. 等待1-5分钟后重试 3. 联系服务商提升配额', 500 => '1. 等待几分钟后重试 2. 联系API服务商排查', 503 => '1. 等待算力释放 2. 联系服务商扩容', // CURL错误码 CURLE_OPERATION_TIMEDOUT => '1. 延长超时时间 2. 检查模型生成耗时 3. 重试请求', CURLE_COULDNT_CONNECT => '1. 检查API地址是否正确 2. 验证网络连通性 3. 检查防火墙配置', CURLE_SSL_CACERT => '1. 开启SSL证书验证 2. 配置正确的CA证书路径 3. 确认API域名证书有效', ]; if (isset($solutionMap[$curlErrno])) { return $solutionMap[$curlErrno]; } if (isset($solutionMap[$httpCode])) { return $solutionMap[$httpCode]; } return '1. 等待几分钟后重试 2. 检查API服务提供商状态 3. 联系服务提供商确认服务可用性'; } // /** // * 通用 API 调用方法(支持重试机制) // * // * @param string $url 接口地址 // * @param string $apiKey 授权密钥(Bearer Token) // * @param array $data 请求数据(JSON 格式) // * // * 功能说明: // * - 使用 cURL 发送 POST 请求到指定 API 接口 // * - 设置请求头和超时时间等参数 // * - 支持最多重试 2 次,当接口调用失败时自动重试 // * - 返回成功时解析 JSON 响应为数组 // * // * 异常处理: // * - 若全部重试失败,将抛出异常并包含最后一次错误信息 // * // * @return array 接口响应数据(成功时返回解析后的数组) // * @throws \Exception 接口请求失败时抛出异常 // */ // public static function callApi($data,$model,$timeout = 60) // { // $maxRetries = 0; // 减少重试次数为0,避免不必要的等待 // $attempt = 0; // $lastError = ''; // $httpCode = 0; // $apiErrorDetail = ''; // // $ai_model = Db::name("ai_model") // ->where('model_name',$model) // ->select(); // $num = 0; // while ($attempt <= $maxRetries) { // try { // if(!$ai_model[$num]){ // throw new \Exception("请求发送失败: " . $curlError); // } // $ch = curl_init(); // curl_setopt_array($ch, [ // CURLOPT_URL => $ai_model[$num]['api_url'], // CURLOPT_RETURNTRANSFER => true, // CURLOPT_POST => true, // CURLOPT_POSTFIELDS => json_encode($data, JSON_UNESCAPED_UNICODE), // CURLOPT_HTTPHEADER => [ // 'Content-Type: application/json', // 'Authorization: Bearer ' . $ai_model[$num]['api_key'] // ], // CURLOPT_TIMEOUT => (int) $timeout, // CURLOPT_SSL_VERIFYPEER => false, // CURLOPT_SSL_VERIFYHOST => false, // CURLOPT_CONNECTTIMEOUT => 15, // 减少连接超时时间为15秒 // CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, // CURLOPT_FAILONERROR => false // ]); // // $response = curl_exec($ch); // $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); // $curlError = curl_error($ch); // // if ($response === false) { // // 尝试从curl错误信息中提取HTTP状态码 // if (preg_match('/HTTP\/[0-9.]+\s+([0-9]+)/', $curlError, $matches)) { // $httpCode = (int)$matches[1]; // } // throw new \Exception("请求发送失败: " . $curlError); // } // // $result = json_decode($response, true); // // // 检查API返回的错误 // if (isset($result['error'])) { // $apiErrorDetail = $result['error']['message'] ?? ''; // $errorType = $result['error']['type'] ?? ''; // $errorCode = $result['error']['code'] ?? ''; // // // 常见错误类型映射 // $errorMessages = [ // 'invalid_request_error' => '请求参数错误', // 'authentication_error' => '认证失败', // 'rate_limit_error' => '请求频率过高', // 'insufficient_quota' => '额度不足', // 'billing_not_active' => '账户未开通付费', // 'content_policy_violation' => '内容违反政策', // 'model_not_found' => '模型不存在或无可用渠道' // ]; // // // 优先使用errorCode进行映射,如果没有则使用errorType // $friendlyMessage = $errorMessages[$errorCode] ?? ($errorMessages[$errorType] ?? 'API服务错误'); // // // 构建详细的错误信息,包含错误代码、类型和详细描述 // $detailedError = "{$friendlyMessage}"; // if ($errorCode) { // $detailedError .= " (错误代码: {$errorCode})"; // } // if ($apiErrorDetail) { // $detailedError .= ": {$apiErrorDetail}"; // } // // throw new \Exception($detailedError); // } // // if ($httpCode !== 200) { // // HTTP状态码映射 // $statusMessages = [ // 400 => '请求参数不合法', // 401 => 'API密钥无效或权限不足', // 403 => '访问被拒绝', // 404 => 'API端点不存在', // 429 => '请求过于频繁,请稍后再试', // 500 => '服务器内部错误', // 503 => '服务暂时不可用' // ]; // // $statusMessage = $statusMessages[$httpCode] ?? "HTTP错误({$httpCode})"; // throw new \Exception($statusMessage); // } // // curl_close($ch); // return $result; // // } catch (\Exception $e) { // $lastError = $e->getMessage(); // $attempt++; // $num++; // if ($attempt <= $maxRetries) { // sleep(pow(2, $attempt)); // } else { // // 最终失败时的详细错误信息 // $errorDetails = [ // '错误原因' => self::getErrorCause($httpCode, $apiErrorDetail), // '解决方案' => self::getErrorSolution($httpCode), // '请求参数' => json_encode($data, JSON_UNESCAPED_UNICODE), // 'HTTP状态码' => $httpCode, // '重试次数' => $attempt // ]; // // // 构建最终的错误信息,优先显示原始的详细错误消息 // $finalError = "API请求失败\n"; // $finalError .= "失败说明: " . $lastError . "\n"; // 使用原始的详细错误消息 // $finalError .= "建议解决方案: " . $errorDetails['解决方案'] . "\n"; // $finalError .= "技术详情: HTTP {$httpCode} - " . $errorDetails['错误原因']; // // throw new \Exception($finalError); // } // } // } // } // // private static function getErrorCause($httpCode, $apiErrorDetail) // { // $causeMap = [ // 400 => '参数格式错误/必填参数缺失', // 401 => 'API Key无效/过期/无权限', // 429 => '超出接口调用频率限制', // 500 => '服务端内部故障', // 503 => '服务维护/算力不足', // 'model_not_found' => '模型渠道未开通/无可用资源', // 'invalid_size' => '模型尺寸参数不符合要求', // CURLE_OPERATION_TIMEDOUT => '请求超时(模型处理耗时超过设置值)' // ]; // // if (strpos($apiErrorDetail, 'No available capacity') !== false) { // return '模型算力不足,无可用资源'; // } elseif (strpos($apiErrorDetail, 'size is invalid') !== false) { // return '模型尺寸参数无效'; // } elseif (isset($causeMap[$httpCode])) { // return $causeMap[$httpCode]; // } // return $apiErrorDetail ?: '未知原因'; // } // // private static function getErrorSolution($httpCode) // { // $solutionMap = [ // 400 => '1. 检查参数是否完整 2. 确认参数类型(如seconds为字符串) 3. 验证尺寸/模型名是否合法', // 401 => '1. 检查API Key是否正确 2. 确认Key未过期/有对应模型权限', // 429 => '1. 降低请求频率 2. 等待1-5分钟后重试', // 500 => '1. 等待几分钟后重试 2. 联系API服务商排查', // 503 => '1. 等待算力释放 2. 联系服务商扩容', // CURLE_OPERATION_TIMEDOUT => '1. 延长超时时间 2. 检查模型生成耗时 3. 重试请求' // ]; // // if (isset($solutionMap[$httpCode])) { // return $solutionMap[$httpCode]; // } // return '1. 等待几分钟后重试 2. 检查API服务提供商状态 3. 联系服务提供商确认服务可用性'; // } /** * 获取图片的base64数据和MIME类型 * @return array 包含base64数据和MIME类型的数组 */ public static function file_get_contents($ImageUrl){ // 构建完整的文件系统路径 $rootPath = str_replace('\\', '/', ROOT_PATH); $filePath = rtrim($rootPath, '/') . '/public/' . $ImageUrl; // 检查文件是否存在 if (!file_exists($filePath)) { throw new \Exception('图片文件不存在'); } // 读取图片内容并进行base64编码 $imageContent = file_get_contents($filePath); if (!$imageContent) { throw new \Exception('图片内容读取失败'); } // 获取图片的MIME类型 $finfo = finfo_open(FILEINFO_MIME_TYPE); $mimeType = finfo_file($finfo, $filePath); finfo_close($finfo); // 返回包含base64数据和MIME类型的数组 return [ 'base64Data' => base64_encode($imageContent), 'mimeType' => $mimeType ]; } }