liuhairui 12 часов назад
Родитель
Сommit
5859e42dd6

+ 23 - 32
application/api/controller/Facility.php

@@ -833,71 +833,62 @@ class Facility extends Api
     //获取原文件夹数据
     public function getPreviewFolders()
     {
-        $rootPath = app()->getRootPath(); // 更标准
-        $baseDir = rtrim(str_replace('\\', '/', $rootPath), '/') . '/public/uploads/operate/ai/Preview';
-        $cacheDir = $rootPath . 'runtime/cache/';
+        $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
+
+        $cacheDir = ROOT_PATH . '/runtime/cache/';
         $cacheFile = $cacheDir . 'folder_list.json';
-        $cacheTTL = 86400; // 缓存 1 
-        $forceRefresh = input('refresh/d', 0); // 更安全,强制转 int
+        $cacheTTL = 86400; // 缓存有效期:1
+        $forceRefresh = input('get.refresh', 0); // 是否强制刷新缓存
 
-        try {
             if (!is_dir($baseDir)) {
                 return json([
                     'code' => 404,
-                    'msg'  => '预览目录不存在',
+                    'msg' => '目录不存在',
                     'data' => []
                 ]);
             }
 
-            $useCache = file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTTL;
             $folders = [];
+            $useCache = file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTTL;
 
             if ($useCache && !$forceRefresh) {
-                $folders = json_decode(file_get_contents($cacheFile), true) ?: [];
+                $folders = json_decode(file_get_contents($cacheFile), true);
             } else {
-                // 重新扫描目录
-                $directory = new \RecursiveDirectoryIterator($baseDir, \RecursiveDirectoryIterator::SKIP_DOTS);
-                $iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
+                $folders = [];
+
+                // 使用高效方式递归遍历所有子目录
+                $directory = new RecursiveDirectoryIterator($baseDir, RecursiveDirectoryIterator::SKIP_DOTS);
+                $iterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST);
 
                 foreach ($iterator as $file) {
                     if ($file->isDir()) {
                         $fullPath = str_replace('\\', '/', $file->getPathname());
-
-                        // 去掉 public/ 之前的路径(使其相对路径用于前端)
-                        $relativePath = ltrim(str_replace(str_replace('\\', '/', $rootPath . 'public/'), '', $fullPath), '/');
-
+                        $relativePath = str_replace(ROOT_PATH . 'public/', '', $fullPath);
                         $folders[] = [
-                            'name'      => basename($fullPath),
-                            'path'      => $relativePath,
+                            'name' => basename($fullPath),
+                            'path' => $relativePath,
                             'full_path' => $fullPath
                         ];
                     }
                 }
 
-                // 写入缓存(带锁)
+                // 写入缓存
                 if (!is_dir($cacheDir)) {
                     mkdir($cacheDir, 0777, true);
                 }
-                file_put_contents($cacheFile, json_encode($folders, JSON_UNESCAPED_UNICODE), LOCK_EX);
+                file_put_contents($cacheFile, json_encode($folders));
             }
 
             return json([
                 'code' => 0,
-                'msg'  => '获取预览文件夹成功',
+                'msg' => '获取所有预览文件夹成功',
                 'data' => [
-                    'folders'     => $folders,
-                    'total'       => count($folders),
-                    'from_cache'  => $useCache && !$forceRefresh
+                    'folders' => $folders,
+                    'total' => count($folders),
+                    'from_cache' => $useCache && !$forceRefresh
                 ]
             ]);
-        } catch (\Exception $e) {
-            return json([
-                'code' => 500,
-                'msg'  => '服务器错误: ' . $e->getMessage(),
-                'data' => []
-            ]);
-        }
-    }
 
+    }
 
 }

+ 346 - 6
application/api/controller/WorkOrder.php

@@ -3,6 +3,7 @@ namespace app\api\controller;
 
 use app\common\controller\Api;
 use app\job\ImageJob;
+use app\service\AIGatewayService;
 use app\service\ImageService;
 use think\App;
 use think\Db;
@@ -10,11 +11,18 @@ use think\Exception;
 use think\Log;
 use think\Queue;
 use think\queue\job\Redis;
+use think\Request;
+
 class WorkOrder extends Api
 {
     protected $noNeedLogin = ['*'];
     protected $noNeedRight = ['*'];
 
+    public function indexx()
+    {
+        echo 123;
+    }
+
     /**
      * 出图接口
      * 此方法处理图像转换为文本的请求,将图像信息存入队列以供后续处理。
@@ -22,9 +30,338 @@ class WorkOrder extends Api
     public function imageToText()
     {
         $params = $this->request->param();
-        $service = new ImageService();
-        $service->handleImage($params);
-        $this->success('任务成功提交至队列');
+            $service = new ImageService();
+            $service->handleImage($params);
+            $this->success('任务成功提交至队列');
+    }
+
+    //扩写文本内容提示词
+    public function GetTxtToTxt(){
+        // 确保所有必要的变量都已初始化
+        $params = $this->request->param();
+        $prompt = $params['prompt'];
+
+        $ai = new AIGatewayService();
+        $gptRes = $ai->txtGptApi($prompt,'gemini-2.0-flash');
+        echo "<pre>";
+        print_r($gptRes);
+        echo "<pre>";
+    }
+
+    //获取URL地址与端口
+    public function GetHttpUrl(){
+        $data = Db::name('http_url')->find();
+        // 拼接完整的HTTP URL
+        $fullUrl = "http://" . $data['baseUrl'] . ":" . $data['port'];
+        $res = [
+            'code' => 0,
+            'msg' => '成功',
+            'data' => [
+                'full_url' => $fullUrl,
+                'id' => $data['id'],
+                'baseUrl' => $data['baseUrl'],
+                'port' => $data['port']
+            ]
+        ];
+        return json($res);
+    }
+
+
+    public function Getvideolist(){
+        if (!$this->request->isGet()) {
+            $this->error('请求方式错误');
+        }
+        $params = $this->request->param();
+        $search = input('search', '');
+        $page = isset($params['page']) ? (int)$params['page'] : 1;
+        $limit = isset($params['limit']) ? (int)$params['limit'] : 50;
+        $where = [];
+        if (!empty($search)) {
+            $where['prompt'] = ['like', '%' . $search . '%'];
+        }
+        $list = Db::name('video')->where('mod_rq', null)
+            ->where($where)
+            ->order('id desc')
+            ->limit(($page - 1) * $limit, $limit)
+            ->select();
+        $total = Db::name('video')->where('mod_rq', null)
+            ->where($where)
+            ->count();
+        $res['code'] = 0;
+        $res['msg'] = '成功';
+        $res['count'] = $total;
+        $res['data'] = $list;
+        return json($res);
+    }
+
+
+        //video_691c078dbb648190a17625bbef815ce50cbc1621ce1702d7
+    public function video()
+    {
+        $params = $this->request->param();
+
+        $apiUrl = 'https://chatapi.onechats.ai/v1/videos';
+        $apiKey = 'sk-sWW1GFlnjbrDRb1DkMEzePIxgdvLK6cZt0Qg93yDMVP2z1yN';
+        $postData = [
+            'prompt' => $params['prompt'],
+            'model' => $params['model'],
+            'seconds' => $params['seconds'],
+            '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
+                ]
+            ]);
+        }
+
+        $videoData = [
+            'video_id' => $responseData['id'],
+            'prompt' => $postData['prompt'],
+            'model' => $postData['model'],
+            'seconds' => $postData['seconds'],
+            'size' => $postData['size'],
+            'sys_rq' => date("Y-m-d H:i:s")
+        ];
+
+        // 尝试插入数据
+        try {
+            $res = Db::name('video')->insert($videoData);
+            return json([
+                'code' => 0,
+                'msg' => '视频正在生成中',
+                'data ' => [
+                    'video_id' => $responseData['id'],
+                    'insert_result' => $res
+                ]
+            ]);
+        } catch (Exception $e) {
+            return json([
+                'code' => 1,
+                'msg' => '数据库操作失败',
+                'data' => [
+                    'error_message' => $e->getMessage()
+                ]
+            ]);
+        }
+    }
+
+     /**
+     * 获取视频内容
+     * 下载已完成的视频内容
+     */
+    public function videoContent(){
+            // 从请求参数获取video_id,如果没有则使用默认值
+            $video_id = input('get.video_id');
+
+            $apiKey = 'sk-sWW1GFlnjbrDRb1DkMEzePIxgdvLK6cZt0Qg93yDMVP2z1yN';
+            // 1. 先检查视频状态
+            $statusUrl = 'https://chatapi.onechats.ai/v1/videos/' . $video_id;
+            $statusData = $this->fetchVideoStatus($statusUrl, $apiKey);
+
+            // 检查视频状态
+            if ($statusData['status'] !== 'completed') {
+                return json([
+                    'code' => 202,
+                    'msg' => '视频尚未生成完成',
+                    'data' => [
+                        'video_id' => $video_id,
+                        'status' => $statusData['status'],
+                        'progress' => $statusData['progress'],
+                        'created_at' => $statusData['created_at'],
+                        'message' => '请稍后再试,视频仍在' . ($statusData['status'] === 'queued' ? '排队中' : '处理中')
+                    ]
+                ]);
+            }
+
+            // 2. 视频生成完成,准备下载
+            $apiUrl = 'https://chatapi.onechats.ai/v1/videos/' . $video_id . '/content';
+
+            // 获取可选的variant参数
+            $variant = input('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 = $video_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, '/');
+
+            // 将正确格式的文件路径存入数据库
+            Db::name('video')->where('video_id', $video_id)->update([
+                'web_url' => $savePath
+            ]);
+
+            // 返回成功响应
+            return json([
+                'code' => 0,
+                'msg' => '视频下载成功',
+                'data' => [
+                    'video_id' => $video_id,
+                    'local_path' => $localPath,
+                    'web_url' => $savePath,
+                    'file_size' => filesize($fullPath)
+                ]
+            ]);
+        }
+
+
+    /**
+     * 获取视频状态
+     */
+    private function fetchVideoStatus($url, $apiKey) {
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_HTTPGET, true);
+        curl_setopt($ch, CURLOPT_HTTPHEADER, [
+            'Authorization: Bearer ' . $apiKey,
+            'Accept: application/json'
+        ]);
+        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
+
+        $response = curl_exec($ch);
+        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+        $error = curl_error($ch);
+        curl_close($ch);
+
+        if ($error) {
+            throw new Exception('获取视频状态失败: ' . $error);
+        }
+
+        if ($httpCode < 200 || $httpCode >= 300) {
+            throw new Exception('获取视频状态失败,HTTP状态码: ' . $httpCode);
+        }
+
+        $data = json_decode($response, true);
+        if (!is_array($data)) {
+            throw new Exception('视频状态数据格式错误');
+        }
+
+        return $data;
+    }
+
+    /**
+     * 下载视频文件
+     */
+    private function downloadVideo($url, $apiKey) {
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_URL, $url);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($ch, CURLOPT_HTTPGET, true);
+        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);
+
+        $response = curl_exec($ch);
+        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+        $error = curl_error($ch);
+        curl_close($ch);
+
+        if ($error) {
+            throw new Exception('视频下载失败: ' . $error);
+        }
+
+        if ($httpCode < 200 || $httpCode >= 300) {
+            throw new Exception('视频下载失败,HTTP状态码: ' . $httpCode);
+        }
+
+        return $response;
     }
 
 
@@ -390,7 +727,7 @@ class WorkOrder extends Api
     public function imgtowimg()
     {
         $prompt = $this->request->param('prompt', '');
-        $imgRelPath = 'uploads/operate/ai/Preview/arr/两只手碰拳的黑白线条插画下有Daddy  Me字样风格简约质.png';
+        $imgRelPath = 'uploads/operate/ai/Preview/arr/0835006071623.png';
 
         $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
 
@@ -487,7 +824,7 @@ class WorkOrder extends Api
     {
         // 配置参数
         $config = [
-            'input_dir' => 'uploads/operate/ai/Preview/arr/',
+            'input_dir' => 'uploads/img2img/',
             'output_dir' => 'uploads/extra_image/',
             'api_url' => 'http://20.0.17.188:45001/sdapi/v1/extra-single-image',
             'timeout' => 120, // 增加超时时间,高清处理可能耗时较长
@@ -507,7 +844,7 @@ class WorkOrder extends Api
         ];
 
         // 输入文件处理
-        $imgRelPath = '图案的整体色调是柔和的蓝色和灰色形成温馨而宁静的视觉效果花卉.png';
+        $imgRelPath = '0835006071623-1757406184-1024x1248.png';
         $imgPath = ROOT_PATH . 'public/' . $config['input_dir'] . $imgRelPath;
 
         if (!file_exists($imgPath)) {
@@ -740,6 +1077,7 @@ class WorkOrder extends Api
             ->group('status')
             ->select();
 
+
         $statusCount = [];
         foreach ($statusList as $item) {
             $statusCount[$item['status']] = $item['total'];
@@ -920,4 +1258,6 @@ class WorkOrder extends Api
         ]);
     }
 
+
+
 }

+ 47 - 80
application/job/TextToImageJob.php

@@ -19,6 +19,7 @@ class TextToImageJob
      */
     public function fire(Job $job, $data)
     {
+
         $logId = $data['log_id'] ?? null;
 
         try {
@@ -68,7 +69,8 @@ class TextToImageJob
                         $row["chinese_description"],
                         $row["img_name"],
                         $data["selectedOption"],
-                        $data["executeKeywords"]
+                        $data["executeKeywords"],
+                        $data['sourceDir']
                     );
 
                     // 标准化结果文本
@@ -147,8 +149,10 @@ class TextToImageJob
      * 文生图处理函数
      * 描述:根据提示词调用图像生成接口,保存图像文件,并更新数据库
      */
-    public function textToImage($fileName, $outputDirRaw, $width, $height, $prompt, $img_name, $selectedOption,$executeKeywords)
+    public function textToImage($fileName, $outputDirRaw, $width, $height, $prompt, $img_name, $selectedOption,$executeKeywords,$sourceDir)
     {
+
+
         $rootPath = str_replace('\\', '/', ROOT_PATH);
         $outputDir = rtrim($rootPath . 'public/' . $outputDirRaw, '/') . '/';
         $dateDir = date('Y-m-d') . '/';
@@ -194,97 +198,60 @@ class TextToImageJob
 
         $template = Db::name('template')
             ->field('id,english_content,content')
-            ->where('path',$fileName)
+            ->where('path',$sourceDir)
             ->where('ids',1)
             ->find();
 
         // AI 图像生成调用
         $ai = new AIGatewayService();
-        $response = $ai->callDalleApi($template['content'].$prompt, $selectedOption);
 
-        if($response['result']){
-// echo "<pre>";
-// print_r($response);
-// echo "<pre>";
-//             sleep(180);
-
-//             $imageUrl = $this->getImageSeed($response['result']);
-//             // echo "<pre>";
-//             // print_r($imageUrl);
-//             // echo "<pre>";
-//             if (!$imageUrl['data']['imageUrl']) {
-//                 throw new Exception('未找到图像');
-//             }
-
-//             $img_name = mb_substr(preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name), 0, 30);
-//             $filename = $img_name . '.png';
-//             $path512 = $fullBaseDir . '2048x2048/' . $filename;
-// // echo "<pre>";
-// // print_r($imageUrl['data']['imageUrl']);
-// // echo "<pre>";
-//             // 下载并保存图片
-//             file_get_contents($imageUrl['data']['imageUrl']);
-
-            // 数据库更新
-            Db::name('text_to_image')->where('id', $record['id'])->update([
-                // 'new_image_url' => str_replace($rootPath . 'public/', '', $path512),
-                'new_image_url' => $response['result'],
-                'taskId' => $response['result'],
-                'img_name' => $img_name,
-                'model' => $selectedOption,
-                'status' => trim($img_name) === '' ? 0 : 1,
-                'status_name' => "文生图",
-                'size' => "2028x2048",
-                'quality' => 'standard',
-                'style' => 'vivid',
-                'error_msg' => '',
-                'update_time' => date('Y-m-d H:i:s')
-            ]);
-            return "成功";
 
+        $response = $ai->callDalleApi($template['content'].$prompt, $selectedOption);
 
 
-        }else{
 
+        if (isset($response['error'])) {
+            throw new \Exception("❌ 图像生成失败:" . $response['error']['message']);
+        }
 
-            if (isset($response['error'])) {
-                throw new \Exception("❌ 图像生成失败:" . $response['error']['message']);
-            }
-            // 支持 URL 格式(为主)和 base64
-            $imgData = null;
-            if (isset($response['data'][0]['url'])) {
-                $imgData = @file_get_contents($response['data'][0]['url']);
-            } elseif (isset($response['data'][0]['b64_json'])) {
-                $imgData = base64_decode($response['data'][0]['b64_json']);
-            }
-            if (!$imgData || strlen($imgData) < 1000) {
-                throw new \Exception("❌ 图像内容为空或异常!");
-            }
-            // 保存文件路径定义
-            $img_name = mb_substr(preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name), 0, 30);
-            $filename = $img_name . '.png';
-            $path512 = $fullBaseDir . '1024x1024/' . $filename;
-            $pathCustom = $fullBaseDir . "{$width}x{$height}/" . $filename;
-            // 保存原图
-            file_put_contents($path512, $imgData);
-            // 数据库更新
-            Db::name('text_to_image')->where('id', $record['id'])->update([
-                'new_image_url' => str_replace($rootPath . 'public/', '', $path512),
-                // 注释以下一行则不保存裁剪路径(适配你的配置)
-                // 'custom_image_url' => str_replace($rootPath . 'public/', '', $pathCustom),
-                'img_name' => $img_name,
-                'model' => $selectedOption,
-                'status' => trim($img_name) === '' ? 0 : 1,
-                'status_name' => "文生图",
-                'size' => "{$width}x{$height}",
-                'quality' => 'standard',
-                'style' => 'vivid',
-                'error_msg' => '',
-                'update_time' => date('Y-m-d H:i:s')
-            ]);
-            return "成功";
+        // 支持 URL 格式(为主)和 base64
+        $imgData = null;
+        if (isset($response['data'][0]['url'])) {
+            $imgData = @file_get_contents($response['data'][0]['url']);
+        } elseif (isset($response['data'][0]['b64_json'])) {
+            $imgData = base64_decode($response['data'][0]['b64_json']);
+        }
+        if (!$imgData || strlen($imgData) < 1000) {
+            throw new \Exception("❌ 图像内容为空或异常!");
         }
 
+        // 保存文件路径定义
+        $img_name = mb_substr(preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name), 0, 30);
+        $filename = $img_name . '.png';
+        $path512 = $fullBaseDir . '1024x1024/' . $filename;
+        $pathCustom = $fullBaseDir . "{$width}x{$height}/" . $filename;
+
+        // 保存原图
+        file_put_contents($path512, $imgData);
+
+        // 数据库更新
+        Db::name('text_to_image')->where('id', $record['id'])->update([
+            'new_image_url' => str_replace($rootPath . 'public/', '', $path512),
+            // 注释以下一行则不保存裁剪路径(适配你的配置)
+            // 'custom_image_url' => str_replace($rootPath . 'public/', '', $pathCustom),
+            'img_name' => $img_name,
+            'model' => $selectedOption,
+            'status' => trim($img_name) === '' ? 0 : 1,
+            'status_name' => "文生图",
+            'size' => "{$width}x{$height}",
+            'quality' => 'standard',
+            'style' => 'vivid',
+            'error_msg' => '',
+            'update_time' => date('Y-m-d H:i:s')
+        ]);
+
+        return "成功";
+
     }
 
 

+ 3 - 3
application/job/TextToTextJob.php

@@ -46,7 +46,7 @@ class TextToTextJob
                     echo "处理时间:{$currentTime}\n";
                     echo "👉 正在处理第 " . ($index + 1) . " 条,ID: {$row['id']}\n";
 
-                    $result = $this->textToTxt($row['id'],$data["txttotxt_selectedOption"],$fullPath);
+                    $result = $this->textToTxt($row['id'],$data["txttotxt_selectedOption"],$fullPath,$data['sourceDir']);
                     echo $result;
                     echo "✅ 处理结果:完成\n";
                     echo "完成时间:" . date('Y-m-d H:i:s') . "\n";
@@ -108,11 +108,11 @@ class TextToTextJob
      * @param int $id text_to_image 表主键
      * @return string
      */
-    public function textToTxt($id,$txttotxt_selectedOption,$fullPath)
+    public function textToTxt($id,$txttotxt_selectedOption,$fullPath,$sourceDir)
     {
         $template = Db::name('template')
             ->field('id,english_content,content')
-            ->where('path',$fullPath)
+            ->where('path',$sourceDir)
             ->where('ids',1)
             ->find();
 

+ 80 - 28
application/service/AIGatewayService.php

@@ -11,27 +11,32 @@ class AIGatewayService{
      * - api_url:对应功能的服务端地址
      */
     protected $config = [
-        //图生文-gemini-2.5-flash-preview
+        //图生文 gemini-2.5-flash-preview
         'imgtotxt' => [
             'api_key' => 'sk-LVcDfTx5SYK6pWiGpfcAN2KA0LunymnMiYSVfzUKQXrjlkZv',
             'api_url' => 'https://chatapi.onechats.top/v1/chat/completions'
         ],
-        //文生文-gtp-4
+        //文生文 gtp-4
         'txttotxtgtp' => [
             'api_key' => 'sk-fxlawqVtbbQbNW0wInR3E4wsLo5JHozDC2XOHzMa711su6ss',
             'api_url' => 'https://chatapi.onechats.top/v1/chat/completions'
         ],
-        //文生文-gemini-2.0-flash
+        //文生文 gemini-2.0-flash
         'txttotxtgemini' => [
             'api_key' => 'sk-cqfCZFiiSIdpDjIHLMBbH6uWfeg7iVsASvlubjrNEmfUXbpX',
             'api_url' => 'https://chatapi.onechats.top/v1/chat/completions'
         ],
-        //文生图-dall-e-3
+        //文生图 black-forest-labs/FLUX.1-kontext-pro、dall-e-3、gpt-image-1
         'txttoimg' => [
-            // 'api_key' => 'sk-MB6SR8qNaTjO80U7HJl4ztivX3zQKPgKVka9oyfVSXIkHSYZ',
-            'api_key' => 'sk-iURfrAgzAjhZ4PpPLwzmWIAhM7zKfrkwDvyxk4RVBQ4ouJNK',
+            'api_key' => 'sk-MB6SR8qNaTjO80U7HJl4ztivX3zQKPgKVka9oyfVSXIkHSYZ',
             'api_url' => 'https://chatapi.onechats.ai/v1/images/generations'
         ],
+        //gemini-3-pro-image-preview
+        'txttoimgfour' => [
+            'api_key' => 'sk-8yavOx3JxkiNqfgONHS4eSjFDLfErKj9W91PJc8Qvw8y95sT',
+            'api_url' => 'https://chatapi.onechats.ai/v1beta/models/gemini-3-pro-image-preview:streamGenerateContent'
+        ],
+        //文生图 MID_JOURNEY
         'submitimage' => [
             'api_key' => 'sk-iURfrAgzAjhZ4PpPLwzmWIAhM7zKfrkwDvyxk4RVBQ4ouJNK',
             'api_url' => 'https://chatapi.onechats.ai/mj/submit/imagine'
@@ -89,6 +94,7 @@ class AIGatewayService{
         if (empty($prompt)) {
             throw new \Exception("Prompt 不允许为空");
         }
+
         //判断使用模型
         if ($txttotxt_selectedOption === 'gemini-2.0-flash') {
             $data = [
@@ -170,17 +176,6 @@ class AIGatewayService{
                 'response_format' => 'url',
             ];
             return $this->callApi($this->config['txttoimg']['api_url'],$this->config['txttoimg']['api_key'],$data);
-        } else if ($selectedOption === 'gpt-image-1') {
-            $data = [
-                'prompt'  => $prompt,
-                'model'   => $selectedOption,
-                'n'       => 1,
-                'size'    => '1024x1024',
-                'quality' => 'hd',
-                'style'   => 'vivid',
-                'response_format' => 'url',
-            ];
-            return $this->callApi($this->config['txttoimg']['api_url'],$this->config['txttoimg']['api_key'],$data);
         } else if ($selectedOption === 'MID_JOURNEY') {
             $data = [
                 'botType' => $selectedOption,
@@ -198,8 +193,43 @@ class AIGatewayService{
                 'state' => ""
             ];
             return $this->callApi($this->config['submitimage']['api_url'],$this->config['submitimage']['api_key'],$data);
+        }else if ($selectedOption === 'gpt-image-1') {
+            $data = [
+                'prompt'  => $prompt,
+                'model'   => $selectedOption,
+                'n'       => 1,
+                'size'    => '1024x1024',
+                'quality' => 'hd',
+                'style'   => 'vivid',
+                'response_format' => 'url',
+            ];
+            return $this->callApi($this->config['txttoimg']['api_url'],$this->config['txttoimg']['api_key'],$data);
+        } else if ($selectedOption === 'gemini-3-pro-image-preview') {
+            // 使用Google Gemini模型的正确参数格式,与curl命令保持一致
+            $data = [
+                "contents" => [
+                    [
+                        "role" => "user",
+                        "parts" => [
+                            ["text" => $prompt]
+                        ]
+                    ]
+                ],
+                "generationConfig" => [
+                    "responseModalities" => ["TEXT", "IMAGE"],
+                    "imageConfig" => [
+                        "aspectRatio" => "16:9"
+                        // 移除imageSize字段,与curl命令保持一致
+                    ]
+                ]
+            ];
+            return $this->callApi($this->config['txttoimgfour']['api_url'],$this->config['txttoimgfour']['api_key'],$data);
         }else{
-            echo '其他文生图模型参数配置';
+            return [
+                'code' => 400,
+                'msg' => '未配置的文生图模型: ' . $selectedOption,
+                'data' => null
+            ];
         }
     }
 
@@ -597,7 +627,7 @@ class AIGatewayService{
                     CURLOPT_SSL_VERIFYHOST => 2,
                     CURLOPT_CONNECTTIMEOUT => 30,
                     CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
-                    CURLOPT_FAILONERROR => true
+                    CURLOPT_FAILONERROR => false
                 ]);
 
                 $response = curl_exec($ch);
@@ -605,6 +635,10 @@ class AIGatewayService{
                 $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);
                 }
 
@@ -614,6 +648,7 @@ class AIGatewayService{
                 if (isset($result['error'])) {
                     $apiErrorDetail = $result['error']['message'] ?? '';
                     $errorType = $result['error']['type'] ?? '';
+                    $errorCode = $result['error']['code'] ?? '';
 
                     // 常见错误类型映射
                     $errorMessages = [
@@ -622,11 +657,23 @@ class AIGatewayService{
                         'rate_limit_error' => '请求频率过高',
                         'insufficient_quota' => '额度不足',
                         'billing_not_active' => '账户未开通付费',
-                        'content_policy_violation' => '内容违反政策'
+                        'content_policy_violation' => '内容违反政策',
+                        'model_not_found' => '模型不存在或无可用渠道'
                     ];
 
-                    $friendlyMessage = $errorMessages[$errorType] ?? 'API服务错误';
-                    throw new \Exception("{$friendlyMessage}: {$apiErrorDetail}");
+                    // 优先使用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) {
@@ -663,10 +710,13 @@ class AIGatewayService{
                         '重试次数' => $attempt
                     ];
 
-                    throw new \Exception("API请求失败\n" .
-                        "失败说明: " . $errorDetails['错误原因'] . "\n" .
-                        "建议解决方案: " . $errorDetails['解决方案'] . "\n" .
-                        "技术详情: HTTP {$httpCode} - " . $lastError);
+                    // 构建最终的错误信息,优先显示原始的详细错误消息
+                    $finalError = "API请求失败\n";
+                    $finalError .= "失败说明: " . $lastError . "\n"; // 使用原始的详细错误消息
+                    $finalError .= "建议解决方案: " . $errorDetails['解决方案'] . "\n";
+                    $finalError .= "技术详情: HTTP {$httpCode} - " . $errorDetails['错误原因'];
+
+                    throw new \Exception($finalError);
                 }
             }
         }
@@ -682,7 +732,8 @@ class AIGatewayService{
             400 => $apiError ?: '请求参数不符合API要求',
             429 => '已达到API调用频率限制',
             403 => '您的账户可能没有开通相关服务权限',
-            500 => 'OpenAI服务器处理请求时出错'
+            500 => 'OpenAI服务器处理请求时出错',
+            503 => 'API服务暂时不可用,可能是服务器维护或负载过高'
         ];
 
         return $causes[$httpCode] ?? '未知错误,请检查网络连接和API配置';
@@ -698,7 +749,8 @@ class AIGatewayService{
             400 => '1. 检查请求参数 2. 验证提示词内容 3. 参考API文档修正参数',
             429 => '1. 等待1分钟后重试 2. 升级账户提高限额 3. 优化调用频率',
             403 => '1. 检查账户状态 2. 确认是否已开通付费 3. 联系OpenAI支持',
-            500 => '1. 等待几分钟后重试 2. 检查OpenAI服务状态页'
+            500 => '1. 等待几分钟后重试 2. 检查OpenAI服务状态页',
+            503 => '1. 等待几分钟后重试 2. 检查API服务提供商状态 3. 联系服务提供商确认服务可用性'
         ];
 
         return $solutions[$httpCode] ?? '1. 检查网络连接 2. 查看服务日志 3. 联系技术支持';

+ 4 - 2
application/service/ImageService.php

@@ -27,9 +27,12 @@ class ImageService{
          * */
         $template = Db::name('template')
             ->field('id,english_content,content,ids')
+            ->where('path',$params['old_image_file'])
             ->where('ids',1)
             ->find();
 
+
+
         // 构建任务基础结构(每图生成 N 份任务)
         foreach ($batch as $k => $v) {
             $baseItem = [
@@ -40,7 +43,7 @@ class ImageService{
                 "selectedOption" => $params['selectedOption'],//文生图模型
                 "txttotxt_selectedOption" => $params['txttotxt_selectedOption'],//文生文模型
                 "imgtotxt_selectedOption" => $params['imgtotxt_selectedOption'],//图生文模型
-                "prompt" => $template['content'],
+                "prompt" => '',
                 "width" => $params['width'],
                 "height" => $params['height'],
                 "executeKeywords" => $params['executeKeywords'],//是否执行几何图
@@ -118,7 +121,6 @@ class ImageService{
                 return $item;
             }, $arr);
 
-
             // 投递任务到队列
             $payload = [
                 'task_id' => $task_id,