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

+ 388 - 92
application/api/controller/Facility.php

@@ -29,6 +29,7 @@ class Facility extends Api
 
 
     /**
+     * 缓存及时
      * 获取指定目录所有图片
      */
     public function getPreviewimg()
@@ -46,124 +47,127 @@ class Facility extends Api
             return json(['code' => 1, 'msg' => '原图目录不存在']);
         }
 
-        // 设置缓存路径
-        $cacheDir = RUNTIME_PATH . 'image_cache/';
-        if (!is_dir($cacheDir)) {
-            mkdir($cacheDir, 0755, true);
-        }
-        $cacheFile = $cacheDir . md5($relativePath) . '.json';
-
-        // 判断缓存文件是否存在,并且最后修改时间在1小时(3600秒)以内
-        if (file_exists($cacheFile) && time() - filemtime($cacheFile) < 3600) {
-            $imageInfoMap = json_decode(file_get_contents($cacheFile), true);
-        } else {
-            // 没有缓存或缓存过期,重新扫描目录
-            $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
-            $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))
-                ];
-            }
-            file_put_contents($cacheFile, json_encode($imageInfoMap));
-        }
-
-        // 1. 获取所有图片路径
-        $relativeImages = array_keys($imageInfoMap);
+        // 构建缓存键与构建锁键
+        $hash = md5($relativePath);
+        $cacheKey = "previewimg_full_{$hash}";
+        $lockKey = "previewimg_building_{$hash}";
+        $cacheExpire = 600; // 10分钟
+
+        $fullData = cache($cacheKey);
+
+        if (!$fullData) {
+            // 防止缓存“惊群效应”:只允许一个任务生成缓存,其它等待
+            if (!cache($lockKey)) {
+                cache($lockKey, 1, 60); // 1分钟构建锁
+
+                $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+                $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))
+                    ];
+                }
 
-        // 2. 查询数据库记录(一次性查询所有相关记录)
-        $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, status_name')
-            ->select();
+                $relativeImages = array_keys($imageInfoMap);
 
-        // 3. 查询队列表中的记录(获取队列状态信息)
-        $queueRecords = Db::name('image_task_log')
-            // ->where('status', 0)
-            // ->where('log', '队列中')
-            ->field('file_name, log')
-            ->select();
+                // 批量查库
+                $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, status_name')
+                    ->select();
 
-        // 4. 创建队列信息映射
-        $queueMap = [];
-        foreach ($queueRecords as $queueItem) {
-            $key = str_replace('\\', '/', trim($queueItem['file_name'], '/'));
-            $queueMap[$key] = $queueItem['log'];
-        }
+                $queueRecords = Db::name('image_task_log')
+                    ->where('mod_rq', null)
+                    ->field('file_name, log')
+                    ->select();
 
-        // 5. 映射记录
-        $processedMap = [];
-        foreach ($dbRecords as $item) {
-            $key = str_replace('\\', '/', trim($item['old_image_url'], '/'));
-            $processedMap[$key] = $item;
-        }
+                $queueMap = [];
+                foreach ($queueRecords as $q) {
+                    $key = str_replace('\\', '/', trim($q['file_name'], '/'));
+                    $queueMap[$key] = $q['log'];
+                }
 
-        // 6. 构建完整数据并进行筛选
-        $filteredData = [];
-        foreach ($relativeImages as $path) {
-            $item = $processedMap[$path] ?? [];
-            $info = $imageInfoMap[$path];
+                $processedMap = [];
+                foreach ($dbRecords as $item) {
+                    $key = str_replace('\\', '/', trim($item['old_image_url'], '/'));
+                    $processedMap[$key] = $item;
+                }
 
-            $dbStatus = isset($item['status']) ? (int)$item['status'] : 0;
-            $dbStatusName = isset($item['status_name']) ? trim($item['status_name']) : '';
+                $fullData = [];
+                foreach ($relativeImages as $path) {
+                    $item = $processedMap[$path] ?? [];
+                    $info = $imageInfoMap[$path];
+
+                    $fullData[] = [
+                        'path' => $path,
+                        'item' => $item,
+                        'info' => $info,
+                        'dbStatus' => isset($item['status']) ? (int)$item['status'] : 0,
+                        'dbStatusName' => $item['status_name'] ?? '',
+                        'isProcessed' => !empty($item['img_name']) && !empty($item['custom_image_url']),
+                        'queueStatus' => $queueMap[$path] ?? ''
+                    ];
+                }
 
-            // 状态筛选条件
-            if ($status !== '' && (int)$status !== $dbStatus) {
-                continue;
-            }
-            if ($status_name !== '' && $dbStatusName !== $status_name) {
-                continue;
+                // 设置缓存 + 删除构建锁
+                cache($cacheKey, $fullData, $cacheExpire);
+                cache($lockKey, null);
+            } else {
+                // 如果有构建锁,等待缓存生成后再读
+                $waitTime = 0;
+                while (!$fullData && $waitTime < 10) {
+                    sleep(1);
+                    $waitTime++;
+                    $fullData = cache($cacheKey);
+                }
+                if (!$fullData) {
+                    return json(['code' => 2, 'msg' => '系统正忙,请稍后重试']);
+                }
             }
+        }
 
-            $isProcessed = !empty($item['img_name']) && !empty($item['custom_image_url']);
-            $queueStatus = $queueMap[$path] ?? '';
+        // 筛选状态字段
+        $filtered = array_filter($fullData, function ($data) use ($status, $status_name) {
+            if ($status !== '' && (int)$status !== $data['dbStatus']) return false;
+            if ($status_name !== '' && $status_name !== $data['dbStatusName']) return false;
+            return true;
+        });
 
-            $filteredData[] = [
-                'path' => $path,
-                'item' => $item,
-                'info' => $info,
-                'dbStatus' => $dbStatus,
-                'dbStatusName' => $dbStatusName,
-                'isProcessed' => $isProcessed,
-                'queueStatus' => $queueStatus
-            ];
-        }
+        // 分页处理
+        $total = count($filtered);
+        $paged = array_slice(array_values($filtered), ($page - 1) * $limit, $limit);
 
-        // 7. 获取相同图片数量统计(基于筛选后的结果,只统计有new_image_url的记录)
-        $filteredPaths = array_column($filteredData, 'path');
+        // 统计 same_count
+        $paths = array_column($paged, 'path');
         $sameCountMap = [];
-        if (!empty($filteredPaths)) {
+        if ($paths) {
             $sameCountMap = Db::name('text_to_image')
-                ->whereIn('old_image_url', $filteredPaths)
-                ->where('new_image_url', '<>', '')  // 只统计有new_image_url的记录
+                ->whereIn('old_image_url', $paths)
+                ->where('new_image_url', '<>', '')
                 ->group('old_image_url')
                 ->column('count(*) as cnt', 'old_image_url');
         }
 
-        // 8. 分页处理
-        $total = count($filteredData);
-        $pagedData = array_slice($filteredData, ($page - 1) * $limit, $limit);
-
-        // 9. 构建最终响应数据
-        $resultData = [];
-        foreach ($pagedData as $i => $data) {
+        // 构建返回数据
+        $result = [];
+        foreach ($paged as $i => $data) {
             $path = $data['path'];
             $item = $data['item'];
             $info = $data['info'];
 
-            $resultData[] = [
+            $result[] = [
                 'id' => ($page - 1) * $limit + $i + 1,
                 'path' => $path,
                 'status' => $data['dbStatus'],
                 'status_name' => $data['dbStatusName'],
                 'same_count' => $sameCountMap[$path] ?? 0,
                 'is_processed' => $data['isProcessed'] ? 1 : 0,
-                'queue_status' => $data['queueStatus'],  // 新增队列状态字段
+                'queue_status' => $data['queueStatus'],
                 'new_image_url' => $item['new_image_url'] ?? '',
                 'custom_image_url' => $item['custom_image_url'] ?? '',
                 'chinese_description' => $item['chinese_description'] ?? '',
@@ -179,12 +183,304 @@ class Facility extends Api
         return json([
             'code' => 0,
             'msg' => '获取成功',
-            'data' => $resultData,
+            'data' => $result,
             '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', '');
+//        $status_name = $this->request->param('status_name', '');
+//        $relativePath = $this->request->param('path', '');
+//
+//        $basePath = ROOT_PATH . 'public/';
+//        $fullPath = $basePath . $relativePath;
+//
+//        if (!is_dir($fullPath)) {
+//            return json(['code' => 1, 'msg' => '原图目录不存在']);
+//        }
+//
+//        $baseCacheKey = 'previewimg_full_' . md5($relativePath);
+//        $fullData = cache($baseCacheKey);
+//
+//        if (!$fullData) {
+//            $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+//            $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);
+//
+//            $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, status_name')
+//                ->select();
+//
+//            $queueRecords = Db::name('image_task_log')
+//                ->field('file_name, log')
+//                ->where('mod_rq', null)
+//                ->select();
+//
+//            $queueMap = [];
+//            foreach ($queueRecords as $q) {
+//                $k = str_replace('\\', '/', trim($q['file_name'], '/'));
+//                $queueMap[$k] = $q['log'];
+//            }
+//
+//            $processedMap = [];
+//            foreach ($dbRecords as $item) {
+//                $key = str_replace('\\', '/', trim($item['old_image_url'], '/'));
+//                $processedMap[$key] = $item;
+//            }
+//
+//            $fullData = [];
+//            foreach ($relativeImages as $path) {
+//                $item = $processedMap[$path] ?? [];
+//                $info = $imageInfoMap[$path];
+//
+//                $fullData[] = [
+//                    'path' => $path,
+//                    'item' => $item,
+//                    'info' => $info,
+//                    'dbStatus' => isset($item['status']) ? (int)$item['status'] : 0,
+//                    'dbStatusName' => $item['status_name'] ?? '',
+//                    'isProcessed' => !empty($item['img_name']) && !empty($item['custom_image_url']),
+//                    'queueStatus' => $queueMap[$path] ?? ''
+//                ];
+//            }
+//
+//            // 缓存整个数据,不包含分页
+//            cache($baseCacheKey, $fullData, 600);
+//        }
+//
+//        // 筛选状态(如 status, status_name)
+//        $filteredData = array_filter($fullData, function ($data) use ($status, $status_name) {
+//            if ($status !== '' && (int)$status !== $data['dbStatus']) return false;
+//            if ($status_name !== '' && $status_name !== $data['dbStatusName']) return false;
+//            return true;
+//        });
+//
+//        // 分页处理
+//        $total = count($filteredData);
+//        $pagedData = array_slice(array_values($filteredData), ($page - 1) * $limit, $limit);
+//
+//        // 统计 same_count
+//        $paths = array_column($pagedData, 'path');
+//        $sameCountMap = [];
+//        if (!empty($paths)) {
+//            $sameCountMap = Db::name('text_to_image')
+//                ->whereIn('old_image_url', $paths)
+//                ->where('new_image_url', '<>', '')
+//                ->group('old_image_url')
+//                ->column('count(*) as cnt', 'old_image_url');
+//        }
+//
+//        // 构建返回结构
+//        $resultData = [];
+//        foreach ($pagedData as $i => $data) {
+//            $path = $data['path'];
+//            $item = $data['item'];
+//            $info = $data['info'];
+//
+//            $resultData[] = [
+//                'id' => ($page - 1) * $limit + $i + 1,
+//                'path' => $path,
+//                'status' => $data['dbStatus'],
+//                'status_name' => $data['dbStatusName'],
+//                'same_count' => $sameCountMap[$path] ?? 0,
+//                'is_processed' => $data['isProcessed'] ? 1 : 0,
+//                'queue_status' => $data['queueStatus'],
+//                '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']
+//            ];
+//        }
+//
+//        return json([
+//            'code' => 0,
+//            'msg' => '获取成功',
+//            'data' => $resultData,
+//            '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', '');
+//        $status_name = $this->request->param('status_name', '');
+//        $relativePath = $this->request->param('path', '');
+//
+//        $basePath = ROOT_PATH . 'public/';
+//        $fullPath = $basePath . $relativePath;
+//
+//        if (!is_dir($fullPath)) {
+//            return json(['code' => 1, 'msg' => '原图目录不存在']);
+//        }
+//
+//        // 设置缓存路径
+//        $cacheDir = RUNTIME_PATH . 'image_cache/';
+//        if (!is_dir($cacheDir)) {
+//            mkdir($cacheDir, 0755, true);
+//        }
+//        $cacheFile = $cacheDir . md5($relativePath) . '.json';
+//
+//        // 判断缓存文件是否存在,并且最后修改时间在1小时(3600秒)以内
+//        if (file_exists($cacheFile) && time() - filemtime($cacheFile) < 3600) {
+//            $imageInfoMap = json_decode(file_get_contents($cacheFile), true);
+//        } else {
+//            // 没有缓存或缓存过期,重新扫描目录
+//            $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+//            $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))
+//                ];
+//            }
+//            file_put_contents($cacheFile, json_encode($imageInfoMap));
+//        }
+//
+//        // 1. 获取所有图片路径
+//        $relativeImages = array_keys($imageInfoMap);
+//
+//        // 2. 查询数据库记录(一次性查询所有相关记录)
+//        $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, status_name')
+//            ->select();
+//
+//        // 3. 查询队列表中的记录(获取队列状态信息)
+//        $queueRecords = Db::name('image_task_log')
+//            // ->where('status', 0)
+//            // ->where('log', '队列中')
+//            ->field('file_name, log')
+//            ->select();
+//
+//        // 4. 创建队列信息映射
+//        $queueMap = [];
+//        foreach ($queueRecords as $queueItem) {
+//            $key = str_replace('\\', '/', trim($queueItem['file_name'], '/'));
+//            $queueMap[$key] = $queueItem['log'];
+//        }
+//
+//        // 5. 映射记录
+//        $processedMap = [];
+//        foreach ($dbRecords as $item) {
+//            $key = str_replace('\\', '/', trim($item['old_image_url'], '/'));
+//            $processedMap[$key] = $item;
+//        }
+//
+//        // 6. 构建完整数据并进行筛选
+//        $filteredData = [];
+//        foreach ($relativeImages as $path) {
+//            $item = $processedMap[$path] ?? [];
+//            $info = $imageInfoMap[$path];
+//
+//            $dbStatus = isset($item['status']) ? (int)$item['status'] : 0;
+//            $dbStatusName = isset($item['status_name']) ? trim($item['status_name']) : '';
+//
+//            // 状态筛选条件
+//            if ($status !== '' && (int)$status !== $dbStatus) {
+//                continue;
+//            }
+//            if ($status_name !== '' && $dbStatusName !== $status_name) {
+//                continue;
+//            }
+//
+//            $isProcessed = !empty($item['img_name']) && !empty($item['custom_image_url']);
+//            $queueStatus = $queueMap[$path] ?? '';
+//
+//            $filteredData[] = [
+//                'path' => $path,
+//                'item' => $item,
+//                'info' => $info,
+//                'dbStatus' => $dbStatus,
+//                'dbStatusName' => $dbStatusName,
+//                'isProcessed' => $isProcessed,
+//                'queueStatus' => $queueStatus
+//            ];
+//        }
+//
+//        // 7. 获取相同图片数量统计(基于筛选后的结果,只统计有new_image_url的记录)
+//        $filteredPaths = array_column($filteredData, 'path');
+//        $sameCountMap = [];
+//        if (!empty($filteredPaths)) {
+//            $sameCountMap = Db::name('text_to_image')
+//                ->whereIn('old_image_url', $filteredPaths)
+//                ->where('new_image_url', '<>', '')  // 只统计有new_image_url的记录
+//                ->group('old_image_url')
+//                ->column('count(*) as cnt', 'old_image_url');
+//        }
+//
+//        // 8. 分页处理
+//        $total = count($filteredData);
+//        $pagedData = array_slice($filteredData, ($page - 1) * $limit, $limit);
+//
+//        // 9. 构建最终响应数据
+//        $resultData = [];
+//        foreach ($pagedData as $i => $data) {
+//            $path = $data['path'];
+//            $item = $data['item'];
+//            $info = $data['info'];
+//
+//            $resultData[] = [
+//                'id' => ($page - 1) * $limit + $i + 1,
+//                'path' => $path,
+//                'status' => $data['dbStatus'],
+//                'status_name' => $data['dbStatusName'],
+//                'same_count' => $sameCountMap[$path] ?? 0,
+//                'is_processed' => $data['isProcessed'] ? 1 : 0,
+//                'queue_status' => $data['queueStatus'],  // 新增队列状态字段
+//                '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']
+//            ];
+//        }
+//
+//        return json([
+//            'code' => 0,
+//            'msg' => '获取成功',
+//            'data' => $resultData,
+//            'total' => $total,
+//            'page' => $page,
+//            'limit' => $limit
+//        ]);
+//    }
 
     /**
      * 采用缓存机制

+ 25 - 11
application/job/ImageArrJob.php

@@ -5,33 +5,41 @@ use think\Db;
 use think\queue\Job;
 use think\Queue;
 
+/**
+ * ImageArrJob 图像任务初始化队列
+ * 用于将批量图像任务按类型分发至下游子队列(图生文/文生文/文生图)
+ */
 class ImageArrJob
 {
+    /**
+     * 队列任务执行函数
+     *
+     * @param Job $job 当前队列任务对象
+     * @param array $data 传入的数据,包含 task_id 和图像列表 data
+     * task_id:队列id
+     * data:队列数据
+     */
     public function fire(Job $job, $data)
     {
 
-        //队列id
         $task_id = $data['task_id'];
-        //队列数据
         $images = $data['data'];
 
         foreach ($images as $value) {
             $value['task_id'] = $task_id;
-            // 1. 清理 sourceDir,去掉末尾重复的 Preview
+            // 清理路径:去除末尾 Preview 目录
             $sourceDir = rtrim($value["sourceDir"], '/\\');
             $dirParts = explode('/', str_replace('\\', '/', $sourceDir));
             if (end($dirParts) === 'Preview') {
-                array_pop($dirParts); // 删除末尾 Preview
+                array_pop($dirParts);
             }
             $cleanedSourceDir = implode('/', $dirParts);
 
-            // 2. 获取文件名
+            // 获取文件名并拼接最终路径
             $fileName = basename($value['file_name']);
-
-            // 3. 拼接最终路径
             $fullPath = $cleanedSourceDir . '/' . $fileName;
 
-            // 4. 写入日志记录
+            // 写入 image_task_log 队列明细日志记录
             $log_id = Db::name('image_task_log')->insertGetId([
                 'task_id' => $task_id,
                 'file_name' => $fullPath,
@@ -42,8 +50,9 @@ class ImageArrJob
             ]);
 
             $value['log_id'] = $log_id;
-            // 6. 若有链式任务,传递下去
+            //若有链式任务,传递下去
             $chain_next = $value['chain_next'] ?? [];
+
             switch (trim($value['type'])) {
                 case '图生文':
                     Queue::push('app\job\ImageJob', array_merge($value, ['chain_next' => $chain_next]), 'imgtotxt');
@@ -61,14 +70,19 @@ class ImageArrJob
 
         }
 
-        // 6. 更新任务状态为已启动
+        //更新任务状态为已启动
         Db::name('queue_logs')->where('id', $task_id)->update(['status' => '已启动队列']);
         echo date('Y-m-d H:i:s') . " 队列已启动\n";
 
-        // 7. 删除当前队列任务
+        //删除当前队列任务
         $job->delete();
     }
 
+    /**
+     * 任务执行失败回调
+     *
+     * @param array $data 原始任务数据
+     */
     public function failed($data)
     {
         \think\Log::error("ImageArrJob 任务失败:" . json_encode($data, JSON_UNESCAPED_UNICODE));

+ 17 - 10
application/job/ImageJob.php

@@ -5,17 +5,24 @@ use think\Db;
 use think\queue\Job;
 use think\Queue;
 /**
- * 图生文队列任务
+ * 图生文任务处理类
+ * 功能:识别图片内容并调用大模型生成描述,支持链式任务衔接
  */
 class ImageJob{
+    /**
+     * 队列主入口
+     * @param Job $job 当前队列任务对象
+     * @param array $data 包含图片路径、提示词、任务ID、链式信息等
+     */
     public function fire(Job $job, $data)
     {
 
-        $logId = $data['log_id'] ?? null;
+        $logId = $data['log_id'];
 
         try {
-            echo date('Y-m-d H:i:s')."图生文开始\n";
+            echo date('Y-m-d H:i:s')."图生文任务开始\n";
 
+            // 更新日志状态:处理中
             if ($logId) {
                 Db::name('image_task_log')->where('id', $logId)->update([
                     'status' => 1,
@@ -24,9 +31,11 @@ class ImageJob{
                 ]);
             }
 
+            //执行逻辑
             $result = $this->processImage($data);
             echo $result;
 
+            // 更新日志状态:成功
             if ($logId) {
                 Db::name('image_task_log')->where('id', $logId)->update([
                     'status' => 2,
@@ -48,7 +57,7 @@ class ImageJob{
                     'data' => [ $data ]
                 ], 'arrimage');
             }
-
+            // 删除当前任务
             $job->delete();
         } catch (\Exception $e) {
             //异常处理,记录失败日志
@@ -63,16 +72,16 @@ class ImageJob{
                     'update_time' => date('Y-m-d H:i:s')
                 ]);
             }
+            // 删除当前任务
             $job->delete();
         }
     }
 
     /**
-     * 任务失败时的处理
+     * 失败回调(可用于后续通知或重试机制)
      */
     public function failed($data)
     {
-        // 记录失败日志或发送通知
         echo "ImageJob failed: " . json_encode($data);
     }
 
@@ -88,7 +97,7 @@ class ImageJob{
 
 
     /**
-     * 图生文接口
+     * 执行图生文逻辑(图像转文本)
      */
     public function imageToText($sourceDirRaw, $fileName, $prompt, $call_data)
     {
@@ -99,7 +108,6 @@ class ImageJob{
             $sourceDirRaw = preg_replace('/\/' . preg_quote($fileName, '/') . '$/', '', $sourceDirRaw);
         }
 
-        // 参数校验
         if ($sourceDirRaw === '' || $fileName === '') {
             return '参数错误:原图路径 或 图片名称 不能为空';
         }
@@ -110,7 +118,6 @@ class ImageJob{
         $filePath = $sourceDir . $fileName;
         $relativePath = $sourceDirRaw . '/' . $fileName;
 
-
         // 文件夹是否存在(绝对路径检查)
         if (!is_dir($sourceDir)) {
             return '源目录不存在:' . $sourceDir;
@@ -136,7 +143,7 @@ class ImageJob{
         $logDir = $rootPath . 'runtime/logs/';
         if (!is_dir($logDir)) mkdir($logDir, 0755, true);
 
-        // 调用图生文
+        // 调用 图生文 接口
         $ai = new AIGatewayService();
         $gptRes = $ai->callGptApi($imageUrl, $prompt);
 

+ 33 - 18
application/job/TextToImageJob.php

@@ -5,16 +5,23 @@ use think\Db;
 use think\Queue;
 use think\queue\Job;
 /**
- * 文生图队列任务
+ * 文生图任务处理类
+ * 描述:接收提示词,通过模型生成图像,保存图像并记录数据库信息,是链式任务中的最后一环
  */
 class TextToImageJob
 {
+    /**
+     * 队列入口方法
+     * @param Job $job 队列任务对象
+     * @param array $data 任务传参,包含图像文件名、路径、尺寸、提示词等
+     */
     public function fire(Job $job, $data)
     {
 
         $logId = $data['log_id'] ?? null;
 
         try {
+            // 任务类型校验(必须是文生图)
             if (!isset($data['type']) || $data['type'] !== '文生图') {
                 $job->delete();
                 return;
@@ -24,7 +31,7 @@ class TextToImageJob
             echo "━━━━━━━━━━ ▶ 文生图任务开始处理━━━━━━━━━━\n";
             echo "处理时间:{$startTime}\n";
 
-            // 更新日志状态处理中
+            // 更新日志状态处理中
             if ($logId) {
                 Db::name('image_task_log')->where('id', $logId)->update([
                     'status' => 1,
@@ -33,7 +40,7 @@ class TextToImageJob
                 ]);
             }
 
-            //文件路径 + 图片名称
+            //拼接原图文件路径 + 图片名称
             $old_image_url = rtrim($data['sourceDir'], '/') . '/' . ltrim($data['file_name'], '/');
 
             $list = Db::name("text_to_image")
@@ -60,7 +67,7 @@ class TextToImageJob
                         $data["height"],
                         $row["english_description"],
                         $row["img_name"],
-                        $data["selectedOption"] ?? null
+                        $data["selectedOption"]
                     );
 
                     $resultText = ($result === true || $result === 1 || $result === '成功') ? '成功' : '失败或无返回';
@@ -72,7 +79,7 @@ class TextToImageJob
                     echo "文生图已处理完成\n\n";
                 }
 
-                // 更新日志成功
+                // 更新日志状态:成功
                 if ($logId) {
                     Db::name('image_task_log')->where('id', $logId)->update([
                         'status' => 2,
@@ -134,7 +141,8 @@ class TextToImageJob
     }
 
     /**
-     * 文生图接口
+     * 文生图处理函数
+     * 描述:根据提示词调用图像生成接口,保存图像文件,并更新数据库
      */
     public function textToImage($fileName, $outputDirRaw, $width, $height, $prompt, $img_name,$selectedOption)
     {
@@ -143,7 +151,7 @@ class TextToImageJob
         $dateDir = date('Y-m-d') . '/';
         $fullBaseDir = $outputDir . $dateDir;
 
-        // 创建输出目录结构
+        // 创建所需的输出目录
         foreach ([$fullBaseDir, $fullBaseDir . '1024x1024/', $fullBaseDir . "{$width}x{$height}/"] as $dir) {
             if (!is_dir($dir)) {
                 mkdir($dir, 0755, true);
@@ -188,7 +196,6 @@ class TextToImageJob
         }
 
         //文生图调用
-//        $dalle1024 = $this->callDalleApi($prompt,$selectedOption);
         $ai = new AIGatewayService();
         $dalle1024 = $ai->callDalleApi($prompt,$selectedOption);
 
@@ -217,14 +224,13 @@ class TextToImageJob
         // 保存原图(1024x1024)
         $img_name = preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name);
         $img_name = mb_substr($img_name, 0, 30); // 限制为前30个字符(避免路径过长)
-
         $filename1024 = $img_name . '.png';
         $savePath1024 = $fullBaseDir . '1024x1024/' . $filename1024;
         file_put_contents($savePath1024, $imgData1024);
 
-        // 处理缩略
+        // 图像裁剪生成自定义尺寸
         $im = @imagecreatefromstring($imgData1024);
-        if (!$im) return "图像格式不支持或已损坏";
+        if (!$im) return "图像损坏或格式不支持";
 
         $srcWidth = imagesx($im);
         $srcHeight = imagesy($im);
@@ -254,20 +260,29 @@ class TextToImageJob
         imagedestroy($im);
         imagedestroy($dstImg);
 
+        // 成功写入数据库记录
         $status = trim($img_name) === '' ? 0 : 1;
-        // 更新数据库记录
+
+        // 根据 selectedOption 设置 quality 和 style
+        $quality = 'hd';
+        $style = 'vivid';
+        if ($selectedOption === 'dall-e-3') {
+            $quality = 'standard';
+            $style = 'vivid';
+        }
+
         $updateRes = Db::name('text_to_image')->where('id', $record['id'])->update([
             'new_image_url' => str_replace($rootPath . 'public/', '', $savePath1024),
             'custom_image_url' => str_replace($rootPath . 'public/', '', $savePathCustom),
             'img_name' => $img_name,
-            'error_msg' => '',
             'model' => $selectedOption,
-            'quality' => 'hd',
-            'style' => 'vivid',
-            'size' => "{$width}x{$height}",
-            'update_time' => date('Y-m-d H:i:s'),
             'status' => $status,
-            'status_name' => "文生图"
+            'status_name' => "文生图",
+            'size' => "{$width}x{$height}",
+            'quality' => $quality,
+            'style' => $style,
+            'error_msg' => '',
+            'update_time' => date('Y-m-d H:i:s')
         ]);
         return 0;
     }

+ 12 - 5
application/job/TextToTextJob.php

@@ -5,10 +5,16 @@ use think\Db;
 use think\queue\Job;
 use think\Queue;
 /**
- * 文生文队列任务
+ * 文生文任务队列处理类
+ * 基于图生文生成的描述,继续生成扩展文本内容(链式任务中间环)
  */
 class TextToTextJob
 {
+    /**
+     * 队列任务入口
+     * @param Job $job 当前队列任务
+     * @param array $data 包含任务上下文、路径信息、链式类型等
+     */
     public function fire(Job $job, $data)
     {
 
@@ -70,7 +76,7 @@ class TextToTextJob
                 }
             }
 
-            // 🔁 链式执行:检查是否还有下一个任务
+            // 链式执行:检查是否还有下一个任务
             if (!empty($data['chain_next'])) {
                 $nextType = array_shift($data['chain_next']); // 获取下一个任务类型
                 $data['type'] = $nextType;
@@ -98,7 +104,9 @@ class TextToTextJob
     }
 
     /**
-     * 文生文接口
+     * 文生文核心处理逻辑(调用 GPT 接口)
+     * @param int $id text_to_image 表主键
+     * @return string
      */
     public function textToTxt($id)
     {
@@ -114,8 +122,7 @@ class TextToTextJob
             ->find();
         if (!$record) {return '没有找到匹配的图像记录';}
 
-        // 调用文生文
-//        $gptRes = $this->TxtGptApi($template['english_content'].$record['english_description']);
+        // 拼接提示词调用 文生文 接口
         $ai = new AIGatewayService();
         $gptRes = $ai->txtGptApi($template['english_content'].$record['english_description']);
         $gptText = trim($gptRes['choices'][0]['message']['content'] ?? '');

+ 24 - 19
application/service/ImageService.php

@@ -8,10 +8,8 @@ use think\Queue;
  */
 class ImageService{
     /**
-     * 处理图像并推送到队列中
-     * sourceDir 源目录uploads/operate/ai/Preview/
-     * outputDir 输出目录/uploads/operate/ai/dall-e/hua/
-     * file_name 文件名0194b6fdd6203fda369d5e3b74b6b454.png
+     * 推送图像任务到队列(支持链式和单独模式)
+     * @param array $params 请求参数,包含图像批次、模型类型、尺寸等
      */
 
     public function handleImage($params) {
@@ -29,14 +27,14 @@ class ImageService{
             ->where('ids',1)
             ->find();
 
-        // 遍历每个图像,进行处理
+        // 构建任务基础结构(每图生成 N 份任务)
         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'] ?? '', // 获取处理类型
-                "selectedOption" => $params['selectedOption'], //文生图模型参数
+                "sourceDir" => $this->sourceDir($v, 1),
+                "outputDir" => $this->sourceDir($v, 2),
+                "file_name" => $this->sourceDir($v, 3),
+                "type" => $params['type'] ?? '',
+                "selectedOption" => $params['selectedOption'],
                 "prompt" => $template['content'],
                 "width" => $params['width'],
                 "height" => $params['height']
@@ -46,7 +44,7 @@ class ImageService{
         }
 
 
-        // 设置基础字段
+        // 插入队列日志基础信息
         $insertData = [
             'create_time'  => date('Y-m-d H:i:s'),
             'old_image_file'       => $params['old_image_file'],
@@ -55,8 +53,9 @@ class ImageService{
             'params'       => json_encode($params, JSON_UNESCAPED_UNICODE)
         ];
 
+        // 模型任务类型处理
         if (empty($params['type'])) {
-            // 未指定类型,执行链式任务
+            // 链式任务:图生文 → 文生文 → 文生图
             $insertData['model'] = "gpt-4-vision-preview,"."gpt-4,".$params['selectedOption'];
             $insertData['model_name'] = '文生图';
 
@@ -78,8 +77,7 @@ class ImageService{
 
             Queue::push('app\job\ImageArrJob', $payload, "arrimage");
         } else {
-
-            // 指定了单一任务
+            // 指定任务类型
             switch ($params['type']) {
                 case '图生文':
                     $insertData['model'] = 'gpt-4-vision-preview';
@@ -105,6 +103,7 @@ class ImageService{
                 return $item;
             }, $arr);
 
+            // 投递任务到队列
             $payload = [
                 'task_id' => $task_id,
                 'data' => $arr
@@ -117,11 +116,17 @@ class ImageService{
     }
 
     /**
-     * 解析图像路径并返回相关信息
+     * 解析图像路径,返回不同组成部分
      *
-     * @param string $filePath 图像文件路径
-     * @param int $type 返回类型标识
-     * @return string|null 返回解析后的路径或文件名
+     * @param string $filePath 图像路径(如 uploads/operate/ai/Preview/20240610/xxx.png)
+     * @param int $type 返回内容类型:
+     *        1 = 基础路径(去掉日期+文件名)
+     *          sourceDir 源目录uploads/operate/ai/Preview/
+     *        2 = 输出路径(Preview 替换为 dall-e,并加日期代表当天数据存到当天文件夹中)
+     *          outputDir 输出目录/uploads/operate/ai/dall-e/hua/并加日期
+     *        3 = 文件名
+     *          file_name 文件名0194b6fdd6203fda369d5e3b74b6b454.png
+     * @return string|null
      */
     public function sourceDir($filePath, $type) {
         $arr = [];
@@ -149,7 +154,7 @@ class ImageService{
 
         // 根据类型返回不同路径
         if ($type == 1) {
-            return $arr["basePath"]; // 只返回基础路径
+            return $arr["basePath"];
         }
         if ($type == 2) {
             return '/' . str_replace('/Preview/', '/dall-e/', $arr["basePath"]) . $arr["date"];