liuhairui 6 сар өмнө
parent
commit
c8481534f6

+ 268 - 151
application/api/controller/Facility.php

@@ -77,13 +77,11 @@ class Facility extends Api
         // 获取前端传入的图片路径参数
         $params = $this->request->param('path', '');
         // 查询数据库
-        $res = Db::name('text_to_image')->alias('b')
-            ->field('b.chinese_description,b.english_description,b.new_image_url,b.custom_image_url,b.size,b.old_image_url,b.img_name')
+        $res = Db::name('text_to_image')
+            ->field('id,chinese_description,english_description,new_image_url,custom_image_url,size,old_image_url,img_name,model')
             ->where('old_image_url', $params)
             ->where('img_name', '<>', '')
-            // ->where('custom_image_url', '<>', '')
-            // ->where('status', 1)
-            ->order('b.id desc')
+            ->order('id desc')
             ->select();
         return json(['code' => 0, 'msg' => '查询成功', 'data' => $res,'count'=>count($res)]);
     }
@@ -95,88 +93,92 @@ class Facility extends Api
         $page = (int)$this->request->param('page', 1);
         $limit = (int)$this->request->param('limit', 50);
         $status = $this->request->param('status', '');
-
         $relativePath = $this->request->param('path', '');
+
         $basePath = ROOT_PATH . 'public/';
         $fullPath = $basePath . $relativePath;
 
         if (!is_dir($fullPath)) {
-            return json(['code' => 1, 'msg' => '目录不存在']);
+            return json(['code' => 1, 'msg' => '原图目录不存在']);
         }
 
-        // 1. 获取所有图片路径(不再全部加载到内存)
+        // 1. 获取所有图片路径
         $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
         if (empty($allImages)) {
             return json(['code' => 0, 'msg' => '暂无图片', 'data' => [], 'total' => 0]);
         }
-        // ✅ 加入排序:按照创建时间从新到旧
-        usort($allImages, function ($a, $b) {
-            return filectime($b) - filectime($a);
-        });
-        // 构建相对路径数组
-        $relativeImages = array_map(function ($imgPath) use ($basePath) {
-            return str_replace($basePath, '', $imgPath);
-        }, $allImages);
 
-        // 2. 提前构建是否已出图map
+        // 2. 构建路径信息映射(路径 => 文件信息)
+        $imageInfoMap = [];
+        foreach ($allImages as $imgPath) {
+            $relative = str_replace('\\', '/', trim(str_replace($basePath, '', $imgPath), '/'));
+            $info = @getimagesize($imgPath);
+            $imageInfoMap[$relative] = [
+                'width' => $info[0] ?? 0,
+                'height' => $info[1] ?? 0,
+                'size_kb' => round(filesize($imgPath) / 1024, 2),
+                'created_time' => date('Y-m-d H:i:s', filectime($imgPath))
+            ];
+        }
+
+        $relativeImages = array_keys($imageInfoMap);
+
+        // 3. 获取数据库记录
         $dbRecords = Db::name('text_to_image')
             ->whereIn('old_image_url', $relativeImages)
-            ->where('img_name', '<>', '')
-            ->where('custom_image_url', '<>', '')
-            ->where('status',1)
-            ->field('old_image_url,new_image_url,custom_image_url,chinese_description,english_description,img_name')
+            ->field('id as img_id, old_image_url, new_image_url, custom_image_url, chinese_description, english_description, img_name, status')
             ->select();
 
+        // 4. 构建映射表:路径 => 整条数据库记录
         $processedMap = [];
         foreach ($dbRecords as $item) {
-            $processedMap[$item['old_image_url']] = $item;
+            $key = str_replace('\\', '/', trim($item['old_image_url'], '/'));
+            $processedMap[$key] = $item;
         }
 
-        // 3. 提前获取 same_count 的统计
+        // 5. 获取 same_count 统计
         $sameCountMap = Db::name('text_to_image')
             ->whereIn('old_image_url', $relativeImages)
-            ->where('img_name', '<>', '')
-            ->where('custom_image_url', '<>', '')
+            ->where('status', 1)
             ->group('old_image_url')
-            ->where('status',1)
             ->column('count(*) as cnt', 'old_image_url');
 
-        // 4. 构造最终筛选数据(分页前进行状态筛选)
-        $filtered = [];
-        foreach ($allImages as $imgPath) {
-            $relative = str_replace($basePath, '', $imgPath);
-            $processed = $processedMap[$relative] ?? null;
-            $isProcessed = $processed ? 1 : 0;
+        // 6. 构建结果数据
+        $allData = [];
+        foreach ($relativeImages as $path) {
+            $item = $processedMap[$path] ?? [];
+            $info = $imageInfoMap[$path];
 
-            // 状态过滤
-            if ($status === 'processed' && !$isProcessed) continue;
-            if ($status === 'unprocessed' && $isProcessed) continue;
+            $isProcessed = !empty($item['img_name']) && !empty($item['custom_image_url']);
+            $dbStatus = isset($item['status']) ? (int)$item['status'] : 0;
 
-            $info = @getimagesize($imgPath); // 加@防止报错
-            $sizeKB = round(filesize($imgPath) / 1024, 2);
-            $ctime = date('Y-m-d H:i:s', filectime($imgPath));
+            // 状态过滤(0:未出图,1:已出图)
+            if ($status !== '' && (int)$status !== $dbStatus) {
+                continue;
+            }
 
-            $filtered[] = [
-                'path' => $relative,
-                'width' => $info[0] ?? 0,
-                'height' => $info[1] ?? 0,
-                'size_kb' => $sizeKB,
-                'created_time' => $ctime,
-                'img_name' => $processed['img_name'] ?? '',
-                'is_processed' => $isProcessed,
-                'new_image_url' => $processed['new_image_url'] ?? '',
-                'custom_image_url' => $processed['custom_image_url'] ?? '',
-                'chinese_description' => ($processed['chinese_description'] ?? '') . ($processed['english_description'] ?? ''),
-                'english_description' => ($processed['english_description'] ?? '') . ($processed['english_description'] ?? ''),
-                'same_count' => $sameCountMap[$relative] ?? 0
+            $allData[] = [
+                'path' => $path,//原图路径
+                'status' => $dbStatus,//状态
+                'same_count' => $sameCountMap[$path] ?? 0, // 出图数量
+                'is_processed' => $isProcessed ? 1 : 0,
+                'new_image_url' => $item['new_image_url'] ?? '',
+                'custom_image_url' => $item['custom_image_url'] ?? '',
+                'chinese_description' => $item['chinese_description'] ?? '',
+                'english_description' => $item['english_description'] ?? '',
+                'img_name' => $item['img_name'] ?? '',
+                'width' => $info['width'],
+                'height' => $info['height'],
+                'size_kb' => $info['size_kb'],
+                'created_time' => $info['created_time']
             ];
         }
 
-        // 5. 手动分页(对少量已筛选后的数据)
-        $total = count($filtered);
-        $pagedData = array_slice($filtered, ($page - 1) * $limit, $limit);
-        foreach ($pagedData as $index => &$item) {
-            $item['id'] = ($page - 1) * $limit + $index + 1;
+        // 7. 分页处理
+        $total = count($allData);
+        $pagedData = array_slice(array_values($allData), ($page - 1) * $limit, $limit);
+        foreach ($pagedData as $i => &$row) {
+            $row['id'] = ($page - 1) * $limit + $i + 1;
         }
 
         return json([
@@ -189,6 +191,215 @@ class Facility extends Api
         ]);
     }
 
+
+
+
+//    public function getPreviewimg()
+//    {
+//        $page = (int)$this->request->param('page', 1);
+//        $limit = (int)$this->request->param('limit', 50);
+//        $status = $this->request->param('status', '');
+//        $relativePath = $this->request->param('path', '');
+//
+//        $basePath = ROOT_PATH . 'public/';
+//        $fullPath = $basePath . $relativePath;
+//
+//        if (!is_dir($fullPath)) {
+//            return json(['code' => 1, 'msg' => '原图目录不存在']);
+//        }
+//
+//        // 1. 获取所有图片路径
+//        $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+//        if (empty($allImages)) {
+//            return json(['code' => 0, 'msg' => '暂无图片', 'data' => [], 'total' => 0]);
+//        }
+//
+//        // 2. 构建路径信息映射(路径 => 文件信息)
+//        $imageInfoMap = [];
+//        foreach ($allImages as $imgPath) {
+//            $relative = str_replace('\\', '/', trim(str_replace($basePath, '', $imgPath), '/'));
+//            $info = @getimagesize($imgPath);
+//            $imageInfoMap[$relative] = [
+//                'width' => $info[0] ?? 0,
+//                'height' => $info[1] ?? 0,
+//                'size_kb' => round(filesize($imgPath) / 1024, 2),
+//                'created_time' => date('Y-m-d H:i:s', filectime($imgPath))
+//            ];
+//        }
+//
+//        $relativeImages = array_keys($imageInfoMap);
+//
+//        // 3. 获取数据库记录
+//        $dbRecords = Db::name('text_to_image')
+//            ->whereIn('old_image_url', $relativeImages)
+//            ->field('id as img_id, old_image_url, new_image_url, custom_image_url, chinese_description, english_description, img_name,status')
+//            ->select();
+//
+//        // 4. 构建映射表:路径 => 整条数据库记录
+//        $processedMap = [];
+//        foreach ($dbRecords as $item) {
+//            $key = str_replace('\\', '/', trim($item['old_image_url'], '/'));
+//            $processedMap[$key] = $item;
+//        }
+//
+//        // 5. 获取 same_count 统计
+//        $sameCountMap = Db::name('text_to_image')
+//            ->whereIn('old_image_url', $relativeImages)
+//            ->where('status', 1)
+//            ->group('old_image_url')
+//            ->column('count(*) as cnt', 'old_image_url');
+//
+//        // 6. 构建结果数据
+//        $allData = [];
+//        foreach ($relativeImages as $path) {
+//            $item = $processedMap[$path] ?? [];
+//            $info = $imageInfoMap[$path];
+//
+//            $isProcessed = !empty($item['img_name']) && !empty($item['custom_image_url']);
+//
+//            // 状态过滤
+//            if ($status === '1') {
+//                if (!$isProcessed) continue;
+//            } elseif ($status === '2') {
+//                if ($isProcessed) continue;
+//            }
+//
+//            $allData[] = [
+//                'path' => $path,
+//                'status' => (int)($item['status'] ?? 0),
+//                'same_count' => $sameCountMap[$path] ?? 0,//出图数量
+//                'is_processed' => $isProcessed ? 1 : 0,
+//                'new_image_url' => $item['new_image_url'] ?? '',
+//                'custom_image_url' => $item['custom_image_url'] ?? '',
+//                'chinese_description' => $item['chinese_description'] ?? '',
+//                'english_description' => $item['english_description'] ?? '',
+//                'img_name' => $item['img_name'] ?? '',
+//                'width' => $info['width'],
+//                'height' => $info['height'],
+//                'size_kb' => $info['size_kb'],
+//                'created_time' => $info['created_time']
+//            ];
+//        }
+//
+//        // 7. 分页处理
+//        $total = count($allData);
+//        $pagedData = array_slice(array_values($allData), ($page - 1) * $limit, $limit);
+//        foreach ($pagedData as $i => &$row) {
+//            $row['id'] = ($page - 1) * $limit + $i + 1;
+//        }
+//
+//        return json([
+//            'code' => 0,
+//            'msg' => '获取成功',
+//            'data' => $pagedData,
+//            'total' => $total,
+//            'page' => $page,
+//            'limit' => $limit
+//        ]);
+//    }
+
+
+
+//    public function getPreviewimg()
+//    {
+//        $page = (int)$this->request->param('page', 1);
+//        $limit = (int)$this->request->param('limit', 50);
+//        $status = $this->request->param('status', '');
+//
+//        $relativePath = $this->request->param('path', '');
+//        $basePath = ROOT_PATH . 'public/';
+//        $fullPath = $basePath . $relativePath;
+//
+//        if (!is_dir($fullPath)) {
+//            return json(['code' => 1, 'msg' => '目录不存在']);
+//        }
+//
+//        // 1. 获取所有图片路径(不再全部加载到内存)
+//        $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+//        if (empty($allImages)) {
+//            return json(['code' => 0, 'msg' => '暂无图片', 'data' => [], 'total' => 0]);
+//        }
+//        // ✅ 加入排序:按照创建时间从新到旧
+//        usort($allImages, function ($a, $b) {
+//            return filectime($b) - filectime($a);
+//        });
+//        // 构建相对路径数组
+//        $relativeImages = array_map(function ($imgPath) use ($basePath) {
+//            return str_replace($basePath, '', $imgPath);
+//        }, $allImages);
+//
+//        // 2. 提前构建是否已出图map
+//        $dbRecords = Db::name('text_to_image')
+//            ->whereIn('old_image_url', $relativeImages)
+//            ->where('img_name', '<>', '')
+//            ->where('custom_image_url', '<>', '')
+//            ->where('status',1)
+//            ->field('id,old_image_url,new_image_url,custom_image_url,chinese_description,english_description,img_name')
+//            ->select();
+//
+//        $processedMap = [];
+//        foreach ($dbRecords as $item) {
+//            $processedMap[$item['old_image_url']] = $item;
+//        }
+//
+//        // 3. 提前获取 same_count 的统计
+//        $sameCountMap = Db::name('text_to_image')
+//            ->whereIn('old_image_url', $relativeImages)
+//            ->where('img_name', '<>', '')
+//            ->where('custom_image_url', '<>', '')
+//            ->group('old_image_url')
+//            ->where('status',1)
+//            ->column('count(*) as cnt', 'old_image_url');
+//
+//        // 4. 构造最终筛选数据(分页前进行状态筛选)
+//        $filtered = [];
+//        foreach ($allImages as $imgPath) {
+//            $relative = str_replace($basePath, '', $imgPath);
+//            $processed = $processedMap[$relative] ?? null;
+//            $isProcessed = $processed ? 1 : 0;
+//
+//            // 状态过滤
+//            if ($status === 'processed' && !$isProcessed) continue;
+//            if ($status === 'unprocessed' && $isProcessed) continue;
+//
+//            $info = @getimagesize($imgPath); // 加@防止报错
+//            $sizeKB = round(filesize($imgPath) / 1024, 2);
+//            $ctime = date('Y-m-d H:i:s', filectime($imgPath));
+//
+//            $filtered[] = [
+//                'path' => $relative,
+//                'width' => $info[0] ?? 0,
+//                'height' => $info[1] ?? 0,
+//                'size_kb' => $sizeKB,
+//                'created_time' => $ctime,
+//                'img_name' => $processed['img_name'] ?? '',
+//                'is_processed' => $isProcessed,
+//                'new_image_url' => $processed['new_image_url'] ?? '',
+//                'custom_image_url' => $processed['custom_image_url'] ?? '',
+//                'chinese_description' => ($processed['chinese_description'] ?? '') . ($processed['english_description'] ?? ''),
+//                'english_description' => ($processed['english_description'] ?? '') . ($processed['english_description'] ?? ''),
+//                'same_count' => $sameCountMap[$relative] ?? 0
+//            ];
+//        }
+//
+//        // 5. 手动分页(对少量已筛选后的数据)
+//        $total = count($filtered);
+//        $pagedData = array_slice($filtered, ($page - 1) * $limit, $limit);
+//        foreach ($pagedData as $index => &$item) {
+//            $item['id'] = ($page - 1) * $limit + $index + 1;
+//        }
+//
+//        return json([
+//            'code' => 0,
+//            'msg' => '获取成功',
+//            'data' => $pagedData,
+//            'total' => $total,
+//            'page' => $page,
+//            'limit' => $limit
+//        ]);
+//    }
+
+
     /**
      * 获取原图目录及每个目录下的图片数量
      */
@@ -274,105 +485,10 @@ class Facility extends Api
     }
 
 
-
-
-
-
     /**
      * 图片上传
      * @return void
      */
-//    public function getUploadPath()
-//    {
-//        // 处理 CORS OPTIONS 预检请求
-//        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
-//            header('Access-Control-Allow-Origin: *');
-//            header('Access-Control-Allow-Methods: POST, OPTIONS');
-//            header('Access-Control-Allow-Headers: Content-Type, Authorization');
-//            header('Access-Control-Max-Age: 86400');
-//            exit(204);
-//        }
-//
-//// 实际请求必须返回 CORS 头
-//        header('Access-Control-Allow-Origin: *');
-//        $today = date('Ymd');
-//        $basePath = 'uploads/operate/ai/Preview/' . $today;
-//        $rootBasePath = ROOT_PATH . 'public/' . $basePath;
-//
-//        // 创建当天目录
-//        if (!is_dir($rootBasePath)) {
-//            mkdir($rootBasePath, 0755, true);
-//        }
-//
-//        // 获取子目录索引
-//        $dirs = array_filter(glob($rootBasePath . '/*'), 'is_dir');
-//        $usedIndexes = [];
-//
-//        foreach ($dirs as $dirPath) {
-//            $dirName = basename($dirPath);
-//            if (preg_match('/^\d{2}$/', $dirName)) {
-//                $usedIndexes[] = intval($dirName);
-//            }
-//        }
-//
-//        $nextIndex = empty($usedIndexes) ? 1 : max($usedIndexes) + 1;
-//        $subDir = str_pad($nextIndex, 2, '0', STR_PAD_LEFT);
-//        $relativePath = $basePath . '/' . $subDir;
-//        $targetPath = ROOT_PATH . 'public/' . $relativePath;
-//
-//        // 创建该批次目录
-//        if (!is_dir($targetPath)) {
-//            mkdir($targetPath, 0755, true);
-//        }
-//
-//        return json([
-//            'code' => 0,
-//            'msg' => '获取上传路径成功',
-//            'data' => [
-//                'upload_path' => $relativePath
-//            ]
-//        ]);
-//    }
-//    public function ImgUpload()
-//    {
-//        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
-//            header('Access-Control-Allow-Origin: *');
-//            header('Access-Control-Allow-Methods: POST, OPTIONS');
-//            header('Access-Control-Allow-Headers: Content-Type, Authorization');
-//            header('Access-Control-Max-Age: 86400');
-//            exit(204);
-//        }
-//        header('Access-Control-Allow-Origin: *');
-//
-//        $file = request()->file('image');
-//        $relativePath = input('post.upload_path');
-//
-//        if (!$file || !$relativePath) {
-//            return json(['code' => 1, 'msg' => '缺少上传文件或路径参数']);
-//        }
-//
-//        $targetPath = ROOT_PATH . 'public/' . $relativePath;
-//
-//        if (!is_dir($targetPath)) {
-//            mkdir($targetPath, 0755, true);
-//        }
-//
-//        $tmpFilePath = $file->getPathname();
-//        $extension = pathinfo($file->getInfo('name'), PATHINFO_EXTENSION);
-//        $hashName = hash_file('md5', $tmpFilePath);
-//        $newFileName = $hashName . '.' . $extension;
-//
-//        $info = $file->validate(['size' => 10 * 1024 * 1024, 'ext' => 'jpg,jpeg,png'])
-//            ->move($targetPath, $newFileName);
-//
-//        if ($info) {
-//            $imageUrl = $relativePath . '/' . str_replace('\\', '/', $newFileName);
-//            return json(['code' => 0, 'msg' => '上传成功', 'data' => ['url' => $imageUrl]]);
-//        } else {
-//            return json(['code' => 1, 'msg' => '上传失败', 'data' => $file->getError()]);
-//        }
-//    }
-
     public function ImgUpload()
     {
         // 处理 CORS OPTIONS 预检请求
@@ -450,6 +566,7 @@ class Facility extends Api
         $Template = Db::name("template")
             ->where('id', 1) // 假设模板 ID 是 1,需根据实际情况修改
             ->update([
+                'english_content' => $params['english_content'],  // 更新文生文模版内容
                 'content' => $params['textareaContent'],  // 更新图生文模版内容
                 'width' => $params['width'],  // 更新宽度
                 'height' => $params['height'],  // 更新宽度

+ 82 - 5
application/api/controller/WorkOrder.php

@@ -19,14 +19,94 @@ class WorkOrder extends Api
 
     /**
      * 出图接口
+     *
+     * 此方法处理图像转换为文本的请求,将图像信息存入队列以供后续处理。
      */
     public function imageToText()
     {
         $params = $this->request->param();
-
         $service = new ImageService();
         $service->handleImage($params);
-        $this->success('成功存入队列中');
+        $this->success('任务成功提交至队列');
+    }
+
+
+
+    //查询队列列表
+    public function get_queue_logs()
+    {
+        $params = $this->request->param('old_image_file', '');
+
+        // 查询 queue_logs 表
+        $queue_logs = Db::name('queue_logs')
+            ->where('old_image_file', $params)
+            ->order('id desc')
+            ->select();
+
+        foreach ($queue_logs as &$log) {
+            $taskId = $log['id'];
+
+            // 从 image_task_log 表统计状态
+            $statusCount = Db::name('image_task_log')
+                ->field('status, COUNT(*) as count')
+                ->where('task_id', $taskId)
+                ->group('status')
+                ->select();
+
+            // 初始化统计数据
+            $log['已完成数量'] = 0;
+            $log['处理中数量'] = 0;
+            $log['排队中的数量'] = 0;
+
+            // 遍历状态聚合数据
+            foreach ($statusCount as $item) {
+                switch ($item['status']) {
+                    case 0:
+                        $log['排队中的数量'] = $item['count'];
+                        break;
+                    case 1:
+                        $log['处理中数量'] = $item['count'];
+                        break;
+                    case 2:
+                        $log['已完成数量'] = $item['count'];
+                        break;
+                }
+            }
+        }
+
+        return json([
+            'code' => 0,
+            'msg' => '查询成功',
+            'data' => $queue_logs,
+            'count' => count($queue_logs)
+        ]);
+    }
+
+    //任务状态统计接口
+    public function getTaskProgress()
+    {
+        $params = $this->request->param('id', '1');
+
+        $task_id = Db::name('image_task_log')->order('id desc')->where('task_id',$params)->value('task_id');
+        $lsit = Db::name('image_task_log')->order('id desc')->where('task_id',$params)->select();
+
+
+        $total = Db::name('image_task_log')->where('task_id', $task_id)->count();
+        $processing = Db::name('image_task_log')->where('task_id', $task_id)->where('status', 1)->count();
+        $success = Db::name('image_task_log')->where('task_id', $task_id)->where('status', 2)->count();
+        $fail = Db::name('image_task_log')->where('task_id', $task_id)->where('status', -1)->count();
+
+        $status = Db::name('queue_logs')->where('id', $task_id)->value('status');
+
+        return json([
+            'data' => $lsit,
+            'task_id' => $task_id,
+            '总数' => $total,
+            '处理中' => $processing,
+            '已完成' => $success,
+            '失败' => $fail,
+            '当前状态' => $status
+        ]);
     }
 
     /**
@@ -42,12 +122,9 @@ class WorkOrder extends Api
                 'msg'  => '监听进程已存在,请勿重复启动'
             ]);
         }
-
         // 启动监听
-//        $command = 'php think queue:listen --queue --timeout=300 --sleep=3 --memory=256 > /dev/null 2>&1 &';
         $command = 'nohup php think queue:listen --queue --timeout=300 --sleep=3 --memory=256 > /var/log/job_queue.log 2>&1 &';
         exec($command, $output, $status);
-
         if ($status === 0) {
             return json([
                 'code' => 0,

+ 54 - 26
application/job/ImageArrJob.php

@@ -1,45 +1,73 @@
 <?php
-
 namespace app\job;
+
 use think\Db;
 use think\queue\Job;
 use think\Queue;
+
 class ImageArrJob
 {
     public function fire(Job $job, $data)
     {
-        //第一步:接收前端批量上传图片的任务
-        echo  date('Y-m-d H:i:s') ."批量推送任务已开始\n";
-
-        foreach ($data as $key => $value) {
-            // 判断 type 字段
-            if (isset($value['type']) && $value['type'] === '文生图') {
-                echo date('Y-m-d H:i:s') ."跳过图生文任务,批量执行文生图任务已开始\n";
-                Queue::push('app\job\TextToImageJob', $value, 'txttoimg');
-            } else {
-                // 示例:推送 ImageJob 队列
-                $id = Db::name('queue_log')->insertGetId([
-                    'job_name' => 'app\job\ImageJob',
-                    'status' => 0,
-                    'data' => json_encode($value, JSON_UNESCAPED_UNICODE),
-                    'created_at' => date('Y-m-d H:i:s'),
-                ]);
-                echo date('Y-m-d H:i:s') ."批量推送任务已结束,推送到图生文任务已开始\n";
-                $value['log_id'] = $id;
-                Queue::push('app\job\ImageJob', $value, 'imgtotxt');
+        $task_id = $data['task_id'];
+        $images = $data['data'];
+
+        foreach ($images as $value) {
+            $value['task_id'] = $task_id;
+
+            // 1. 清理 sourceDir,去掉末尾重复的 Preview
+            $sourceDir = rtrim($value["sourceDir"], '/\\');
+            $dirParts = explode('/', str_replace('\\', '/', $sourceDir));
+            if (end($dirParts) === 'Preview') {
+                array_pop($dirParts); // 删除末尾 Preview
+            }
+            $cleanedSourceDir = implode('/', $dirParts);
+
+            // 2. 获取纯文件名
+            $fileName = basename($value['file_name']);
+
+            // 3. 拼接最终路径
+            $fullPath = $cleanedSourceDir . '/' . $fileName;
+
+            // 4. 写入日志记录
+            $log_id = Db::name('image_task_log')->insertGetId([
+                'task_id' => $task_id,
+                'file_name' => $fullPath,
+                'type' => $value['type'] ?? '',
+                'status' => 0,
+                'log' => '队列中',
+                'create_time' => date('Y-m-d H:i:s')
+            ]);
+            $value['log_id'] = $log_id;
+
+            // 5. 推送任务到对应队列
+            switch (trim($value['type'])) {
+                case '图生文':
+                    Queue::push('app\job\ImageJob', $value, 'imgtotxt');
+                    break;
+                case '文生文':
+                    Queue::push('app\job\TextToTextJob', $value, 'txttotxt');
+                    break;
+                case '文生图':
+                    Queue::push('app\job\TextToImageJob', $value, 'txttoimg');
+                    break;
+                default:
+                    // 类型未知时不推送,并可写入错误日志
+                    \think\Log::warning("未识别的任务类型:" . json_encode($value, JSON_UNESCAPED_UNICODE));
+                    break;
             }
         }
 
-        // 最后删除任务
+        // 6. 更新任务状态为已启动
+        Db::name('queue_logs')->where('id', $task_id)->update(['status' => '已启动队列']);
+        echo date('Y-m-d H:i:s') . " 队列已启动\n";
+
+        // 7. 删除当前队列任务
         $job->delete();
     }
 
-    /**
-     * 任务失败时的处理
-     */
     public function failed($data)
     {
-        // 记录失败日志或发送通知
-        \think\Log::error("ImageJob failed: " . json_encode($data));
+        \think\Log::error("ImageArrJob 任务失败:" . json_encode($data, JSON_UNESCAPED_UNICODE));
     }
 }

+ 73 - 78
application/job/ImageJob.php

@@ -1,13 +1,9 @@
 <?php
-// 1. 正确的队列任务类 application/job/ImageJob.php
 namespace app\job;
-
 use think\Db;
 use think\queue\Job;
 use think\Queue;
-
-class ImageJob
-{
+class ImageJob{
     protected $config = [
         'gpt' => [
             'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
@@ -20,39 +16,39 @@ class ImageJob
     ];
 
     /**
-     * fire方法是队列默认调用的方法
-     * @param Job $job 当前的任务对象
-     * @param array|mixed $data 发布任务时自定义的数据
+     * 图生文
      */
     public function fire(Job $job, $data)
     {
-        echo date('Y-m-d H:i:s')."图生文开始\n";
-//        echo "接收的数据: " . json_encode($data) . "\n";
+        // echo "<pre>";
+        // print_r($data);
+        // echo "<pre>";die;
 
         $logId = $data['log_id'] ?? null;
+
         try {
-            //录日志状态为“正在处理”
+            echo date('Y-m-d H:i:s')."图生文开始\n";
+
             if ($logId) {
-                Db::name('queue_log')->where('id', $logId)->update([
-                    'status'     => 1,
-                    'log'        => '图生文正在处理',
-                    'updated_at' => date('Y-m-d H:i:s')
+                Db::name('image_task_log')->where('id', $logId)->update([
+                    'status' => 1,
+                    'log' => '图生文处理',
+                    'update_time' => date('Y-m-d H:i:s')
                 ]);
             }
 
-            // 执行业务逻辑
-            $str = $this->processImage($data);
-            echo $str;
-            echo date('Y-m-d H:i:s')."图生文结束\n";
+            $result = $this->processImage($data);
+            echo $result;
 
             if ($logId) {
-                Db::name('queue_log')->where('id', $logId)->update([
-                    'status'     => 2,
-                    'log'        => '图生文执行成功',
-                    'updated_at' => date('Y-m-d H:i:s')
+                Db::name('image_task_log')->where('id', $logId)->update([
+                    'status' => 2,
+                    'log' => '图生文处理成功',
+                    'update_time' => date('Y-m-d H:i:s')
                 ]);
             }
 
+            echo date('Y-m-d H:i:s')."图生文结束\n";
             $job->delete();
         } catch (\Exception $e) {
             //异常处理,记录失败日志
@@ -61,20 +57,13 @@ class ImageJob
             echo "行号: " . $e->getLine() . "\n";
 
             if ($logId) {
-                Db::name('queue_log')->where('id', $logId)->update([
-                    'status'     => 3, // 3 表示失败
-                    'log'        => '图生文失败:' . $e->getMessage(),
-                    'updated_at' => date('Y-m-d H:i:s')
+                Db::name('image_task_log')->where('id', $logId)->update([
+                    'status' => 99,
+                    'log' => '图生文失败:' . $e->getMessage(),
+                    'update_time' => date('Y-m-d H:i:s')
                 ]);
             }
             $job->delete();
-
-            // 最多重试一次(总执行两次)
-            // if ($job->attempts() < 2) {
-            //     $job->release(30); // 延迟30秒再次执行
-            // } else {
-            //     $job->failed(); // 达到最大尝试次数,标记失败
-            // }
         }
     }
 
@@ -143,44 +132,27 @@ class ImageJob
         $logDir = $rootPath . 'runtime/logs/';
         if (!is_dir($logDir)) mkdir($logDir, 0755, true);
 
-        // file_put_contents(
-        //     $logDir . 'text.txt',
-        //     "\n====原始 " . date('Y-m-d H:i:s') . " ====\n" . $prompt . "\n\n",
-        //     FILE_APPEND
-        // );
         // 调用图生文
         $gptRes = $this->callGptApi($imageUrl, $prompt);
         $gptText = trim($gptRes['choices'][0]['message']['content'] ?? '');
 
 
-        // 保存 GPT 返回内容
-        // file_put_contents($logDir . 'text.txt',
-        //     "\n==== " . date('Y-m-d H:i:s') . " ====\n" . $gptText . "\n\n",
-        //     FILE_APPEND
-        // );
-
-// 提取英文描述
+        // 提取英文描述
         $patternEnglish = '/^([\s\S]+?)---json json---/';
         preg_match($patternEnglish, $gptText, $matchEn);
         $englishDesc = isset($matchEn[1]) ? trim($matchEn[1]) : '';
 
-// 提取中文描述
+        // 提取中文描述
         $patternChinese = '/---json json---\s*([\x{4e00}-\x{9fa5}][\s\S]+?)---json json---/u';
         preg_match($patternChinese, $gptText, $matchZh);
         $chineseDesc = isset($matchZh[1]) ? trim($matchZh[1]) : '';
 
-// 提取图片名(可能是中文短句,也可能是关键词)
+        // 提取图片名(可能是中文短句,也可能是关键词)
         $patternName = '/---json json---\s*(.+)$/s';
         preg_match($patternName, $gptText, $matchName);
         $rawName = isset($matchName[1]) ? trim($matchName[1]) : '';
         $img_name = preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $rawName);
 
-        // file_put_contents(
-        //     $logDir . 'text.txt',
-        //     "\n==== " . date('Y-m-d H:i:s') . " ====\n" . $gptText . "\n\n",
-        //     FILE_APPEND
-        // );
-
         // 验证 GPT 返回格式
         if (strpos($gptText, '---json json---') === false) {
             return  'GPT 返回格式不正确,缺少分隔符';
@@ -198,12 +170,10 @@ class ImageJob
         $chineseDesc = isset($parts[1]) ? $cleanPrefix(trim($parts[1])) : '';
         $part2 = isset($parts[2]) ? $cleanPrefix(trim($parts[2])) : '';
 
-        // 提取图片名
         // 只保留中英文、数字、下划线、短横线、空格
         $img_name = preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $part2);
 
-
-        // 成功后的日志
+        // 成功后的图生文提示词日志
         // file_put_contents(
         //     $logDir . 'img_name_success.txt',
         //     "\n======== " . date('Y-m-d H:i:s') . " ========\n" .
@@ -222,30 +192,39 @@ class ImageJob
             'size' => "",
             'status' => 0
         ]);
-        //分解任务
-        $arr = [
-            "fileName" =>$fileName,
-            "outputDir"=>$call_data["outputDir"],
-            "width"=>$call_data["width"],
-            "height"=>$call_data["height"],
-            "englishDesc"=>$englishDesc,
-            "img_name"=>$img_name
-        ];
+        return ;
+    }
 
-        if($call_data['type'] == '图生文'){
-            echo "执行图生文结束";
-            return ;
-        }else{
-            // 执行文生图
-            echo "现在推送";
-            Queue::push('app\job\TextToImageJob', $arr,'txttoimg');
-            return ;
-        }
 
+    /**
+     * 文生文接口
+     */
+    public function textToTxt($prompt,$id)
+    {
+
+        // 查询数据库记录
+        $record = Db::name('text_to_image')
+            ->field('id,english_description')
+            ->where('id',$id)
+            ->order('id desc')
+            ->find();
 
+        if (!$record) {
+            return '没有找到匹配的图像记录';
+        }
+
+        // 调用文生文
+        $gptRes = $this->TxtGptApi($prompt.$record['english_description']);
+        $gptText = trim($gptRes['choices'][0]['message']['content'] ?? '');
 
 
+        // 更新数据库记录
+        Db::name('text_to_image')->where('id', $record['id'])->update([
+            'english_description' => $gptText
+        ]);
+        return 0;
     }
+
     public function logToDatabase($data)
     {
         $record = [
@@ -253,12 +232,10 @@ class ImageJob
             'new_image_url' => $data['new_image_url'] ?? '',
             'custom_image_url' => $data['custom_image_url'] ?? '',
             'img_name' => $data['img_name'],
+            'model' => '',
             'size' => isset($data['image_width'], $data['image_height']) ? $data['image_width'] . 'x' . $data['image_height'] : '',
             'chinese_description' => $data['chinese_description'] ?? '',
             'english_description' => $data['english_description'] ?? '',
-            'model' => 'gpt-image-1',
-            'quality' => 'standard',
-            'style' => 'vivid',
             'status' => $data['status'] ?? 0,
             'error_msg' => $data['error_msg'] ?? '',
             'created_time' => date('Y-m-d H:i:s'),
@@ -272,6 +249,9 @@ class ImageJob
         }
     }
 
+    /**
+     * 图升文模型
+     */
     public function callGptApi($imageUrl, $prompt)
     {
         $data = [
@@ -292,6 +272,21 @@ class ImageJob
         return $this->callApi($this->config['gpt']['api_url'], $this->config['gpt']['api_key'], $data);
     }
 
+    /**
+     * 文升文模型
+     */
+    public function TxtGptApi($prompt)
+    {
+        $data = [
+            'prompt' => $prompt,
+            'model' => 'gpt-4',
+            'session_id' => null,
+            'context_reset' => true
+        ];
+
+        return $this->callApi($this->config['gpt']['api_url'],$this->config['gpt']['api_key'],$data);
+    }
+
     /**
      * 通用API调用方法
      */

+ 158 - 94
application/job/TextToImageJob.php

@@ -19,68 +19,108 @@ class TextToImageJob
         ]
     ];
 
+
+    /**
+     * 文生图队列任务
+     */
     public function fire(Job $job, $data)
     {
-        echo date('Y-m-d H:i:s') . " 任务开始执行\n";
+        $logId = $data['log_id'] ?? null;
 
         try {
-            if (isset($data['type']) && $data['type'] === '文生图') {
-                echo date('Y-m-d H:i:s') . " [文生图 - 数据库模式] 阶段开始\n";
-
-                $list = Db::name("text_to_image")
-                    ->where('old_image_url', $data['sourceDir'] . '/' . $data['file_name'])
-                    ->where('img_name', '<>', '')
-                    ->where('status', 0)
-                    ->select();
-
-                if (!empty($list)) {
-                    foreach ($list as $index => $row) {
-                        echo "处理第 " . ($index + 1) . " 条数据,处理ID:" . $row['id'] . "\n";
-
-                        // 调用 textToImage 方法
-                        $result = $this->textToImage(
-                            $data["file_name"],          // 文件名
-                            $data["outputDir"],          // 输出目录
-                            $data["width"],              // 宽度
-                            $data["height"],             // 高度
-                            $row["english_description"], // 英文描述
-                            $row["img_name"]             // 图片名
-                        );
-
-                        echo "处理结果:" . $result . "\n";
-
-                    }
-
-                    echo date('Y-m-d H:i:s') . " 文生图任务全部执行完成\n";
-                } else {
-                    echo "未找到 status=0 的数据,跳过执行\n";
+            if (!isset($data['type']) || $data['type'] !== '文生图') {
+                $job->delete();
+                return;
+            }
+
+            $startTime = date('Y-m-d H:i:s');
+            echo "━━━━━━━━━━ ▶ 文生图任务开始处理━━━━━━━━━━\n";
+            echo "处理时间:{$startTime}\n";
+
+            // 更新日志状态为处理中
+            if ($logId) {
+                Db::name('image_task_log')->where('id', $logId)->update([
+                    'status' => 1,
+                    'log' => '文生图处理中',
+                    'update_time' => $startTime
+                ]);
+            }
+
+            $fullPath = rtrim($data['sourceDir'], '/') . '/' . ltrim($data['file_name'], '/');
+
+            $list = Db::name("text_to_image")
+                ->where('old_image_url', $fullPath)
+                ->where('img_name', '<>', '')
+                ->where('status', 0)
+                ->select();
+
+            if (!empty($list)) {
+                $total = count($list);
+                echo "📊 共需处理:{$total} 条记录\n\n";
+
+                foreach ($list as $index => $row) {
+                    $currentIndex = $index + 1;
+                    $begin = date('Y-m-d H:i:s');
+                    echo "处理时间:{$begin}\n";
+                    echo "👉 正在处理第 {$currentIndex} 条,ID: {$row['id']}\n";
+
+                    // 调用生成图像方法
+                    $result = $this->textToImage(
+                        $data["file_name"],
+                        $data["outputDir"],
+                        $data["width"],
+                        $data["height"],
+                        $row["english_description"],
+                        $row["img_name"],
+                        $data["selectedOption"] ?? null
+                    );
+
+                    $resultText = ($result === true || $result === 1 || $result === '成功') ? '成功' : '失败或无返回';
+                    echo "✅ 处理结果:{$resultText}\n";
+
+                    $end = date('Y-m-d H:i:s');
+                    echo "完成时间:{$end}\n";
+                    echo "Processed: " . static::class . "\n";
+                    echo "文生图已处理完成\n\n";
                 }
 
-                $job->delete(); // 删除队列任务
+                // 更新日志为成功
+                if ($logId) {
+                    Db::name('image_task_log')->where('id', $logId)->update([
+                        'status' => 2,
+                        'log' => '文生图执行成功',
+                        'update_time' => date('Y-m-d H:i:s')
+                    ]);
+                }
 
+                echo date('Y-m-d H:i:s') . " ✅ 文生图任务全部完成\n";
             } else {
-                echo date('Y-m-d H:i:s') . " [文生图 - 单条传参模式] 开始执行\n";
-
-                $result = $this->textToImage(
-                    $data["fileName"],
-                    $data["outputDir"],
-                    $data["width"],
-                    $data["height"],
-                    $data["englishDesc"],
-                    $data["img_name"]
-                );
+                echo "⚠ 未找到可处理的数据,跳过执行\n";
+
+                if ($logId) {
+                    Db::name('image_task_log')->where('id', $logId)->update([
+                        'status' => 2,
+                        'log' => '无数据可处理,已跳过',
+                        'update_time' => date('Y-m-d H:i:s')
+                    ]);
+                }
+            }
 
-                echo "返回结果:" . $result . "\n";
-                echo date('Y-m-d H:i:s') . " 单条文生图执行结束\n";
+            $job->delete();
 
-                $job->delete();
+        } catch (\Exception $e) {
+            echo "❌ 异常信息: " . $e->getMessage() . "\n";
+            echo "📄 文件: " . $e->getFile() . "\n";
+            echo "📍 行号: " . $e->getLine() . "\n";
+
+            if ($logId) {
+                Db::name('image_task_log')->where('id', $logId)->update([
+                    'status' => -1,
+                    'log' => '文生图失败:' . $e->getMessage(),
+                    'update_time' => date('Y-m-d H:i:s')
+                ]);
             }
 
-        } catch (\Exception $e) {
-            echo "异常信息: " . $e->getMessage() . "\n";
-            echo "文件: " . $e->getFile() . "\n";
-            echo "行号: " . $e->getLine() . "\n";
-            echo date('Y-m-d H:i:s') . " 任务执行失败\n";
             $job->delete();
         }
     }
@@ -97,7 +137,7 @@ class TextToImageJob
     /**
      * 文生图接口
      */
-    public function textToImage($fileName, $outputDirRaw, $width, $height, $prompt, $img_name)
+    public function textToImage($fileName, $outputDirRaw, $width, $height, $prompt, $img_name,$selectedOption)
     {
         $rootPath = str_replace('\\', '/', ROOT_PATH);
         $outputDir = rtrim($rootPath . 'public/' . $outputDirRaw, '/') . '/';
@@ -111,8 +151,7 @@ class TextToImageJob
             }
         }
 
-        // 清理 prompt 的换行
-        $prompt = preg_replace('/[\r\n\t]+/', ' ', $prompt);
+
 
         // 查询数据库记录
         $record = Db::name('text_to_image')
@@ -127,15 +166,33 @@ class TextToImageJob
         // 写入 prompt 日志
         $logDir = $rootPath . 'runtime/logs/';
         if (!is_dir($logDir)) mkdir($logDir, 0755, true);
-//        echo 2345;
+
         // 调用文生图模型接口生成图像
         $startTime = microtime(true);
-//         echo "<pre>";
-// print_r($prompt);
-// echo "<pre>";die;
-        $dalle1024 = $this->callDalleApi($prompt);
-        // $dalle1024 = json_decode('{"created":1747932746,"data":[{"revised_prompt":"**First paragraph:**   A geometric abstract design with a central motif consisting of curved and angular shapes in a symmetrical arrangement. The color scheme predominantly features shades of blue, with hints of white and black creating a contrast. The design incorporates smooth, flowing lines mixed with sharp angles. The overall style has a modern, minimalist aesthetic, with a focus on balance and clean shapes.","url":"https:\/\/filesystem.site\/cdn\/20250523\/3NVcCUaZDkLimWjtgOwJYniGezDX8d.png"}],"usage":{"total_tokens":4250,"input_tokens":75,"output_tokens":4175,"input_tokens_details":{"text_tokens":75}}}',true);
 
+
+        // 清理 prompt 的换行
+        $prompt = preg_replace('/[\r\n\t]+/', ' ', $prompt);
+
+        // 定义要跳过的关键词(可按需扩展)
+        $skipKeywords = ['几何', 'geometry', 'geometric'];
+        foreach ($skipKeywords as $keyword) {
+            // 判断提示词中是否包含关键词(不区分大小写)
+            if (stripos($prompt, $keyword) !== false) {
+                $skipId = $record['id'];
+                echo "🚫 跳过生成:提示词中包含关键词“{$keyword}”,记录 ID:{$skipId}\n";
+                $updateRes = Db::name('text_to_image')->where('id', $skipId)->update([
+                    'status' => 3,
+                    'update_time' => date('Y-m-d H:i:s')
+                ]);
+                return "跳过生成:记录 ID {$skipId},包含关键词 - {$keyword}";
+            }
+        }
+
+        //文生图调用
+        $dalle1024 = $this->callDalleApi($prompt,$selectedOption);
+
+        //查询接口调用时长
         $endTime = microtime(true);
         $executionTime = $endTime - $startTime;
         echo "API调用耗时: " . round($executionTime, 3) . " 秒\n";
@@ -143,31 +200,28 @@ class TextToImageJob
         // 检查 URL 返回是否成功
         if (!isset($dalle1024['data'][0]['url']) || empty($dalle1024['data'][0]['url'])) {
             $errorText = $dalle1024['error']['message'] ?? '未知错误';
-            echo '生成失败:' . $errorText;
+            Db::name('text_to_image')->where('id', $record['id'])->update([
+                'error_msg' => '生成失败:' . $errorText,
+                'status' => 0
+            ]);
+            return '生成失败:' . $errorText;
         }
-//        echo 342342;
+
         // 下载图像
         $imgUrl1024 = $dalle1024['data'][0]['url'];
         $imgData1024 = @file_get_contents($imgUrl1024);
         if (!$imgData1024 || strlen($imgData1024) < 1000) {
             return "下载图像失败或内容异常";
         }
-//        echo 3423424444;
-        // file_put_contents(
-        //     $logDir . 'img_name.txt',
-        //     "\n====图片 " . date('Y-m-d H:i:s') . " ====\n" . $img_name . "\n\n",
-        //     FILE_APPEND
-        // );
 
         // 保存原图(1024x1024)
-//        $img_name = $this->limitStringLength($img_name);
         $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);
-//        echo 342344543532;
+
         // 处理缩略图
         $im = @imagecreatefromstring($imgData1024);
         if (!$im) return "图像格式不受支持或已损坏";
@@ -189,7 +243,7 @@ class TextToImageJob
             $srcX = 0;
             $srcY = intval(($srcHeight - $cropHeight) / 2);
         }
-//        echo 789;
+
         $dstImg = imagecreatetruecolor($width, $height);
         imagecopyresampled($dstImg, $im, 0, 0, $srcX, $srcY, $width, $height, $cropWidth, $cropHeight);
 
@@ -200,13 +254,6 @@ class TextToImageJob
         imagedestroy($im);
         imagedestroy($dstImg);
 
-
-        // file_put_contents(
-        //     $logDir . 'image_url.txt',
-        //     "\n====图片路径 " . date('Y-m-d H:i:s') . " ====\n" . str_replace($rootPath . 'public/', '', $savePath1024) . "\n\n",
-        //     "\n====图片路径 " . date('Y-m-d H:i:s') . " ====\n" . str_replace($rootPath . 'public/', '', $savePathCustom) . "\n\n",
-        //     FILE_APPEND
-        // );
         $status = trim($img_name) === '' ? 0 : 1;
         // 更新数据库记录
         $updateRes = Db::name('text_to_image')->where('id', $record['id'])->update([
@@ -214,6 +261,9 @@ class TextToImageJob
             'custom_image_url' => str_replace($rootPath . 'public/', '', $savePathCustom),
             'img_name' => $img_name,
             'error_msg' => '',
+            'model' => $selectedOption,
+            'quality' => 'hd',
+            'style' => 'vivid',
             'size' => "{$width}x{$height}",
             'updated_time' => date('Y-m-d H:i:s'),
             'status' => $status
@@ -238,6 +288,7 @@ class TextToImageJob
         // 超出限制则截断
         return mb_substr($str, 0, $maxLength, 'UTF-8');
     }
+
     public function cleanImageUrl($input) {
         // 去除字符串首尾空格和中文引号替换为英文引号
         $input = trim($input);
@@ -260,26 +311,39 @@ class TextToImageJob
 
 
     /**
-     * 文生图模
+     * 文生图模
      */
-    public function callDalleApi($prompt)
+    public function callDalleApi($prompt,$selectedOption)
     {
-        $data = [
-            'prompt' => $prompt,
-//            'model'   => 'dall-e-2',
-            'model'   => 'gpt-image-1',
-            'n'       => 1,
-            'size'    => '1024x1024',
-            'quality' => 'standard',
-            'style'   => 'vivid',
-            'response_format' => 'url'
-        ];
+        if($selectedOption == 'dall-e-3'){
+            $data = [
+                'prompt' => $prompt,
+                'model'   => $selectedOption,
+                'n'       => 1,
+                'size'    => '1024x1024',
+                'quality' => 'standard',
+                'style'   => 'vivid',
+                'response_format' => 'url',
+                'session_id' => null,
+                'context_reset' => true
+            ];
+        }else{
+            $data = [
+                'prompt' => $prompt,
+                'model'   => $selectedOption,
+                'n'       => 1,
+                'size'    => '1024x1024',
+                'quality' => 'hd',
+                'style'   => 'vivid',
+                'response_format' => 'url',
+                'session_id' => null,
+                'context_reset' => true
+            ];
+        }
+
         return $this->callApi($this->config['dalle']['api_url'], $this->config['dalle']['api_key'], $data);
     }
 
-
-
-
     /**
      * 通用API调用方法
      */
@@ -316,12 +380,12 @@ class TextToImageJob
                 $result = json_decode($response, true);
                 return $result;
             }
-
             $lastError = $curlError ?: "HTTP错误:{$httpCode}";
             $attempt++;
             sleep(1);
         }
-
         throw new \Exception("请求失败(重试{$maxRetries}次):{$lastError}");
     }
+
+
 }

+ 181 - 0
application/job/TextToTextJob.php

@@ -0,0 +1,181 @@
+<?php
+namespace app\job;
+use think\Db;
+use think\queue\Job;
+use think\Queue;
+class TextToTextJob
+{
+    protected $config = [
+        'gpt' => [
+            'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
+            'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
+        ],
+        'dalle' => [
+            'api_key' => 'sk-e0JuPjMntkbgi1BoMjrqyyzMKzAxILkQzyGMSy3xiMupuoWY',
+            'api_url' => 'https://niubi.zeabur.app/v1/images/generations'
+        ]
+    ];
+
+    /**
+     * 文生文
+     */
+    public function fire(Job $job, $data)
+    {
+        $logId = $data['log_id'] ?? null;
+        echo "━━━━━━━━━━ ▶ 文生文任务开始处理━━━━━━━━━━\n";
+
+        try {
+            $startTime = date('Y-m-d H:i:s');
+
+            if ($logId) {
+                Db::name('image_task_log')->where('id', $logId)->update([
+                    'status' => 1,
+                    'log' => '文生文处理中',
+                    'update_time' => date('Y-m-d H:i:s')
+                ]);
+            }
+
+            $fullPath = rtrim($data['sourceDir'], '/') . '/' . ltrim($data['file_name'], '/');
+
+            $list = Db::name("text_to_image")
+                ->where('old_image_url', $fullPath)
+                ->where('img_name', '<>', '')
+                ->where('status', 0)
+                ->select();
+
+            if (!empty($list)) {
+                foreach ($list as $index => $row) {
+                    $currentTime = date('Y-m-d H:i:s');
+                    echo "处理时间:{$currentTime}\n";
+                    echo "👉 正在处理第 " . ($index + 1) . " 条,ID: {$row['id']}\n";
+
+                    $result = $this->textToTxt($data["prompt"], $row['id']);
+                    echo $result;
+                    echo "✅ 处理结果:完成\n";
+                    echo "完成时间:" . date('Y-m-d H:i:s') . "\n";
+
+                    echo "Processed: " . static::class . "\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')
+                    ]);
+                }
+
+                echo "处理完成\n";
+            } else {
+                echo "⚠ 未找到可处理的数据,跳过执行\n";
+
+                if ($logId) {
+                    Db::name('image_task_log')->where('id', $logId)->update([
+                        'status' => 2,
+                        'log' => '无数据可处理,已跳过',
+                        'update_time' => date('Y-m-d H:i:s')
+                    ]);
+                }
+            }
+
+        } catch (\Exception $e) {
+            echo "❌ 错误信息: " . $e->getMessage() . "\n";
+            echo "📄 文件: " . $e->getFile() . ",第 " . $e->getLine() . " 行\n";
+
+            if ($logId) {
+                Db::name('image_task_log')->where('id', $logId)->update([
+                    'status' => -1,
+                    'log' => '文生文失败:' . $e->getMessage(),
+                    'update_time' => date('Y-m-d H:i:s')
+                ]);
+            }
+        }
+
+        $job->delete();
+    }
+
+    /**
+     * 文生文接口
+     */
+    public function textToTxt($prompt,$id)
+    {
+        $record = Db::name('text_to_image')
+            ->field('id,english_description')
+            ->where('id',$id)
+            ->order('id desc')
+            ->find();
+        if (!$record) {return '没有找到匹配的图像记录';}
+
+        // 调用文生文
+        $gptRes = $this->TxtGptApi($prompt.$record['english_description']);
+        $gptText = trim($gptRes['choices'][0]['message']['content'] ?? '');
+
+        // 更新数据库记录
+        Db::name('text_to_image')->where('id', $record['id'])->update([
+            'english_description' => $gptText
+        ]);
+        return 0;
+    }
+
+    /**
+     * 文升文模型
+     */
+    public function TxtGptApi($prompt)
+    {
+        $data = [
+            'prompt' => $prompt,
+            'model' => 'gpt-4',
+            'session_id' => null,
+            'context_reset' => true
+        ];
+
+        return $this->callApi($this->config['gpt']['api_url'],$this->config['gpt']['api_key'],$data);
+    }
+
+    /**
+     * 通用API调用方法
+     */
+    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}");
+    }
+
+}

+ 86 - 36
application/service/ImageService.php

@@ -1,49 +1,103 @@
 <?php
-// 1. 正确的队列任务类 application/job/ImageJob.php
 namespace app\service;
-
 use think\Db;
 use think\Queue;
+/**
+ * ImageService 类用于处理图像任务。
+ * 该类将前端传过来的多个图像信息推送到处理队列中。
+ */
+class ImageService{
+    /**
+     * 处理图像并推送到队列中
+     */
 
-class ImageService
-{
     public function handleImage($params) {
-        // 如果是单条,转为数组
-        if(!isset($params["batch"])){
-            return false;
-        }
+        if (!isset($params["batch"])) {return false;}
+
         $arr = [];
-        $batch = $params["batch"];
-        $num = $params["num"];
-        //进行数据处理
-        $j = 0;
+        $batch = $params["batch"]; // 获取图像批量信息
+        $num = $params["num"]; // 获取需要生成的实例数量
+
+        // 遍历每个图像,进行处理
         foreach ($batch as $k => $v) {
             $baseItem = [
-                "sourceDir" => $this->sourceDir($v, 1) ?? '',
-                "outputDir" => $this->sourceDir($v, 2) ?? '',
-                "file_name" => $this->sourceDir($v, 3) ?? '',
-                "type" => $params['type'] ?? '',
-                "prompt" => $params['prompt'] ?? '',
-                "width" => $params['width'] ?? 512,
-                "height" => $params['height'] ?? 512
+                "sourceDir" => $this->sourceDir($v, 1), // 获取源目录
+                "outputDir" => $this->sourceDir($v, 2), // 获取输出目录
+                "file_name" => $this->sourceDir($v, 3), // 获取文件名
+                "type" => $params['type'] ?? '', // 获取处理类型
+                "selectedOption" => $params['selectedOption'], //生图模型参数
+                "prompt" => $params['prompt'], // 获取处理提示
+                "width" => $params['width'], // 获取图像宽度
+                "height" => $params['height'] // 获取图像高度
             ];
-
             // 创建$num个相同的项目并合并到$arr
             $arr = array_merge($arr, array_fill(0, $num, $baseItem));
         }
-//         推送队列(启用时请去掉 die)
-        //整体进行推送
-        Queue::push('app\job\ImageArrJob', $arr,"arrimage");
+
+
+        // 设置基础字段
+        $insertData = [
+            'create_time'  => date('Y-m-d H:i:s'),
+            'old_image_file'       => $params['old_image_file'],
+            'status'       => '等待中',
+            'image_count'  => count($arr),
+            'params'       => json_encode($params, JSON_UNESCAPED_UNICODE)
+        ];
+
+        // 根据任务类型设定模型
+        switch ($params['type']) {
+            case '图生文':
+                $insertData['model'] = 'gpt-4-vision-preview';
+                break;
+
+            case '文生文':
+                $insertData['model'] = 'gpt-4';
+                break;
+
+            case '文生图':
+                $insertData['model'] = $params['selectedOption'] ?? '未知模型';
+                break;
+
+            default:
+                // 混合任务或自定义模型逻辑
+                $selected = $params['selectedOption'] ?? '未知';
+                $insertData['model'] = "gpt-4-vision-preview,gpt-4,{$selected}";
+                break;
+        }
+
+        // 插入并获取任务 ID
+        $task_id = Db::name('queue_logs')->insertGetId($insertData);
+
+
+        // 推送到队列
+        $payload = [
+            'task_id' => $task_id,
+            'data' => $arr
+        ];
+
+        //测试查看推送队列前的数据
+//        echo "<pre>";print_r($payload);echo "<pre>";die;
+
+        // 推送队列
+        Queue::push('app\job\ImageArrJob', $payload, "arrimage");
     }
 
+    /**
+     * 解析图像路径并返回相关信息
+     *
+     * @param string $filePath 图像文件路径
+     * @param int $type 返回类型标识
+     * @return string|null 返回解析后的路径或文件名
+     */
     public function sourceDir($filePath,$type){
         $arr = [];
+
         // 使用正则表达式匹配完整路径
         if (preg_match('/^(.+?)\/Preview\/(\d{8})\/(.+)$/', $filePath, $matches)) {
             $arr =  [
-                'basePath' => $matches[1],  // uploads/operate/ai
-                'date' => $matches[2],      // 20250522
-                'filename' => $matches[3]   // 333 (89).jpg
+                'basePath' => $matches[1],  // 基础路径
+                'date' => $matches[2],      // 日期 (YYYYMMDD)
+                'filename' => $matches[3]   // 文件名
             ];
         }else{
             // 备用方案:如果正则匹配失败
@@ -68,19 +122,15 @@ class ImageService
             ];
         }
 
-
-
-        if($type==1){
-            // 使用正则表达式匹配路径中的日期部分(YYYYMMDD)
-            return $arr["basePath"]."/Preview/".$arr["date"];
+        // 根据类型返回不同的路径
+        if ($type == 1) {
+            return $arr["basePath"] . "/Preview/" . $arr["date"]; // 返回预览目录
         }
-        if($type==2){
-            // 使用正则表达式匹配路径中的日期部分(YYYYMMDD)
-            return '/'.$arr["basePath"]."/dall-e/".$arr["date"];
+        if ($type == 2) {
+            return '/' . $arr["basePath"] . "/dall-e/" . $arr["date"]; // 返回输出目录
         }
         if($type==3){
-            // 使用正则表达式匹配路径中的日期部分(YYYYMMDD)
-            return $arr["filename"];
+            return $arr["filename"]; // 返回图片名称
         }
     }
 }