liuhairui 5 сар өмнө
parent
commit
f587e327f3

+ 366 - 572
application/api/controller/Facility.php

@@ -2,6 +2,7 @@
 namespace app\api\controller;
 use app\common\controller\Api;
 use think\Db;
+use think\File;
 use think\Request;
 use RecursiveIteratorIterator;
 use RecursiveDirectoryIterator;
@@ -10,27 +11,291 @@ class Facility extends Api
     protected $noNeedLogin = ['*'];
     protected $noNeedRight = ['*'];
 
-    /**
-     * 获取一张原目录图片对应明细数据
-     */
-    public function getlsit()
+    public function getPreviewSubDirs()
     {
-        // 获取前端传入的图片路径参数
-        $params = $this->request->param('path', '');
-        // 查询数据库
-        $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,imgtoimg_url')
-            ->where('old_image_url', $params)
+        $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
+        $baseRelativePath = 'uploads/operate/ai/Preview';
+
+        if (!is_dir($baseDir)) {
+            return json(['code' => 1, 'msg' => '目录不存在']);
+        }
+
+        $version = $this->generateFlexibleDirectoryHash($baseDir);
+        $cacheKey = 'preview_flexible_dirs_' . $version;
+
+        $dirList = cache($cacheKey);
+        if (!$dirList) {
+            $dirList = $this->scanFlexibleDirectories($baseDir, $baseRelativePath);
+            cache($cacheKey, $dirList);
+        }
+
+        return json([
+            'code' => 0,
+            'msg' => '获取成功',
+            'data' => $dirList
+        ]);
+    }
+
+    private function scanFlexibleDirectories($baseDir, $baseRelativePath)
+    {
+        $dirs = [];
+        $index = 1;
+
+        $firstLevelDirs = glob($baseDir . '/*', GLOB_ONLYDIR);
+        foreach ($firstLevelDirs as $level1Path) {
+            $secondLevelDirs = glob($level1Path . '/*', GLOB_ONLYDIR);
+
+            if ($secondLevelDirs) {
+                foreach ($secondLevelDirs as $level2Path) {
+                    $dirs = array_merge($dirs, $this->processDir($level2Path, $baseDir, $baseRelativePath, $index));
+                }
+            } else {
+                $dirs = array_merge($dirs, $this->processDir($level1Path, $baseDir, $baseRelativePath, $index));
+            }
+        }
+
+        usort($dirs, function ($a, $b) {
+            return $b['id'] - $a['id'];
+        });
+
+        return $dirs;
+    }
+
+    private function processDir($fullPath, $baseDir, $baseRelativePath, &$index)
+    {
+        $result = [];
+
+        $relativeDir = ltrim(str_replace($baseDir, '', $fullPath), '/');
+        $ctime = @filectime($fullPath) ?: time();
+
+        $imageFiles = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+        $originalImageCount = $imageFiles ? count($imageFiles) : 0;
+
+        $img_count = Db::name('text_to_image')
+            ->where('status', 1)
+            ->where('custom_image_url', '<>', '')
             ->where('img_name', '<>', '')
-            ->order('id desc')
-            ->select();
-        return json(['code' => 0, 'msg' => '查询成功', 'data' => $res,'count'=>count($res)]);
+            ->whereLike('old_image_url', $baseRelativePath . '/' . $relativeDir . '/%')
+            ->count();
+
+        $queueLog = Db::name('image_task_log')
+            ->whereLike('file_name', $baseRelativePath . '/' . $relativeDir . '/%')
+            ->whereLike('log', '%处理中%')
+            ->order('id', 'desc')
+            ->find();
+
+
+        if ($img_count === 0 && !$queueLog && $originalImageCount === 0) {
+            return [];
+        }
+
+        $result[] = [
+            'id' => $index++,
+            'name' => basename($fullPath),
+            'ctime' => $ctime,
+            'ctime_text' => date('Y-m-d H:i:s', $ctime),
+            'old_img_count' => $originalImageCount,
+            'new_image_count' => $img_count,
+            'old_image_url' => $baseRelativePath . '/' . $relativeDir,
+            'new_image_url' => '/uploads/operate/ai/dall-e/',
+            'queueLog_id' => $queueLog['id'] ?? '',
+            'queueLog_task_id' => $queueLog['task_id'] ?? '',
+            'queueLog_model_name' => $queueLog['model_name'] ?? '',
+            'queueLog_model_name_status' => $queueLog ? 1 : 0,
+        ];
+
+        return $result;
+    }
+
+
+    private function generateFlexibleDirectoryHash($baseDir)
+    {
+        $hash = '';
+        $dirPaths = [];
+
+        $firstDirs = glob($baseDir . '/*', GLOB_ONLYDIR);
+        foreach ($firstDirs as $dir1) {
+            $subDirs = glob($dir1 . '/*', GLOB_ONLYDIR);
+            if ($subDirs) {
+                foreach ($subDirs as $sub) {
+                    $dirPaths[] = $sub;
+                    $hash .= basename($dir1) . '/' . basename($sub) . filemtime($sub);
+                }
+            } else {
+                $dirPaths[] = $dir1;
+                $hash .= basename($dir1) . filemtime($dir1);
+            }
+        }
+
+        $baseRelativePath = 'uploads/operate/ai/Preview';
+        $queueStatusBits = [];
+
+        foreach ($dirPaths as $fullPath) {
+            $relativeDir = ltrim(str_replace($baseDir, '', $fullPath), '/');
+            $fileNameLike = $baseRelativePath . '/' . $relativeDir . '/%';
+
+            // 查询是否存在任何“处理中”的记录
+            $logs = Db::name('image_task_log')
+                ->whereLike('file_name', $fileNameLike)
+                ->whereLike('log', '%处理中%')
+                ->select();
+
+            // 转换为布尔状态再转成位标记(0 或 1)
+            $queueStatusBits[] = count($logs) > 0 ? '1' : '0';
+
+            // 可选:调试打印
+            // echo "<pre>路径:{$fileNameLike} => 状态:" . (count($logs) > 0 ? '有处理中' : '无') . "</pre>";
+        }
+
+        // 队列状态位图拼接
+        $queueStatusHash = implode('', $queueStatusBits); // 如:'01001'
+        $hash .= '_QS_' . md5($queueStatusHash); // 状态稳定扰动,无需 time()
+
+        return md5($hash);
     }
 
 
+//    private function generateFlexibleDirectoryHash($baseDir)
+//    {
+//        $hash = '';
+//        $firstDirs = glob($baseDir . '/*', GLOB_ONLYDIR);
+//        foreach ($firstDirs as $dir1) {
+//            $subDirs = glob($dir1 . '/*', GLOB_ONLYDIR);
+//            if ($subDirs) {
+//                foreach ($subDirs as $sub) {
+//                    $hash .= basename($dir1) . '/' . basename($sub) . filemtime($sub);
+//                }
+//            } else {
+//                $hash .= basename($dir1) . filemtime($dir1);
+//            }
+//        }
+//        return md5($hash);
+//    }
+
+
+
+//    /**
+//     * 采用缓存机制
+//     * 获取原图目录及每个目录下的图片数量(优化版)
+//     */
+//    public function getPreviewSubDirs()
+//    {
+//        // 1. 设置基础路径
+//        $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
+//        $baseRelativePath = 'uploads/operate/ai/Preview';
+//
+//        // 2. 检查目录是否存在
+//        if (!is_dir($baseDir)) {
+//            return json(['code' => 1, 'msg' => '目录不存在']);
+//        }
+//
+//        // 3. 获取目录最后修改时间作为缓存标识
+//        $cacheKey = 'preview_dirs_' . md5($baseDir);
+//        $lastModified = filemtime($baseDir);
+//        $cacheVersionKey = $cacheKey . '_version';
+//
+//        // 4. 检查缓存版本是否匹配
+//        if (cache($cacheVersionKey) != $lastModified) {
+//            cache($cacheKey, null);
+//            cache($cacheVersionKey, $lastModified, 86400);
+//        }
+//
+//        // 5. 尝试从缓存获取
+//        if (!$dirList = cache($cacheKey)) {
+//            // 6. 重新扫描目录
+//            $dirList = $this->scanDirectories($baseDir, $baseRelativePath);
+//            cache($cacheKey, $dirList, 86400); // 缓存1天
+//        }
+//
+//
+//        return json([
+//            'code' => 0,
+//            'msg' => '获取成功',
+//            'data' => $dirList
+//        ]);
+//    }
+//
+//    /**
+//     * 扫描目录结构
+//     */
+//    private function scanDirectories($baseDir, $baseRelativePath)
+//    {
+//
+//        $dirs = [];
+//        $index = 1;
+//        $processedDirs = [];
+//
+//        $scanDir = function ($dirPath, $relativePath) use (&$scanDir, &$dirs, &$index, &$processedDirs) {
+//            $items = @scandir($dirPath) ?: [];
+//            foreach ($items as $item) {
+//                if ($item === '.' || $item === '..') continue;
+//
+//                $fullPath = $dirPath . '/' . $item;
+//                $relPath = $relativePath . '/' . $item;
+//
+//                if (is_dir($fullPath)) {
+//                    $dirKey = md5($fullPath);
+//                    if (!isset($processedDirs[$dirKey])) {
+//                        $processedDirs[$dirKey] = true;
+//                        $scanDir($fullPath, $relPath);
+//                    }
+//                } elseif (preg_match('/\.(jpg|jpeg|png)$/i', $item)) {
+//                    $parentDir = dirname($fullPath);
+//                    $relativeDir = dirname($relPath);
+//                    $key = md5($parentDir);
+//
+//                    if (!isset($dirs[$key])) {
+//                        $ctime = @filectime($parentDir) ?: time();
+//
+//                        // 数据库查询
+//                        $hasData = Db::name('text_to_image')
+//                            ->where('custom_image_url', '<>', '')
+//                            ->where('img_name', '<>', '')
+//                            ->whereLike('old_image_url', $relativeDir . '/%')
+//                            ->where('status',1)
+//                            ->cache(true, 300)
+//                            ->count();
+//
+//                        $queue_logs = Db::name('queue_logs')
+//                            ->whereLike('file_name', $relativeDir . '/%')
+//                            ->group('file_name')
+//                            ->order('id desc')
+//                            ->find();
+//
+//
+//
+//                        $imageFiles = @glob($parentDir . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+//                        $imageCount = $imageFiles ? count($imageFiles) : 0;
+//
+//                        $dirs[$key] = [
+//                            'model_name'=> $queue_logs['model_name'],
+//                            'id' => $index++,
+//                            'name' => basename($parentDir),
+//                            'count' => $hasData,
+//                            'ctime' => $ctime,
+//                            'ctime_text' => date('Y-m-d H:i:s', $ctime),
+//                            'image_count' => $imageCount,
+//                            'new_image_url' => "/uploads/operate/ai/dall-e/",
+//                            'old_image_url' => $relativeDir
+//                        ];
+//                    }
+//                }
+//            }
+//        };
+//
+//        $scanDir($baseDir, $baseRelativePath);
+//
+//        // 按ID降序排序
+//        $dirList = array_values($dirs);
+//        usort($dirList, function ($a, $b) {
+//            return $b['id'] - $a['id'];
+//        });
+//
+//        return $dirList;
+//    }
+
     /**
-     * 缓存及时
-     * 获取指定目录所有图片
+     * 获取指定目录所有图片(完全实时版本)
      */
     public function getPreviewimg()
     {
@@ -47,25 +312,26 @@ class Facility extends Api
             return json(['code' => 1, 'msg' => '原图目录不存在']);
         }
 
-        // 构建缓存键与构建锁键
+        // 构建缓存键与构建锁键(仅缓存文件系统信息)
         $hash = md5($relativePath);
-        $cacheKey = "previewimg_full_{$hash}";
+        $cacheKey = "previewimg_fileinfo_{$hash}";
         $lockKey = "previewimg_building_{$hash}";
         $cacheExpire = 600; // 10分钟
 
-        $fullData = cache($cacheKey);
+        $cachedFileInfo = cache($cacheKey);
 
-        if (!$fullData) {
-            // 防止缓存“惊群效应”:只允许一个任务生成缓存,其它等待
+        if (!$cachedFileInfo) {
+            // 防止缓存"惊群效应"
             if (!cache($lockKey)) {
                 cache($lockKey, 1, 60); // 1分钟构建锁
 
+                // 获取所有图片文件信息
                 $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
-                $imageInfoMap = [];
+                $fileInfoMap = [];
                 foreach ($allImages as $imgPath) {
                     $relative = str_replace('\\', '/', trim(str_replace($basePath, '', $imgPath), '/'));
                     $info = @getimagesize($imgPath);
-                    $imageInfoMap[$relative] = [
+                    $fileInfoMap[$relative] = [
                         'width' => $info[0] ?? 0,
                         'height' => $info[1] ?? 0,
                         'size_kb' => round(filesize($imgPath) / 1024, 2),
@@ -73,66 +339,82 @@ class Facility extends Api
                     ];
                 }
 
-                $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')
-                    ->where('mod_rq', null)
-                    ->field('file_name, log')
-                    ->select();
-
-                $queueMap = [];
-                foreach ($queueRecords as $q) {
-                    $key = str_replace('\\', '/', trim($q['file_name'], '/'));
-                    $queueMap[$key] = $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[] = [
+                // 构建缓存数据(仅文件系统信息)
+                $cachedFileInfo = [];
+                foreach (array_keys($fileInfoMap) as $path) {
+                    $cachedFileInfo[] = [
                         '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] ?? ''
+                        'info' => $fileInfoMap[$path]
                     ];
                 }
 
                 // 设置缓存 + 删除构建锁
-                cache($cacheKey, $fullData, $cacheExpire);
+                cache($cacheKey, $cachedFileInfo, $cacheExpire);
                 cache($lockKey, null);
             } else {
-                // 如果有构建锁,等待缓存生成后再读
+                // 等待缓存生成
                 $waitTime = 0;
-                while (!$fullData && $waitTime < 10) {
+                while (!$cachedFileInfo && $waitTime < 10) {
                     sleep(1);
                     $waitTime++;
-                    $fullData = cache($cacheKey);
+                    $cachedFileInfo = cache($cacheKey);
                 }
-                if (!$fullData) {
+                if (!$cachedFileInfo) {
                     return json(['code' => 2, 'msg' => '系统正忙,请稍后重试']);
                 }
             }
         }
 
+        // 获取所有需要实时查询的路径
+        $paths = array_column($cachedFileInfo, 'path');
+
+        // 实时查询数据库状态信息(单次批量查询)
+        $dbRecords = Db::name('text_to_image')
+            ->whereIn('old_image_url', $paths)
+            ->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')
+            ->where('mod_rq', null)
+            ->whereIn('file_name', $paths)
+            ->field('file_name, log')
+            ->select();
+
+        // 实时查询same_count(稍后按需查询)
+
+        // 构建映射关系
+        $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'];
+        }
+
+        // 合并数据
+        $mergedData = [];
+        foreach ($cachedFileInfo as $data) {
+            $path = $data['path'];
+            $item = $processedMap[$path] ?? [];
+
+            $mergedData[] = [
+                'path' => $path,
+                'item' => $item,
+                'info' => $data['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] ?? ''
+            ];
+        }
+
         // 筛选状态字段
-        $filtered = array_filter($fullData, function ($data) use ($status, $status_name) {
+        $filtered = array_filter($mergedData, 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;
@@ -142,18 +424,18 @@ class Facility extends Api
         $total = count($filtered);
         $paged = array_slice(array_values($filtered), ($page - 1) * $limit, $limit);
 
-        // 统计 same_count
-        $paths = array_column($paged, 'path');
+        // 实时查询当前页的same_count(优化性能)
+        $pagedPaths = array_column($paged, 'path');
         $sameCountMap = [];
-        if ($paths) {
+        if ($pagedPaths) {
             $sameCountMap = Db::name('text_to_image')
-                ->whereIn('old_image_url', $paths)
+                ->whereIn('old_image_url', $pagedPaths)
                 ->where('new_image_url', '<>', '')
                 ->group('old_image_url')
                 ->column('count(*) as cnt', 'old_image_url');
         }
 
-        // 构建返回数据
+        // 构建最终结果
         $result = [];
         foreach ($paged as $i => $data) {
             $path = $data['path'];
@@ -163,6 +445,7 @@ class Facility extends Api
             $result[] = [
                 'id' => ($page - 1) * $limit + $i + 1,
                 'path' => $path,
+                // 实时数据
                 'status' => $data['dbStatus'],
                 'status_name' => $data['dbStatusName'],
                 'same_count' => $sameCountMap[$path] ?? 0,
@@ -173,9 +456,9 @@ class Facility extends Api
                 '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']
             ];
         }
@@ -189,513 +472,24 @@ class Facility extends Api
             '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
-//        ]);
-//    }
 
     /**
-     * 采用缓存机制
-     * 获取原图目录及每个目录下的图片数量(优化版)
+     * 通过服务器中目录获取对应数据
      */
-    public function getPreviewSubDirs()
-    {
-        // 1. 设置基础路径
-        $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
-        $baseRelativePath = 'uploads/operate/ai/Preview';
-
-        // 2. 检查目录是否存在
-        if (!is_dir($baseDir)) {
-            return json(['code' => 1, 'msg' => '目录不存在']);
-        }
-
-        // 3. 获取目录最后修改时间作为缓存标识
-        $cacheKey = 'preview_dirs_' . md5($baseDir);
-        $lastModified = filemtime($baseDir);
-        $cacheVersionKey = $cacheKey . '_version';
-
-        // 4. 检查缓存版本是否匹配
-        if (cache($cacheVersionKey) != $lastModified) {
-            cache($cacheKey, null);
-            cache($cacheVersionKey, $lastModified, 86400);
-        }
-
-        // 5. 尝试从缓存获取
-        if (!$dirList = cache($cacheKey)) {
-            // 6. 重新扫描目录
-            $dirList = $this->scanDirectories($baseDir, $baseRelativePath);
-            cache($cacheKey, $dirList, 86400); // 缓存1天
-        }
-
-        return json([
-            'code' => 0,
-            'msg' => '获取成功',
-            'data' => $dirList
-        ]);
-    }
-
-    /**
-     * 扫描目录结构
-     */
-    private function scanDirectories($baseDir, $baseRelativePath)
-    {
-        $dirs = [];
-        $index = 1;
-        $processedDirs = [];
-
-        $scanDir = function ($dirPath, $relativePath) use (&$scanDir, &$dirs, &$index, &$processedDirs) {
-            $items = @scandir($dirPath) ?: [];
-            foreach ($items as $item) {
-                if ($item === '.' || $item === '..') continue;
-
-                $fullPath = $dirPath . '/' . $item;
-                $relPath = $relativePath . '/' . $item;
-
-                if (is_dir($fullPath)) {
-                    $dirKey = md5($fullPath);
-                    if (!isset($processedDirs[$dirKey])) {
-                        $processedDirs[$dirKey] = true;
-                        $scanDir($fullPath, $relPath);
-                    }
-                } elseif (preg_match('/\.(jpg|jpeg|png)$/i', $item)) {
-                    $parentDir = dirname($fullPath);
-                    $relativeDir = dirname($relPath);
-                    $key = md5($parentDir);
-
-                    if (!isset($dirs[$key])) {
-                        $ctime = @filectime($parentDir) ?: time();
-
-                        // 数据库查询
-                        $hasData = Db::name('text_to_image')
-                            ->where('custom_image_url', '<>', '')
-                            ->where('img_name', '<>', '')
-                            ->whereLike('old_image_url', $relativeDir . '/%')
-                            ->where('status',1)
-                            ->cache(true, 300)
-                            ->count();
-
-                        $imageFiles = @glob($parentDir . '/*.{jpg,jpeg,png}', GLOB_BRACE);
-                        $imageCount = $imageFiles ? count($imageFiles) : 0;
-
-                        $dirs[$key] = [
-                            'id' => $index++,
-                            'name' => basename($parentDir),
-                            'count' => $hasData,
-                            'ctime' => $ctime,
-                            'ctime_text' => date('Y-m-d H:i:s', $ctime),
-                            'image_count' => $imageCount,
-                            'new_image_url' => "/uploads/operate/ai/dall-e/",
-                            'old_image_url' => $relativeDir
-                        ];
-                    }
-                }
-            }
-        };
-
-        $scanDir($baseDir, $baseRelativePath);
-
-        // 按ID降序排序
-        $dirList = array_values($dirs);
-        usort($dirList, function ($a, $b) {
-            return $b['id'] - $a['id'];
-        });
-
-        return $dirList;
-    }
-
-    /**
-     * 手动清除缓存(在目录变更后调用)
-     */
-    public function clearCache()
+    public function getlsit()
     {
-        $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
-        $cacheKey = 'preview_dirs_' . md5($baseDir);
-
-        cache($cacheKey, null);
-        cache($cacheKey . '_version', null);
-
-        return json(['code' => 0, 'msg' => '缓存已清除']);
+        // 获取前端传入的图片路径参数
+        $params = $this->request->param('path', '');
+        // 查询数据库
+        $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,imgtoimg_url')
+            ->where('old_image_url', $params)
+            ->where('img_name', '<>', '')
+            ->order('id desc')
+            ->select();
+        return json(['code' => 0, 'msg' => '查询成功', 'data' => $res,'count'=>count($res)]);
     }
 
-    /**
-     * 不使用缓存机制(查询速度较慢)
-     * 获取原图目录及每个目录下的图片数量
-     */
-//    public function getPreviewSubDirs()
-//    {
-//        $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview';
-//        $baseRelativePath = 'uploads/operate/ai/Preview';
-//
-//        if (!is_dir($baseDir)) {
-//            return json(['code' => 1, 'msg' => '目录不存在']);
-//        }
-//
-//        $dirs = [];
-//        $index = 1;
-//
-//        /**
-//         * 递归扫描目录,提取含图片的子目录信息
-//         */
-//        $scanDir = function ($dirPath, $relativePath) use (&$scanDir, &$dirs, &$index) {
-//            $items = scandir($dirPath);
-//            foreach ($items as $item) {
-//                if ($item === '.' || $item === '..') continue;
-//
-//                $fullPath = $dirPath . '/' . $item;
-//                $relPath = $relativePath . '/' . $item;
-//
-//                if (is_dir($fullPath)) {
-//                    // 递归子目录
-//                    $scanDir($fullPath, $relPath);
-//                } else {
-//                    // 匹配图片文件
-//                    if (preg_match('/\.(jpg|jpeg|png)$/i', $item)) {
-//                        $parentDir = dirname($fullPath);
-//                        $relativeDir = dirname($relPath);
-//                        $key = md5($parentDir);
-//
-//                        if (!isset($dirs[$key])) {
-//                            $ctime = filectime($parentDir);
-//
-//                            // 数据库统计:已处理图片数量
-//                            $hasData = Db::name('text_to_image')
-//                                ->where('custom_image_url', '<>', '')
-//                                ->where('img_name', '<>', '')
-//                                ->whereLike('old_image_url', $relativeDir . '/%')
-//                                ->where('status',1)
-//                                ->whereNotNull('custom_image_url')
-//                                ->count();
-//
-//                            // 当前目录下图片数量
-//                            $imageFiles = glob($parentDir . '/*.{jpg,jpeg,png}', GLOB_BRACE);
-//                            $imageCount = is_array($imageFiles) ? count($imageFiles) : 0;
-//
-//                            $dirs[$key] = [
-//                                'id' => $index++,
-//                                'name' => basename($parentDir),
-//                                'count' => $hasData,
-//                                'ctime' => $ctime, // 时间戳,用于排序
-//                                'ctime_text' => date('Y-m-d H:i:s', $ctime), // 格式化日期,用于显示
-//                                'image_count' => $imageCount,
-//                                'new_image_url' => "/uploads/operate/ai/dall-e/",
-//                                'old_image_url' => $relativeDir
-//                            ];
-//                        }
-//                    }
-//                }
-//            }
-//        };
-//
-//        // 执行目录扫描
-//        $scanDir($baseDir, $baseRelativePath);
-//
-//        // 排序:按照创建时间(从新到旧)
-//        $dirList = array_values($dirs);
-////        usort($dirList, function ($a, $b) {
-////            return $b['ctime'] - $a['ctime'];
-////        });
-//
-//        usort($dirList, function ($a, $b) {
-//            return $b['id'] - $a['id'];
-//        });
-//
-//
-//        return json([
-//            'code' => 0,
-//            'msg' => '获取成功',
-//            'data' => $dirList
-//        ]);
-//    }
-
-
     /**
      * 图片上传
      */

+ 38 - 108
application/api/controller/WorkOrder.php

@@ -33,40 +33,46 @@ class WorkOrder extends Api
      */
     public function imgtowimg()
     {
-        //原图路径
-        $imgRelPath = 'uploads/operate/ai/Preview/arr/该图案传递了旅行和无忧无虑的心情采用明快的色调充满活力的复古.png';
+        $prompt = $this->request->param('prompt', '');
+        $denoising = (float)$this->request->param('denoising_strength', 0.2);
+        $scale = (float)$this->request->param('scale', 2.0);
+        $modelName = $this->request->param('model', 'realisticVisionV51_v51VAE-inpainting.safetensors [f0d4872d24]');
+
+        // 原图路径
+        $imgRelPath = 'uploads/operate/ai/Preview/arr/0828004096727.png';
         $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
 
         if (!file_exists($imgPath)) {
             return json(['code' => 1, 'msg' => '原图不存在:' . $imgRelPath]);
         }
 
-        // 原图 base64 编码
+        // 获取原图尺寸 × 倍数
+        list($originW, $originH) = getimagesize($imgPath);
+        $targetW = intval($originW * $scale);
+        $targetH = intval($originH * $scale);
+
+        // base64 编码
         $imgData = file_get_contents($imgPath);
         $base64Img = base64_encode($imgData);
         $initImage = 'data:image/png;base64,' . $base64Img;
 
-        // 参数设置
+        // 请求体
         $postData = json_encode([
-            'prompt' => '',
-            'sampler_name' => 'DPM++ 2M SDE Heun',
-            'seed' =>  -1,
-            'steps' => 20,
+            'prompt' => $prompt,
+            'steps' => 30,
             'cfg_scale' => 7,
-            'denoising_strength' => 0.2,
-            'width' => 1024,
-            'height' => 2048,
-            'inpainting_fill' => 1,
-            'resize_mode' => 0, // 自动等比缩放+填充
+            'denoising_strength' => $denoising,
+            'width' => $targetW,
+            'height' => $targetH,
+            'resize_mode' => 1,
             'inpaint_full_res' => true,
+            'inpainting_fill' => 1,
             'init_images' => [$initImage],
             'override_settings' => [
-                'sd_model_checkpoint' => 'realisticVisionV51_v51VAE-inpainting.safetensors [f0d4872d24]',
-                'sd_vae' => 'anything-v4.5.vae.pt'  // 设置外挂 VAE 模型
+                'sd_model_checkpoint' => $modelName
             ]
         ]);
 
-        //调用接口
         $apiUrl = "http://20.0.17.233:45001/sdapi/v1/img2img";
         $headers = ['Content-Type: application/json'];
 
@@ -87,10 +93,10 @@ class WorkOrder extends Api
 
         $data = json_decode($response, true);
         if (!isset($data['images'][0])) {
-            return json(['code' => 1, 'msg' => '未返回图像数据']);
+            return json(['code' => 1, 'msg' => '接口未返回图像数据']);
         }
 
-        // 保存结果图像
+        // 保存图像:原图名 + -1
         $resultImg = base64_decode($data['images'][0]);
         $saveDir = ROOT_PATH . 'public/uploads/img2img/';
         if (!is_dir($saveDir)) {
@@ -98,105 +104,19 @@ class WorkOrder extends Api
         }
 
         $originalBaseName = pathinfo($imgRelPath, PATHINFO_FILENAME);
-        $fileName = $originalBaseName . '.png';
+        $fileName = $originalBaseName . '-1.png';
         $savePath = $saveDir . $fileName;
         file_put_contents($savePath, $resultImg);
 
         return json([
             'code' => 0,
-            'msg' => '图像上下扩展生成成功',
+            'msg' => '图像生成成功',
             'data' => [
                 'origin_url' => '/uploads/img2img/' . $fileName
             ]
         ]);
     }
 
-//    public function imgtowimg()
-//    {
-//        $prompt = $this->request->param('prompt', '');
-//        $denoising = (float)$this->request->param('denoising_strength', 0.2);
-//        $scale = (float)$this->request->param('scale', 2.0);
-//        $modelName = $this->request->param('model', 'realisticVisionV51_v51VAE-inpainting.safetensors [f0d4872d24]');
-//
-//        // 原图路径
-//        $imgRelPath = 'uploads/operate/ai/Preview/arr/0828004096727.png';
-//        $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
-//
-//        if (!file_exists($imgPath)) {
-//            return json(['code' => 1, 'msg' => '原图不存在:' . $imgRelPath]);
-//        }
-//
-//        // 获取原图尺寸 × 倍数
-//        list($originW, $originH) = getimagesize($imgPath);
-//        $targetW = intval($originW * $scale);
-//        $targetH = intval($originH * $scale);
-//
-//        // base64 编码
-//        $imgData = file_get_contents($imgPath);
-//        $base64Img = base64_encode($imgData);
-//        $initImage = 'data:image/png;base64,' . $base64Img;
-//
-//        // 请求体
-//        $postData = json_encode([
-//            'prompt' => $prompt,
-//            'steps' => 30,
-//            'cfg_scale' => 7,
-//            'denoising_strength' => $denoising,
-//            'width' => $targetW,
-//            'height' => $targetH,
-//            'resize_mode' => 1,
-//            'inpaint_full_res' => true,
-//            'inpainting_fill' => 1,
-//            'init_images' => [$initImage],
-//            'override_settings' => [
-//                'sd_model_checkpoint' => $modelName
-//            ]
-//        ]);
-//
-//        $apiUrl = "http://20.0.17.233:45001/sdapi/v1/img2img";
-//        $headers = ['Content-Type: application/json'];
-//
-//        $ch = curl_init();
-//        curl_setopt($ch, CURLOPT_URL, $apiUrl);
-//        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-//        curl_setopt($ch, CURLOPT_POST, true);
-//        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
-//        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
-//        curl_setopt($ch, CURLOPT_TIMEOUT, 90);
-//        $response = curl_exec($ch);
-//        $error = curl_error($ch);
-//        curl_close($ch);
-//
-//        if ($error) {
-//            return json(['code' => 1, 'msg' => '请求失败:' . $error]);
-//        }
-//
-//        $data = json_decode($response, true);
-//        if (!isset($data['images'][0])) {
-//            return json(['code' => 1, 'msg' => '接口未返回图像数据']);
-//        }
-//
-//        // 保存图像:原图名 + -1
-//        $resultImg = base64_decode($data['images'][0]);
-//        $saveDir = ROOT_PATH . 'public/uploads/img2img/';
-//        if (!is_dir($saveDir)) {
-//            mkdir($saveDir, 0755, true);
-//        }
-//
-//        $originalBaseName = pathinfo($imgRelPath, PATHINFO_FILENAME);
-//        $fileName = $originalBaseName . '-1.png';
-//        $savePath = $saveDir . $fileName;
-//        file_put_contents($savePath, $resultImg);
-//
-//        return json([
-//            'code' => 0,
-//            'msg' => '图像生成成功',
-//            'data' => [
-//                'origin_url' => '/uploads/img2img/' . $fileName
-//            ]
-//        ]);
-//    }
-
     /**
      * 后期图像处理
      * /sdapi/v1/extra-single-image
@@ -320,9 +240,17 @@ class WorkOrder extends Api
                 }
             }
 
-            if ($log['处理中数量'] > 0) {
+            // if ($log['排队中的数量'] >$log['已完成数量']) {
+            //     $result[] = $log;
+            // }
+
+            if ($log['排队中的数量']) {
                 $result[] = $log;
             }
+
+            // if ($log['处理中数量'] >= 0) {
+            //     $result[] = $log;
+            // }
         }
 
         return json([
@@ -422,8 +350,9 @@ class WorkOrder extends Api
         $key_txttoimg = 'queues:txttoimg';
         $key_txttotxt = 'queues:txttotxt';
         $key_imgtotxt = 'queues:imgtotxt';
+        $key_imgtoimg = 'queues:imgtoimg';
 
-        $count = $redis->lLen($key_txttoimg) + $redis->lLen($key_txttotxt) + $redis->lLen($key_imgtotxt);
+        $count = $redis->lLen($key_txttoimg) + $redis->lLen($key_txttotxt) + $redis->lLen($key_imgtotxt) + $redis->lLen($key_imgtoimg);
 
         if ($count === 0) {
             return json([
@@ -436,6 +365,7 @@ class WorkOrder extends Api
         $redis->del($key_txttoimg);
         $redis->del($key_txttotxt);
         $redis->del($key_imgtotxt);
+        $redis->del($key_imgtoimg);
 
 
         $counts = Db::name('image_task_log')

+ 3 - 4
application/job/TextToImageJob.php

@@ -48,16 +48,15 @@ class TextToImageJob
                 ->where('img_name', '<>', '')
                 ->where('status', 0)
                 ->select();
-          
+
 
             if (!empty($list)) {
                 $total = count($list);
-                echo "📊 共需处理:{$total} 条记录\n\n";
+                echo "📊 共需处理:{$total} 条记录\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";
 
                     // 调用生成图像方法
@@ -285,7 +284,7 @@ class TextToImageJob
             'error_msg' => '',
             'update_time' => date('Y-m-d H:i:s')
         ]);
-        return 0;
+        return "成功";
     }
 
 }

+ 169 - 38
application/service/AIGatewayService.php

@@ -22,16 +22,16 @@ class AIGatewayService{
             'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
             'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
         ],
-        //文生图
-        'txttoimg' => [
-            'api_key' => 'sk-e0JuPjMntkbgi1BoMjrqyyzMKzAxILkQzyGMSy3xiMupuoWY',
-            'api_url' => 'https://niubi.zeabur.app/v1/images/generations'
-        ],
-//        //文生文-超结损分析
-//        'chaojiesun' => [
+//        //文生图
+//        'txttoimg' => [
 //            'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
 //            'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
 //        ]
+//        //文生图【权限不足】
+        'txttoimg' => [
+            'api_key' => 'sk-e0JuPjMntkbgi1BoMjrqyyzMKzAxILkQzyGMSy3xiMupuoWY',
+            'api_url' => 'https://niubi.zeabur.app/v1/images/generations'
+        ]
     ];
 
     /**
@@ -243,43 +243,174 @@ class AIGatewayService{
      */
     public function callApi($url, $apiKey, $data)
     {
-        $maxRetries = 2;           // 最多重试次数
-        $attempt = 0;              // 当前尝试次数
-        $lastError = '';           // 最后一次错误信息
+        $maxRetries = 2;
+        $attempt = 0;
+        $lastError = '';
+        $httpCode = 0;
+        $apiErrorDetail = '';
 
         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) {
+            try {
+                $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 => true,
+                    CURLOPT_SSL_VERIFYHOST => 2,
+                    CURLOPT_CONNECTTIMEOUT => 30,
+                    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
+                    CURLOPT_FAILONERROR => true
+                ]);
+
+                $response = curl_exec($ch);
+                $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+                $curlError = curl_error($ch);
+
+                if ($response === false) {
+                    throw new \Exception("请求发送失败: " . $curlError);
+                }
+
                 $result = json_decode($response, true);
+
+                // 检查API返回的错误
+                if (isset($result['error'])) {
+                    $apiErrorDetail = $result['error']['message'] ?? '';
+                    $errorType = $result['error']['type'] ?? '';
+
+                    // 常见错误类型映射
+                    $errorMessages = [
+                        'invalid_request_error' => '请求参数错误',
+                        'authentication_error' => '认证失败',
+                        'rate_limit_error' => '请求频率过高',
+                        'insufficient_quota' => '额度不足',
+                        'billing_not_active' => '账户未开通付费',
+                        'content_policy_violation' => '内容违反政策'
+                    ];
+
+                    $friendlyMessage = $errorMessages[$errorType] ?? 'API服务错误';
+                    throw new \Exception("{$friendlyMessage}: {$apiErrorDetail}");
+                }
+
+                if ($httpCode !== 200) {
+                    // HTTP状态码映射
+                    $statusMessages = [
+                        400 => '请求参数不合法',
+                        401 => 'API密钥无效或权限不足',
+                        403 => '访问被拒绝',
+                        404 => 'API端点不存在',
+                        429 => '请求过于频繁,请稍后再试',
+                        500 => '服务器内部错误',
+                        503 => '服务暂时不可用'
+                    ];
+
+                    $statusMessage = $statusMessages[$httpCode] ?? "HTTP错误({$httpCode})";
+                    throw new \Exception($statusMessage);
+                }
+
+                curl_close($ch);
                 return $result;
-            }
 
-            $lastError = $curlError ?: "HTTP错误:{$httpCode}";
-            $attempt++;
-            sleep(1);
+            } catch (\Exception $e) {
+                $lastError = $e->getMessage();
+                $attempt++;
+                if ($attempt <= $maxRetries) {
+                    sleep(pow(2, $attempt));
+                } else {
+                    // 最终失败时的详细错误信息
+                    $errorDetails = [
+                        '错误原因' => $this->getErrorCause($httpCode, $apiErrorDetail),
+                        '解决方案' => $this->getErrorSolution($httpCode),
+                        '请求参数' => json_encode($data, JSON_UNESCAPED_UNICODE),
+                        'HTTP状态码' => $httpCode,
+                        '重试次数' => $attempt
+                    ];
+
+                    throw new \Exception("API请求失败\n" .
+                        "失败说明: " . $errorDetails['错误原因'] . "\n" .
+                        "建议解决方案: " . $errorDetails['解决方案'] . "\n" .
+                        "技术详情: HTTP {$httpCode} - " . $lastError);
+                }
+            }
         }
+    }
 
-        throw new \Exception("请求失败(重试{$maxRetries}次):{$lastError}");
+    /**
+     * 根据错误类型获取友好的错误原因
+     */
+    private function getErrorCause($httpCode, $apiError)
+    {
+        $causes = [
+            401 => 'API密钥无效、过期或没有访问权限',
+            400 => $apiError ?: '请求参数不符合API要求',
+            429 => '已达到API调用频率限制',
+            403 => '您的账户可能没有开通相关服务权限',
+            500 => 'OpenAI服务器处理请求时出错'
+        ];
+
+        return $causes[$httpCode] ?? '未知错误,请检查网络连接和API配置';
     }
+
+    /**
+     * 根据错误类型获取解决方案建议
+     */
+    private function getErrorSolution($httpCode)
+    {
+        $solutions = [
+            401 => '1. 检查API密钥是否正确 2. 确认密钥是否有访问权限 3. 尝试创建新密钥',
+            400 => '1. 检查请求参数 2. 验证提示词内容 3. 参考API文档修正参数',
+            429 => '1. 等待1分钟后重试 2. 升级账户提高限额 3. 优化调用频率',
+            403 => '1. 检查账户状态 2. 确认是否已开通付费 3. 联系OpenAI支持',
+            500 => '1. 等待几分钟后重试 2. 检查OpenAI服务状态页'
+        ];
+
+        return $solutions[$httpCode] ?? '1. 检查网络连接 2. 查看服务日志 3. 联系技术支持';
+    }
+//    public function callApi($url, $apiKey, $data)
+//    {
+//        $maxRetries = 2;           // 最多重试次数
+//        $attempt = 0;              // 当前尝试次数
+//        $lastError = '';           // 最后一次错误信息
+//
+//        while ($attempt <= $maxRetries) {
+//            $ch = curl_init();
+//            curl_setopt_array($ch, [
+//                CURLOPT_URL => $url,
+//                CURLOPT_RETURNTRANSFER => true,
+//                CURLOPT_POST => true,
+//                CURLOPT_POSTFIELDS => json_encode($data),
+//                CURLOPT_HTTPHEADER => [
+//                    'Content-Type: application/json',
+//                    'Authorization: Bearer ' . $apiKey
+//                ],
+//                CURLOPT_TIMEOUT => 120,
+//                CURLOPT_SSL_VERIFYPEER => false,
+//                CURLOPT_SSL_VERIFYHOST => 0,
+//                CURLOPT_TCP_KEEPALIVE => 1,
+//                CURLOPT_FORBID_REUSE => false
+//            ]);
+//
+//            $response = curl_exec($ch);
+//            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+//            $curlError = curl_error($ch);
+//            curl_close($ch);
+//
+//            if ($response !== false && $httpCode === 200) {
+//                $result = json_decode($response, true);
+//                return $result;
+//            }
+//
+//            $lastError = $curlError ?: "HTTP错误:{$httpCode}";
+//            $attempt++;
+//            sleep(1);
+//        }
+//
+//        throw new \Exception("请求失败(重试{$maxRetries}次):{$lastError}");
+//    }
 }