liuhairui 5 сар өмнө
parent
commit
54c826b70f

+ 30 - 22
application/api/controller/WorkOrder.php

@@ -342,24 +342,35 @@ class WorkOrder extends Api
     public function stopQueueProcesses()
     {
 
+        Db::name('image_task_log')
+            ->where('log', '队列中')
+            ->whereOr('status', 1)
+            ->where('create_time', '>=', date('Y-m-d 00:00:00'))
+            ->update([
+                'status' => "-1",
+                'log' => '清空取消队列',
+                'mod_rq' => date('Y-m-d H:i:s')
+            ]);
+
+        Db::name('image_task_log')
+            ->whereLike('log', '%处理中%')
+            ->where('create_time', '>=', date('Y-m-d 00:00:00'))
+            ->update([
+                'status' => "-1",
+                'log' => '清空取消队列',
+                'mod_rq' => date('Y-m-d H:i:s')
+            ]);
+
         $redis = new \Redis();
         $redis->connect('127.0.0.1', 6379);
         $redis->auth('123456');
         $redis->select(15);
 
-        $key_txttoimg = 'queues:txttoimg';
-        $key_txttotxt = 'queues:txttotxt';
-        $key_imgtotxt = 'queues:imgtotxt';
-        $key_imgtoimg = 'queues:imgtoimg';
+        $key_txttoimg = 'queues:txttoimg:reserved';
+        $key_txttotxt = 'queues:txttotxt:reserved';
+        $key_imgtotxt = 'queues:imgtotxt:reserved';
+        $key_imgtoimg = 'queues:imgtoimg:reserved';
 
-        $count = $redis->lLen($key_txttoimg) + $redis->lLen($key_txttotxt) + $redis->lLen($key_imgtotxt) + $redis->lLen($key_imgtoimg);
-
-        if ($count === 0) {
-            return json([
-                'code' => 1,
-                'msg'  => '暂无队列需要停止'
-            ]);
-        }
 
         // 清空 Redis 队列
         $redis->del($key_txttoimg);
@@ -367,17 +378,14 @@ class WorkOrder extends Api
         $redis->del($key_imgtotxt);
         $redis->del($key_imgtoimg);
 
+        $count = $redis->lLen($key_txttoimg) + $redis->lLen($key_txttotxt) + $redis->lLen($key_imgtotxt) + $redis->lLen($key_imgtoimg);
 
-        $counts = Db::name('image_task_log')
-            ->where('log', '队列中')
-            ->whereOr('status', 1)
-            ->where('create_time', '>=', date('Y-m-d 00:00:00'))
-            ->update([
-                'status' => "-1",
-                'log' => '清空取消队列',
-                'mod_rq' => date('Y-m-d H:i:s')
-            ]);
-
+//        if ($count === 0) {
+//            return json([
+//                'code' => 1,
+//                'msg'  => '暂无队列需要停止'
+//            ]);
+//        }
         return json([
             'code' => 0,
             'msg'  => '成功停止队列任务'

+ 12 - 28
application/job/ImageJob.php

@@ -16,12 +16,9 @@ class ImageJob{
      */
     public function fire(Job $job, $data)
     {
-
         $logId = $data['log_id'];
-
         try {
-            echo date('Y-m-d H:i:s')."图生文任务开始\n";
-
+            echo date('Y-m-d H:i:s')."图生文开始\n";
             // 更新日志状态:处理中
             if ($logId) {
                 Db::name('image_task_log')->where('id', $logId)->update([
@@ -30,9 +27,8 @@ class ImageJob{
                     'update_time' => date('Y-m-d H:i:s')
                 ]);
             }
-
             //执行逻辑
-            $result = $this->processImage($data);
+            $result = $this->imageToText($data["sourceDir"],$data["file_name"],$data["prompt"],$data);
             echo $result;
 
             // 更新日志状态:成功
@@ -48,7 +44,8 @@ class ImageJob{
 
             //链式任务:图生文成功后继续推送文生文
             if (!empty($data['chain_next'])) {
-                $nextType = array_shift($data['chain_next']); // 获取下一个任务类型
+                // 获取下一个任务类型
+                $nextType = array_shift($data['chain_next']);
                 $data['type'] = $nextType;
 
                 // 保留剩余链,继续传下去
@@ -85,23 +82,11 @@ class ImageJob{
         echo "ImageJob failed: " . json_encode($data);
     }
 
-    /**
-     * 处理图片的具体逻辑
-     */
-    public function processImage($data)
-    {
-        // 根据传入的数据处理图片
-        $res = $this->imageToText($data["sourceDir"],$data["file_name"],$data["prompt"],$data);
-        echo $res;
-    }
-
-
     /**
      * 执行图生文逻辑(图像转文本)
      */
     public function imageToText($sourceDirRaw, $fileName, $prompt, $call_data)
     {
-
         // 自动拆分文件名
         if (!$fileName && preg_match('/([^\/]+\.(jpg|jpeg|png))$/i', $sourceDirRaw, $matches)) {
             $fileName = $matches[1];
@@ -149,14 +134,13 @@ class ImageJob{
 
         $gptText = trim($gptRes['choices'][0]['message']['content'] ?? '');
 
-        //gtp返回内容日志
+        //图生文返回内容日志runtime/logs/img_to_txt.txt
         // file_put_contents(
-        //     $logDir . 'img_name_success.txt',
+        //     $logDir . 'img_to_txt.txt',
         //     "\n======== " . date('Y-m-d H:i:s') . " ========\n" .
         //   $gptText. "\n\n",
         //     FILE_APPEND
-        // );
-        // echo "<pre>";print_r($gptText);echo "<pre>"; return 0;die;
+        // );die;
 
         // 验证 GPT 返回格式
         if (strpos($gptText, '---json json---') === false) {
@@ -178,13 +162,13 @@ class ImageJob{
         // 只保留中英文、数字、下划线、短横线、空格
         $img_name = preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $part2);
 
-        // 成功后的图生文提示词日志
+        //图生文返回日志拆分后内容runtime/logs/img_to_txt.txt
         // file_put_contents(
-        //     $logDir . 'img_name_success.txt',
+        //     $logDir . 'img_to_txt.txt',
         //     "\n======== " . date('Y-m-d H:i:s') . " ========\n" .
-        //     $englishDesc . "\n---json json---\n" .
-        //     $chineseDesc . "\n---json json---\n" .
-        //     $img_name . "\n\n",
+        //     $englishDesc . "\n--- 中文 ---\n" .
+        //     $chineseDesc . "\n---英文 ---\n" .
+        //     $img_name . "\n--- 命名 ---\n" ,
         //     FILE_APPEND
         // );
 

+ 105 - 91
application/job/TextToImageJob.php

@@ -4,6 +4,7 @@ use app\service\AIGatewayService;
 use think\Db;
 use think\Queue;
 use think\queue\Job;
+
 /**
  * 文生图任务处理类
  * 描述:接收提示词,通过模型生成图像,保存图像并记录数据库信息,是链式任务中的最后一环
@@ -17,7 +18,6 @@ class TextToImageJob
      */
     public function fire(Job $job, $data)
     {
-
         $logId = $data['log_id'] ?? null;
 
         try {
@@ -40,7 +40,7 @@ class TextToImageJob
                 ]);
             }
 
-            //拼接原图文件路径 + 图片名称
+            // 拼接原图路径
             $old_image_url = rtrim($data['sourceDir'], '/') . '/' . ltrim($data['file_name'], '/');
 
             $list = Db::name("text_to_image")
@@ -49,7 +49,6 @@ class TextToImageJob
                 ->where('status', 0)
                 ->select();
 
-
             if (!empty($list)) {
                 $total = count($list);
                 echo "📊 共需处理:{$total} 条记录\n";
@@ -59,7 +58,7 @@ class TextToImageJob
                     $begin = date('Y-m-d H:i:s');
                     echo "👉 正在处理第 {$currentIndex} 条,ID: {$row['id']}\n";
 
-                    // 调用生成图像方法
+                    // 图像生成
                     $result = $this->textToImage(
                         $data["file_name"],
                         $data["outputDir"],
@@ -70,38 +69,44 @@ class TextToImageJob
                         $data["selectedOption"]
                     );
 
-                    $resultText = ($result === true || $result === 1 || $result === '成功') ? '成功' : '失败或无返回';
-                    echo "✅ 处理结果:{$resultText}\n";
+                    // 标准化结果文本
+                    if ($result === true || $result === 1 || $result === '成功') {
+                        $resultText = '成功';
+                    } else {
+                        $resultText = (string) $result ?: '失败或无返回';
+                    }
 
-                    $end = date('Y-m-d H:i:s');
-                    echo "完成时间:{$end}\n";
+                    echo "✅ 处理结果:{$resultText}\n";
+                    echo "完成时间:" . date('Y-m-d H:i:s') . "\n";
                     echo "Processed: " . static::class . "\n";
                     echo "文生图已处理完成\n\n";
-                }
 
-                // 更新日志状态:成功
-                if ($logId) {
-                    Db::name('image_task_log')->where('id', $logId)->update([
-                        'status' => 2,
-                        'log' => '文生图处理成功',
-                        'update_time' => date('Y-m-d H:i:s')
-                    ]);
+                    // 若包含关键词,日志状态标为失败(-1)
+                    if (strpos($resultText, '包含关键词') !== false) {
+                        // 命中关键词类错误,状态设为失败
+                        if ($logId) {
+                            Db::name('image_task_log')->where('id', $logId)->update([
+                                'status' => -1,
+                                'log' => $resultText,
+                                'update_time' => date('Y-m-d H:i:s')
+                            ]);
+                        }
+                    }else{
+                        // 日志状态设置为成功(仅在未提前失败时)
+                        if ($logId) {
+                            Db::name('image_task_log')->where('id', $logId)->update([
+                                'status' => 2,
+                                'log' => '文生图处理成功',
+                                'update_time' => date('Y-m-d H:i:s')
+                            ]);
+                        }
+                    }
+                    continue; //跳过当前记录的后续处理
                 }
-
                 echo date('Y-m-d H:i:s') . " ✅ 文生图任务全部完成\n";
-            } else {
-                echo "⚠ 未找到可处理的数据,跳过执行\n";
-
-                if ($logId) {
-                    Db::name('image_task_log')->where('id', $logId)->update([
-                        'status' => 2,
-                        'log' => '无数据可处理,已跳过'.$old_image_url,
-                        'update_time' => date('Y-m-d H:i:s')
-                    ]);
-                }
             }
 
-            // 如果还有链式任务,继续推送
+            // 处理链式任务(如果有)
             if (!empty($data['chain_next'])) {
                 $nextType = array_shift($data['chain_next']);
                 $data['type'] = $nextType;
@@ -175,7 +180,6 @@ class TextToImageJob
         // 调用文生图模型接口生成图像
         $startTime = microtime(true);
 
-
         // 清理 prompt 的换行
         $prompt = preg_replace('/[\r\n\t]+/', ' ', $prompt);
 
@@ -185,106 +189,116 @@ class TextToImageJob
             // 判断提示词中是否包含关键词(不区分大小写)
             if (stripos($prompt, $keyword) !== false) {
                 $skipId = $record['id'];
-                echo "🚫 跳过生成:提示词中包含关键词“{$keyword}”,记录 ID:{$skipId}\n";
-                $updateRes = Db::name('text_to_image')->where('id', $skipId)->update([
+                echo "🚫 跳过生成:包含关键词“{$keyword}”,记录 ID:{$skipId}\n";
+                Db::name('text_to_image')->where('id', $skipId)->update([
                     'status' => 3,
-                    'error_msg' => "提示词中包含关键词".$keyword,
+                    'error_msg' => "包含关键词".$keyword,
                     'update_time' => date('Y-m-d H:i:s')
                 ]);
-                return "跳过生成:记录 ID {$skipId},包含关键词 - {$keyword}";
+                return "包含关键词 - {$keyword}";
             }
         }
 
         //文生图调用
+        $startTime = microtime(true);
+
         $ai = new AIGatewayService();
         $dalle1024 = $ai->callDalleApi($prompt,$selectedOption);
 
-        //检测查询接口调用时长
         $endTime = microtime(true);
         $executionTime = $endTime - $startTime;
-        echo "API调用耗时: " . round($executionTime, 3) . " 秒\n";
-
-        // 检查 URL 返回是否成功
-        if (!isset($dalle1024['data'][0]['url']) || empty($dalle1024['data'][0]['url'])) {
-            $errorText = $dalle1024['error']['message'] ?? '未知错误';
-            Db::name('text_to_image')->where('id', $record['id'])->update([
-                'error_msg' => '生成失败:' . $errorText,
-                'status' => 0
-            ]);
-            return '生成失败:' . $errorText;
+        echo "✅ API 调用耗时: " . round($executionTime, 3) . " 秒\n";
+
+        // 错误检查
+        if (isset($dalle1024['error'])) {
+            throw new \Exception("❌ 图像生成接口错误:" . ($dalle1024['error']['message'] ?? '未知错误'));
         }
 
-        // 下载图像
-        $imgUrl1024 = $dalle1024['data'][0]['url'];
-        $imgData1024 = @file_get_contents($imgUrl1024);
-        if (!$imgData1024 || strlen($imgData1024) < 1000) {
-            return "下载图像失败或内容异常";
+        // 提取 url 图像
+//        $base64Image = $dalle1024['data'][0]['url'] ?? null;
+
+        // 提取 base64 图像
+        $base64Image = $dalle1024['data'][0]['b64_json'] ?? null;
+
+        if (!$base64Image || strlen($base64Image) < 1000) {
+            file_put_contents('/tmp/empty_image_base64.txt', json_encode($dalle1024, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
+            throw new \Exception("❌ 图像内容为空或异常,详情写入 /tmp/empty_image_base64.txt");
         }
 
-        // 保存原图(1024x1024)
-        $img_name = preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name);
-        $img_name = mb_substr($img_name, 0, 30); // 限制为前30个字符(避免路径过长)
-        $filename1024 = $img_name . '.png';
-        $savePath1024 = $fullBaseDir . '1024x1024/' . $filename1024;
-        file_put_contents($savePath1024, $imgData1024);
+        // 解码图片
+        $imgData = base64_decode($base64Image);
+
+        // 判断是否为图像
+        $finfo = finfo_open(FILEINFO_MIME_TYPE);
+        $mimeType = finfo_buffer($finfo, $imgData);
+        finfo_close($finfo);
+
+        // 保存图片路径
+        $img_name = mb_substr(preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name), 0, 30);
+        $filename = $img_name . '.png';
+
+        $saveDir1024 = $fullBaseDir . '1024x1024/';
+        $saveDirCustom = $fullBaseDir . "{$width}x{$height}/";
+        @mkdir($saveDir1024, 0755, true);
+        @mkdir($saveDirCustom, 0755, true);
 
-        // 图像裁剪生成自定义尺寸图
-        $im = @imagecreatefromstring($imgData1024);
-        if (!$im) return "图像损坏或格式不支持";
+        $path1024 = $saveDir1024 . $filename;
+        $pathCustom = $saveDirCustom . $filename;
 
-        $srcWidth = imagesx($im);
-        $srcHeight = imagesy($im);
-        $srcRatio = $srcWidth / $srcHeight;
+        file_put_contents($path1024, $imgData);
+
+        // 解析图像内容
+        try {
+            $im = \imagecreatefromstring($imgData);
+            if (!$im) {
+                file_put_contents('/tmp/corrupted.png', $imgData);
+                throw new \Exception("❌ 图像无法解析,写入 /tmp/corrupted.png");
+            }
+        } catch (\Throwable $e) {
+            file_put_contents('/tmp/corrupted.png', $imgData);
+            throw new \Exception("❌ 图像处理异常:" . $e->getMessage());
+        }
+
+        // 裁剪
+        $srcW = imagesx($im);
+        $srcH = imagesy($im);
+        $srcRatio = $srcW / $srcH;
         $dstRatio = $width / $height;
 
-        // 居中裁剪逻辑
         if ($srcRatio > $dstRatio) {
-            $cropHeight = $srcHeight;
-            $cropWidth = intval($srcHeight * $dstRatio);
-            $srcX = intval(($srcWidth - $cropWidth) / 2);
+            $cropW = intval($srcH * $dstRatio);
+            $cropH = $srcH;
+            $srcX = intval(($srcW - $cropW) / 2);
             $srcY = 0;
         } else {
-            $cropWidth = $srcWidth;
-            $cropHeight = intval($srcWidth / $dstRatio);
+            $cropW = $srcW;
+            $cropH = intval($srcW / $dstRatio);
             $srcX = 0;
-            $srcY = intval(($srcHeight - $cropHeight) / 2);
+            $srcY = intval(($srcH - $cropH) / 2);
         }
 
         $dstImg = imagecreatetruecolor($width, $height);
-        imagecopyresampled($dstImg, $im, 0, 0, $srcX, $srcY, $width, $height, $cropWidth, $cropHeight);
+        imagecopyresampled($dstImg, $im, 0, 0, $srcX, $srcY, $width, $height, $cropW, $cropH);
 
-        // 保存裁剪后图像
-        $filenameCustom = $img_name . ".png";
-        $savePathCustom = $fullBaseDir . "{$width}x{$height}/" . $filenameCustom;
-        imagepng($dstImg, $savePathCustom);
+        // 保存裁剪图
+        imagepng($dstImg, $pathCustom);
         imagedestroy($im);
         imagedestroy($dstImg);
 
-        // 成功写入数据库记录
-        $status = trim($img_name) === '' ? 0 : 1;
-
-        // 根据 selectedOption 设置 quality 和 style
-        $quality = 'hd';
-        $style = 'vivid';
-        if ($selectedOption === 'dall-e-3') {
-            $quality = 'standard';
-            $style = 'vivid';
-        }
-
-        $updateRes = Db::name('text_to_image')->where('id', $record['id'])->update([
-            'new_image_url' => str_replace($rootPath . 'public/', '', $savePath1024),
-            'custom_image_url' => str_replace($rootPath . 'public/', '', $savePathCustom),
+        // 写入数据库
+        Db::name('text_to_image')->where('id', $record['id'])->update([
+            'new_image_url' => str_replace($rootPath . 'public/', '', $path1024),
+            'custom_image_url' => str_replace($rootPath . 'public/', '', $pathCustom),
             'img_name' => $img_name,
             'model' => $selectedOption,
-            'status' => $status,
+            'status' => trim($img_name) === '' ? 0 : 1,
             'status_name' => "文生图",
             'size' => "{$width}x{$height}",
-            'quality' => $quality,
-            'style' => $style,
+            'quality' => 'standard',
+            'style' => 'vivid',
             'error_msg' => '',
             'update_time' => date('Y-m-d H:i:s')
         ]);
         return "成功";
     }
-
 }

+ 72 - 102
application/service/AIGatewayService.php

@@ -3,34 +3,38 @@ namespace app\service;
 use think\Db;
 use think\Queue;
 class AIGatewayService{
-
     /**
      * 接口访问配置
      *
      * 每个模块包含:
-     * - api_key:API 调用密钥(Bearer Token)
+     * - api_key:API 调用密钥(Token)
      * - api_url:对应功能的服务端地址
      */
     protected $config = [
         //图生文
         'imgtotxt' => [
-            'api_key' => 'sk-scyWT2YPrPOIzralcb6WzCeFdAZvl91rh1JcuuaNMrhEJNDZ',
-            'api_url' => 'https://niubi.zeabur.app/v1/chat/completions'
+            'api_key' => 'sk-LVcDfTx5SYK6pWiGpfcAN2KA0LunymnMiYSVfzUKQXrjlkZv',
+            'api_url' => 'https://chatapi.onechats.top/v1/chat/completions'
         ],
         //文生文
+        // 'txttotxt' => [
+        //     'api_key' => 'sk-fxlawqVtbbQbNW0wInR3E4wsLo5JHozDC2XOHzMa711su6ss',
+        //     'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
+        // ],
+        //文生文
         'txttotxt' => [
-            'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
-            'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
+            'api_key' => 'sk-fxlawqVtbbQbNW0wInR3E4wsLo5JHozDC2XOHzMa711su6ss',
+            'api_url' => 'https://chatapi.onechats.top/v1/chat/completions'
         ],
 //        //文生图
 //        'txttoimg' => [
 //            'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
 //            'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
 //        ]
-//        //文生图【权限不足】
+//        //文生图
         'txttoimg' => [
-            'api_key' => 'sk-e0JuPjMntkbgi1BoMjrqyyzMKzAxILkQzyGMSy3xiMupuoWY',
-            'api_url' => 'https://niubi.zeabur.app/v1/images/generations'
+            'api_key' => 'sk-MB6SR8qNaTjO80U7HJl4ztivX3zQKPgKVka9oyfVSXIkHSYZ',
+            'api_url' => 'https://chatapi.onechats.top/v1/images/generations'
         ]
     ];
 
@@ -41,8 +45,9 @@ class AIGatewayService{
      */
     public function callGptApi($imageUrl, $prompt)
     {
+        //方式一
         $data = [
-            "model" => "gemini-2.5-pro-preview-05-06",
+            "model" => "gemini-2.5-flash-preview",
             "messages" => [[
                 "role" => "user",
                 "content" => [
@@ -55,18 +60,9 @@ class AIGatewayService{
             ]],
             "max_tokens" => 1000
         ];
-
-        return $this->callApi($this->config['imgtotxt']['api_url'], $this->config['imgtotxt']['api_key'], $data);
-    }
-    /**
-     * 图生文-方法二
-     * @param string $imageUrl 图像 URL,支持公网可访问地址
-     * @param string $prompt   对图像的提问内容或提示文本
-     */
-//    public function callGptApi($imageUrl, $prompt)
-//    {
+        //方式二
 //        $data = [
-//             "model" => "gpt-4-vision-preview",
+//            "model" => "gpt-4-vision-preview",
 //            "messages" => [[
 //                "role" => "user",
 //                "content" => [
@@ -79,8 +75,10 @@ class AIGatewayService{
 //            ]],
 //            "max_tokens" => 1000
 //        ];
-//        return $this->callApi($this->config['imgtotxt']['api_url'], $this->config['imgtotxt']['api_key'], $data);
-//    }
+
+        return $this->callApi($this->config['imgtotxt']['api_url'], $this->config['imgtotxt']['api_key'], $data);
+    }
+
 
     /**
      * 文生文
@@ -88,11 +86,24 @@ class AIGatewayService{
      */
     public function txtGptApi($prompt)
     {
+        if (empty($prompt)) {
+            throw new \Exception("Prompt 不允许为空");
+        }
+        //方式一
+        // $data = [
+        //     'prompt' => $prompt,
+        //     'model' => 'gpt-4',
+        //     'session_id' => null,
+        //     'context_reset' => true
+        // ];
+        //方式二
         $data = [
-            'prompt' => $prompt,
             'model' => 'gpt-4',
-            'session_id' => null,
-            'context_reset' => true
+            'messages' => [
+                ['role' => 'user', 'content' => $prompt]
+            ],
+            'temperature' => 0.7,
+            'max_tokens' => 1024
         ];
         return $this->callApi(
             $this->config['txttotxt']['api_url'],
@@ -101,62 +112,62 @@ class AIGatewayService{
         );
     }
 
-    /**
-     * 文生文-超结损分析
-     * @param string $prompt 用户输入的文本提示内容
-     */
-//    public function chaojiesunGptApi($prompt)
-//    {
-//        $data = [
-//            'prompt' => $prompt,
-//            'model' => 'gpt-4',
-//            'session_id' => null,
-//            'context_reset' => true
-//        ];
-//        return $this->callApi(
-//            $this->config['chaojiesun']['api_url'],
-//            $this->config['chaojiesun']['api_key'],
-//            $data
-//        );
-//    }
-
     /**
      * 文生图
-     * @param string $prompt          提示文本,用于指导图像生成
-     * @param string $selectedOption  模型名称,例如 'dall-e-3' 或其他兼容模型
+     *
+     * @param string $prompt         提示文本,用于指导图像生成(最长建议 1000 字符)
+     * @param string $selectedOption 模型名称,例如 'dall-e-3' 或其他兼容模型
+     *
+     * 默认参数说明(适用于所有模型):
+     * - n: 1(生成 1 张图)
+     * - size: '1024x1024'(标准正方形图像)
+     * - quality: 'hd'(高清质量)
+     * - style: 'vivid'(鲜明风格)
+     *
+     * response_format 参数差异:
+     * - 若模型为 'dall-e-3':返回 base64 图像,字段为 `b64_json`
+     * - 其他模型默认返回图像 URL,字段为 `url`
+     *
+     * ⚠️ 注意:使用此方法后,需在 Job/TextToImageJob.php 中按 response_format 判断提取方式:
+     *  提取 url 图像
+     *        $base64Image = $dalle1024['data'][0]['url'] ?? null;
+     *  提取 base64 图像
+     *        $base64Image = $dalle1024['data'][0]['b64_json'] ?? null;
+     *
+     * @return array 返回接口响应,成功时包含 'data' 字段,失败时包含 'error' 信息
      */
-    public function callDalleApi($prompt,$selectedOption)
+    public function callDalleApi($prompt, $selectedOption)
     {
-        if($selectedOption == 'dall-e-3'){
+        if ($selectedOption === 'dall-e-3') {
             $data = [
-                'prompt' => $prompt,
+                'prompt'  => $prompt,
                 'model'   => $selectedOption,
                 'n'       => 1,
                 'size'    => '1024x1024',
-                'quality' => 'standard',
+                'quality' => 'hd',
                 'style'   => 'vivid',
-                'response_format' => 'url',
-                'session_id' => null,
-                'context_reset' => true
+                'response_format' => 'b64_json',
             ];
-        }else{
+        } else {
             $data = [
-                'prompt' => $prompt,
+                'prompt'  => $prompt,
                 'model'   => $selectedOption,
                 'n'       => 1,
                 'size'    => '1024x1024',
                 'quality' => 'hd',
                 'style'   => 'vivid',
-                'response_format' => 'url',
-                'session_id' => null,
-                'context_reset' => true
+                'response_format' => 'b64_json',
             ];
         }
-
-        return $this->callApi($this->config['txttoimg']['api_url'], $this->config['txttoimg']['api_key'], $data);
+        return $this->callApi($this->config['txttoimg']['api_url'],$this->config['txttoimg']['api_key'],$data);
     }
 
 
+    /**
+     * 图生图
+     * @param string $prompt 用户输入的文本提示内容
+     * @param string $new_image_url 原图
+     */
     public function imgtoimgGptApi($prompt, $new_image_url)
     {
         $imgPath = ROOT_PATH . 'public/' . $new_image_url;
@@ -372,45 +383,4 @@ class AIGatewayService{
 
         return $solutions[$httpCode] ?? '1. 检查网络连接 2. 查看服务日志 3. 联系技术支持';
     }
-//    public function callApi($url, $apiKey, $data)
-//    {
-//        $maxRetries = 2;           // 最多重试次数
-//        $attempt = 0;              // 当前尝试次数
-//        $lastError = '';           // 最后一次错误信息
-//
-//        while ($attempt <= $maxRetries) {
-//            $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 ' . $apiKey
-//                ],
-//                CURLOPT_TIMEOUT => 120,
-//                CURLOPT_SSL_VERIFYPEER => false,
-//                CURLOPT_SSL_VERIFYHOST => 0,
-//                CURLOPT_TCP_KEEPALIVE => 1,
-//                CURLOPT_FORBID_REUSE => false
-//            ]);
-//
-//            $response = curl_exec($ch);
-//            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
-//            $curlError = curl_error($ch);
-//            curl_close($ch);
-//
-//            if ($response !== false && $httpCode === 200) {
-//                $result = json_decode($response, true);
-//                return $result;
-//            }
-//
-//            $lastError = $curlError ?: "HTTP错误:{$httpCode}";
-//            $attempt++;
-//            sleep(1);
-//        }
-//
-//        throw new \Exception("请求失败(重试{$maxRetries}次):{$lastError}");
-//    }
 }

+ 19 - 19
application/service/ImageService.php

@@ -3,7 +3,7 @@ namespace app\service;
 use think\Db;
 use think\Queue;
 /**
- * ImageService 类用于处理图像任务。
+ * ImageService 类用于处理图像任务和存放日志队列
  * 该类将前端传过来的多个图像信息推送到处理队列中。
  */
 class ImageService{
@@ -15,12 +15,15 @@ class ImageService{
         if (!isset($params["batch"])) {return false;}
 
         $arr = [];
-        $batch = $params["batch"]; // 获取图像批量信息
-        $num = $params["num"]; // 获取执行次数数量
-
-        // 获取模板
-        // english_content 文生文提示词
-        // content 图生文提示词
+        // 获取图像批量信息
+        $batch = $params["batch"];
+        // 获取执行次数数量
+        $num = $params["num"];
+
+        /*获取ids为1模板
+         * english_content 文生文提示词
+         * content 图生文提示词
+         * */
         $template = Db::name('template')
             ->field('id,english_content,content,ids')
             ->where('ids',1)
@@ -42,8 +45,7 @@ class ImageService{
             $arr = array_merge($arr, array_fill(0, $num, $baseItem));
         }
 
-
-        // 插入队列日志基础信息
+        // 插入队列日志
         $insertData = [
             'create_time'  => date('Y-m-d H:i:s'),
             'old_image_file'       => $params['old_image_file'],
@@ -52,14 +54,15 @@ class ImageService{
             'params'       => json_encode($params, JSON_UNESCAPED_UNICODE)
         ];
 
-        // 模型任务类型处理
+        //模型任务类型处理
         if (empty($params['type'])) {
-            // 链式任务:图生文 → 文生文 → 文生图
+            /*
+             * 执行全部任务时一键链式任务队列
+             * 用于存放队列日志
+             * 链式任务:图生文 → 文生文 → 文生图
+             * */
             $insertData['model'] = "gpt-4-vision-preview,"."gpt-4,".$params['selectedOption'];
             $insertData['model_name'] = '文生图';
-
-//            echo "<pre>";print_r($insertData);echo "<pre>";die;
-
             $task_id = Db::name('queue_logs')->insertGetId($insertData);
 
             $arr = array_map(function ($item) use ($task_id) {
@@ -76,7 +79,7 @@ class ImageService{
 
             Queue::push('app\job\ImageArrJob', $payload, "arrimage");
         } else {
-            // 指定任务类型
+            // 指定单个独立任务类型
             switch ($params['type']) {
                 case '图生文':
                     $insertData['model'] = 'gpt-4-vision-preview';
@@ -98,23 +101,20 @@ class ImageService{
                     return false;
             }
 
+            //将一组队列存放queue_logs任务表中,将新增id最为任务id记录
             $task_id = Db::name('queue_logs')->insertGetId($insertData);
-
             $arr = array_map(function ($item) use ($params, $task_id) {
                 $item['type'] = $params['type'];
                 $item['task_id'] = $task_id;
                 return $item;
             }, $arr);
-
             // 投递任务到队列
             $payload = [
                 'task_id' => $task_id,
                 'data' => $arr
             ];
-
             Queue::push('app\job\ImageArrJob', $payload, "arrimage");
         }
-
         return true;
     }