liuhairui 6 сар өмнө
parent
commit
9a0beb3a16

+ 348 - 110
application/api/controller/Facility.php

@@ -9,13 +9,76 @@ class Facility extends Api
     protected $noNeedLogin = ['*'];
     protected $noNeedRight = ['*'];
 
+    public function packImagess()
+    {
+        try {
+            $params = $this->request->post();
+            $paths = $params['paths'] ?? [];
+
+            if (empty($paths) || !is_array($paths)) {
+                return json(['code' => 1, 'msg' => '路径参数不能为空或格式不正确']);
+            }
+
+            // 设置基础路径和压缩目录路径
+            $basePath = ROOT_PATH . 'public/';
+            $zipDir = $basePath . 'uploads/operate/ai/zip/';
+
+            if (!is_dir($zipDir)) {
+                mkdir($zipDir, 0755, true);
+            }
+
+            // 压缩包文件名及完整路径
+            $fileName = 'images_' . date('Ymd_His') . '.zip';
+            $zipPath = $zipDir . $fileName;
+
+            // 创建 Zip 文件
+            $zip = new \ZipArchive();
+            if ($zip->open($zipPath, \ZipArchive::CREATE) !== TRUE) {
+                return json(['code' => 1, 'msg' => '无法创建压缩包']);
+            }
+
+            // 添加文件到压缩包
+            $addCount = 0;
+            foreach ($paths as $relativePath) {
+                $relativePath = ltrim($relativePath, '/');
+                $fullPath = $basePath . $relativePath;
+
+                if (file_exists($fullPath)) {
+                    $zip->addFile($fullPath, basename($fullPath)); // 仅保存文件名
+                    $addCount++;
+                }
+            }
+
+            $zip->close();
+
+            if ($addCount === 0) {
+                return json(['code' => 1, 'msg' => '未找到有效图片,未生成压缩包']);
+            }
+
+            // 返回下载地址(注意路径与保存路径一致)
+            $downloadUrl = request()->domain() . '/uploads/operate/ai/zip/' . $fileName;
+
+            return json([
+                'code' => 0,
+                'msg' => '打包成功',
+                'download_url' => $downloadUrl
+            ]);
+        } catch (\Exception $e) {
+            return json([
+                'code' => 1,
+                'msg' => '异常错误:' . $e->getMessage()
+            ]);
+        }
+    }
+
+
     public function getlsit()
     {
         // 获取前端传入的图片路径参数
         $params = $this->request->param('path', '');
         // 查询数据库
         $res = Db::name('text_to_image')->alias('b')
-            ->field('b.chinese_description,b.english_description,b.new_image_url,b.custom_image_url,b.size,b.old_image_url')
+            ->field('b.chinese_description,b.english_description,b.new_image_url,b.custom_image_url,b.size,b.old_image_url,b.img_name')
             ->where('old_image_url', $params)
             ->where('custom_image_url', '<>', '')
             ->where('status', 1)
@@ -26,59 +89,98 @@ class Facility extends Api
 
 
     //获取指定目录所有图片
-    public function getPreviewimg(){
+    public function getPreviewimg()
+    {
+        $page = (int)$this->request->param('page', 1);
+        $limit = (int)$this->request->param('limit', 50);
+        $status = $this->request->param('status', '');
+
         $relativePath = $this->request->param('path', '');
         $basePath = ROOT_PATH . 'public/';
         $fullPath = $basePath . $relativePath;
+
         if (!is_dir($fullPath)) {
             return json(['code' => 1, 'msg' => '目录不存在']);
         }
-        // 1. 获取该目录下所有图片
-        $images = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
-        // 2. 获取数据库中已处理的图片数据
-        $processedList = Db::name('text_to_image')
-            ->field('old_image_url,new_image_url,custom_image_url,chinese_description,english_description')
+
+        // 1. 获取所有图片路径(不再全部加载到内存)
+        $allImages = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
+        if (empty($allImages)) {
+            return json(['code' => 0, 'msg' => '暂无图片', 'data' => [], 'total' => 0]);
+        }
+        // ✅ 加入排序:按照创建时间从新到旧
+        usort($allImages, function ($a, $b) {
+            return filectime($b) - filectime($a);
+        });
+        // 构建相对路径数组
+        $relativeImages = array_map(function ($imgPath) use ($basePath) {
+            return str_replace($basePath, '', $imgPath);
+        }, $allImages);
+
+        // 2. 提前构建是否已出图map
+        $dbRecords = Db::name('text_to_image')
+            ->whereIn('old_image_url', $relativeImages)
             ->where('custom_image_url', '<>', '')
-            ->whereNotNull('custom_image_url')
+            ->field('old_image_url,new_image_url,custom_image_url,chinese_description,english_description,img_name')
             ->select();
-        // 3. 构建以 old_image_url 为 key 的映射数组,方便快速查找
+
         $processedMap = [];
-        foreach ($processedList as $item) {
+        foreach ($dbRecords as $item) {
             $processedMap[$item['old_image_url']] = $item;
         }
-        // 4. 组装结果数据
-        $data = [];
-        $id = 1;
-        foreach ($images as $imgPath) {
+
+        // 3. 提前获取 same_count 的统计
+        $sameCountMap = Db::name('text_to_image')
+            ->whereIn('old_image_url', $relativeImages)
+            ->where('custom_image_url', '<>', '')
+            ->group('old_image_url')
+            ->column('count(*) as cnt', 'old_image_url');
+
+        // 4. 构造最终筛选数据(分页前进行状态筛选)
+        $filtered = [];
+        foreach ($allImages as $imgPath) {
             $relative = str_replace($basePath, '', $imgPath);
-            $info = getimagesize($imgPath);
-            $sizeKB = round(filesize($imgPath) / 1024, 2);
-            $ctime = date('Y-m-d H:i:s', filectime($imgPath));
+            $processed = $processedMap[$relative] ?? null;
+            $isProcessed = $processed ? 1 : 0;
 
-            $processed = $processedMap[$relative] ?? '';
+            // 状态过滤
+            if ($status === 'processed' && !$isProcessed) continue;
+            if ($status === 'unprocessed' && $isProcessed) continue;
 
-            // 查询数据库中相同 old_image_url 的条数
-            $sameCount = Db::name('text_to_image')
-                ->where('old_image_url', $relative)
-                ->where('custom_image_url', '<>', '')
-                ->count();
+            $info = @getimagesize($imgPath); // 加@防止报错
+            $sizeKB = round(filesize($imgPath) / 1024, 2);
+            $ctime = date('Y-m-d H:i:s', filectime($imgPath));
 
-            $data[] = [
-                'id' => $id++,
+            $filtered[] = [
                 'path' => $relative,
                 'width' => $info[0] ?? 0,
                 'height' => $info[1] ?? 0,
                 'size_kb' => $sizeKB,
                 'created_time' => $ctime,
-                'is_processed' => $processed ? 1 : 0,
+                'img_name' => $processed['img_name'] ?? '',
+                'is_processed' => $isProcessed,
                 'new_image_url' => $processed['new_image_url'] ?? '',
                 'custom_image_url' => $processed['custom_image_url'] ?? '',
                 'chinese_description' => ($processed['chinese_description'] ?? '') . ($processed['english_description'] ?? ''),
-                'same_count' => $sameCount  // ✅ 加入统计数量
+                'same_count' => $sameCountMap[$relative] ?? 0
             ];
         }
 
-        return json(['code' => 0, 'msg' => '获取成功', 'data' => $data]);
+        // 5. 手动分页(对少量已筛选后的数据)
+        $total = count($filtered);
+        $pagedData = array_slice($filtered, ($page - 1) * $limit, $limit);
+        foreach ($pagedData as $index => &$item) {
+            $item['id'] = ($page - 1) * $limit + $index + 1;
+        }
+
+        return json([
+            'code' => 0,
+            'msg' => '获取成功',
+            'data' => $pagedData,
+            'total' => $total,
+            'page' => $page,
+            'limit' => $limit
+        ]);
     }
 
     /**
@@ -86,68 +188,183 @@ class Facility extends Api
      */
     public function getPreviewSubDirs()
     {
-//        $baseDir = ROOT_PATH . 'public/uploads/operate/ai/Preview/';
-        $baseDir = rtrim(str_replace('\\', '/', ROOT_PATH), '/') . '/public/uploads/operate/ai/Preview/';
-        $baseRelativePath = 'uploads/operate/ai/Preview/';
+        $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' => '目录不存在']);
         }
 
-        $items = scandir($baseDir);
         $dirs = [];
         $index = 1;
 
-        foreach ($items as $item) {
-            if ($item === '.' || $item === '..') continue;
+        /**
+         * 递归扫描目录,提取含图片的子目录信息
+         */
+        $scanDir = function ($dirPath, $relativePath) use (&$scanDir, &$dirs, &$index) {
+            $items = scandir($dirPath);
+            foreach ($items as $item) {
+                if ($item === '.' || $item === '..') continue;
 
-            $fullPath = $baseDir . $item;
+                $fullPath = $dirPath . '/' . $item;
+                $relPath = $relativePath . '/' . $item;
 
-            if (!is_dir($fullPath)) continue;
+                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);
 
-            $relativeDir = $baseRelativePath . $item;
+                        if (!isset($dirs[$key])) {
+                            $ctime = filectime($parentDir);
 
-            // 查询该目录在数据库中是否有有效数据
-            $hasData = Db::name('text_to_image')
-                ->where('custom_image_url', '<>', '')
-                ->whereLike('old_image_url', $relativeDir . '/%')
-                ->whereNotNull('custom_image_url')
-                ->count();
-//            if ($hasData === 0) {
-//                continue; // 如果没有有效数据,跳过此目录
-//            }
+                            // 数据库统计:已处理图片数量
+                            $hasData = Db::name('text_to_image')
+                                ->where('custom_image_url', '<>', '')
+                                ->whereLike('old_image_url', $relativeDir . '/%')
+                                ->whereNotNull('custom_image_url')
+                                ->count();
 
-            // 统计图片数量
-            $imageFiles = glob($fullPath . '/*.{jpg,jpeg,png}', GLOB_BRACE);
-            $imageCount = is_array($imageFiles) ? count($imageFiles) : 0;
-            // 创建时间
-            $ctime = filectime($fullPath);
-            $formattedDate = date('Y-m-d', $ctime);
-
-            $dirs[] = [
-                'id' => $index++,
-                'name' => $item,
-                'count' => $hasData,
-                'ctime' => $formattedDate,
-                'image_count' => $imageCount,
-                'new_image_url' => "/uploads/operate/ai/dall-e/",
-                'old_image_url' => $relativeDir
-            ];
-        }
+                            // 当前目录下图片数量
+                            $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);
 
-        // 排序:按时间倒序
-        usort($dirs, function ($a, $b) {
-            return strtotime($b['ctime']) - strtotime($a['ctime']);
+        // 排序:按照创建时间(从新到旧)
+        $dirList = array_values($dirs);
+        usort($dirList, function ($a, $b) {
+            return $b['ctime'] - $a['ctime'];
         });
 
-        return json(['code' => 0, 'msg' => '获取成功', 'data' => $dirs]);
+        return json([
+            'code' => 0,
+            'msg' => '获取成功',
+            'data' => $dirList
+        ]);
     }
 
 
+
+
+
+
     /**
      * 图片上传
      * @return void
      */
+//    public function getUploadPath()
+//    {
+//        // 处理 CORS OPTIONS 预检请求
+//        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
+//            header('Access-Control-Allow-Origin: *');
+//            header('Access-Control-Allow-Methods: POST, OPTIONS');
+//            header('Access-Control-Allow-Headers: Content-Type, Authorization');
+//            header('Access-Control-Max-Age: 86400');
+//            exit(204);
+//        }
+//
+//// 实际请求必须返回 CORS 头
+//        header('Access-Control-Allow-Origin: *');
+//        $today = date('Ymd');
+//        $basePath = 'uploads/operate/ai/Preview/' . $today;
+//        $rootBasePath = ROOT_PATH . 'public/' . $basePath;
+//
+//        // 创建当天目录
+//        if (!is_dir($rootBasePath)) {
+//            mkdir($rootBasePath, 0755, true);
+//        }
+//
+//        // 获取子目录索引
+//        $dirs = array_filter(glob($rootBasePath . '/*'), 'is_dir');
+//        $usedIndexes = [];
+//
+//        foreach ($dirs as $dirPath) {
+//            $dirName = basename($dirPath);
+//            if (preg_match('/^\d{2}$/', $dirName)) {
+//                $usedIndexes[] = intval($dirName);
+//            }
+//        }
+//
+//        $nextIndex = empty($usedIndexes) ? 1 : max($usedIndexes) + 1;
+//        $subDir = str_pad($nextIndex, 2, '0', STR_PAD_LEFT);
+//        $relativePath = $basePath . '/' . $subDir;
+//        $targetPath = ROOT_PATH . 'public/' . $relativePath;
+//
+//        // 创建该批次目录
+//        if (!is_dir($targetPath)) {
+//            mkdir($targetPath, 0755, true);
+//        }
+//
+//        return json([
+//            'code' => 0,
+//            'msg' => '获取上传路径成功',
+//            'data' => [
+//                'upload_path' => $relativePath
+//            ]
+//        ]);
+//    }
+//    public function ImgUpload()
+//    {
+//        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
+//            header('Access-Control-Allow-Origin: *');
+//            header('Access-Control-Allow-Methods: POST, OPTIONS');
+//            header('Access-Control-Allow-Headers: Content-Type, Authorization');
+//            header('Access-Control-Max-Age: 86400');
+//            exit(204);
+//        }
+//        header('Access-Control-Allow-Origin: *');
+//
+//        $file = request()->file('image');
+//        $relativePath = input('post.upload_path');
+//
+//        if (!$file || !$relativePath) {
+//            return json(['code' => 1, 'msg' => '缺少上传文件或路径参数']);
+//        }
+//
+//        $targetPath = ROOT_PATH . 'public/' . $relativePath;
+//
+//        if (!is_dir($targetPath)) {
+//            mkdir($targetPath, 0755, true);
+//        }
+//
+//        $tmpFilePath = $file->getPathname();
+//        $extension = pathinfo($file->getInfo('name'), PATHINFO_EXTENSION);
+//        $hashName = hash_file('md5', $tmpFilePath);
+//        $newFileName = $hashName . '.' . $extension;
+//
+//        $info = $file->validate(['size' => 10 * 1024 * 1024, 'ext' => 'jpg,jpeg,png'])
+//            ->move($targetPath, $newFileName);
+//
+//        if ($info) {
+//            $imageUrl = $relativePath . '/' . str_replace('\\', '/', $newFileName);
+//            return json(['code' => 0, 'msg' => '上传成功', 'data' => ['url' => $imageUrl]]);
+//        } else {
+//            return json(['code' => 1, 'msg' => '上传失败', 'data' => $file->getError()]);
+//        }
+//    }
+
     public function ImgUpload()
     {
         // 处理 CORS OPTIONS 预检请求
@@ -159,62 +376,83 @@ class Facility extends Api
             exit(204);
         }
 
-        // 实际请求必须返回 CORS 头
+// 实际请求必须返回 CORS 头
         header('Access-Control-Allow-Origin: *');
 
+// 获取上传的文件
         $file = request()->file('image');
 
         if ($file) {
+            // 指定目标目录(你想上传到的目录)
+            $targetPath = ROOT_PATH . 'public' . DS . 'uploads' . DS . 'operate' . DS . 'ai' . DS . 'Preview';
+
+            // 若目录不存在则创建
+            if (!is_dir($targetPath)) {
+                mkdir($targetPath, 0755, true);
+            }
+
+            // 移动文件到指定目录,并验证大小/格式
             $info = $file->validate([
-                'size' => 10 * 1024 * 1024, // 10MB
-                'ext' => 'jpg,png,jpeg'
-            ])->move(ROOT_PATH . 'public' . DS . 'uploads/operate/ai/Preview/');
+                'size' => 10485760, // 最大10MB
+                'ext' => 'jpg,png'
+            ])->move($targetPath);
 
             if ($info) {
-                $saveName = $info->getSaveName();
-                $savePath = str_replace('\\', '/', $saveName);
-                $fullPath = ROOT_PATH . 'public/uploads/operate/ai/Preview/' . $savePath;
-                $relativePath = 'uploads/operate/ai/Preview/' . $savePath;
-
-                // 获取图片信息
-                $imgInfo = getimagesize($fullPath);
-                $width = $imgInfo[0] ?? 0;
-                $height = $imgInfo[1] ?? 0;
-                $size_kb = round(filesize($fullPath) / 1024, 2);
-                $created_time = date('Y-m-d H:i:s');
-
-                // 写入数据库 original_image 表
-                $insertData = [
-                    'path' => $relativePath,
-                    'width' => $width,
-                    'height' => $height,
-                    'size_kb' => $size_kb,
-                    'created_time' => $created_time
-                ];
-
-                $result = Db::name('original_image')->insert($insertData);
-
-                return json([
-                    'code' => 0,
-                    'msg' => '上传成功',
-                    'data' => [
-                        'url' => $relativePath,
-                        'db_inserted' => $result ? true : false
-                    ]
-                ]);
+                $fileName = $info->getSaveName();
+                $imageUrl = '/uploads/operate/ai/Preview/' . str_replace('\\', '/', $fileName);
+                return json(['code' => 0, 'msg' => '成功', 'data' => ['url' => $imageUrl]]);
             } else {
-                return json([
-                    'code' => 1,
-                    'msg' => '上传失败',
-                    'data' => $file->getError()
-                ]);
+                $res = $file->getError();
+                return json(['code' => 1, 'msg' => '失败', 'data' => $res]);
             }
         }
 
+        return json(['code' => 1, 'msg' => '没有文件上传', 'data' => null]);
+    }
+
+
+    /**
+     * 模版
+    */
+    public function Template(){
+        $Template = Db::name("template")->find();
         return json([
-            'code' => 1,
-            'msg' => '没有接收到上传文件',
-            'data' => null
+            'code' => 0,
+            'msg' => '模版',
+            'data' => $Template
         ]);
     }
+    /**
+     * 更新模版
+     */
+    public function updatetemplate(){
+        if (Request::instance()->isPost() == false){
+            $this->error('非法请求');
+        }
+
+        $params = Request::instance()->post();
+
+
+        // 验证传入的参数是否存在,避免空值
+        if (empty($params['textareaContent']) || empty($params['width']) || empty($params['height'])) {
+            return json(['code' => 1, 'msg' => '参数缺失']);
+        }
+
+        // 更新模板数据
+        $Template = Db::name("template")
+            ->where('id', 1) // 假设模板 ID 是 1,需根据实际情况修改
+            ->update([
+                'content' => $params['textareaContent'],  // 更新图生文模版内容
+                'width' => $params['width'],  // 更新宽度
+                'height' => $params['height'],  // 更新宽度
+            ]);
+
+        // 判断数据库更新是否成功
+        if ($Template){
+            return json(['code' => 0, 'msg' => '成功']);
+        }else{
+            return json(['code' => 1, 'msg' => '失败']);
+        }
+    }
+
 }

+ 218 - 145
application/api/controller/WorkOrder.php

@@ -28,6 +28,38 @@ class WorkOrder extends Api
         $this->success('成功存入队列中');
     }
 
+    /**
+     * 开启队列任务
+     */
+    public function kaiStats()
+    {
+        // 判断是否已有监听进程在运行
+        $check = shell_exec("ps aux | grep 'queue:listen' | grep -v grep");
+        if ($check) {
+            return json([
+                'code' => 1,
+                'msg'  => '监听进程已存在,请勿重复启动'
+            ]);
+        }
+
+        // 启动监听
+//        $command = 'php think queue:listen --queue --timeout=300 --sleep=3 --memory=256 > /dev/null 2>&1 &';
+        $command = 'nohup php think queue:listen --queue --timeout=300 --sleep=3 --memory=256 > /var/log/job_queue.log 2>&1 &';
+        exec($command, $output, $status);
+
+        if ($status === 0) {
+            return json([
+                'code' => 0,
+                'msg'  => '队列监听已启动'
+            ]);
+        } else {
+            return json([
+                'code' => 1,
+                'msg'  => '队列启动失败',
+                'output' => $output
+            ]);
+        }
+    }
 
     /**
      * 查看队列任务
@@ -36,11 +68,10 @@ class WorkOrder extends Api
     {
         $statusCounts = Db::name('queue_log')
             ->field('status, COUNT(*) as total')
-            ->whereTime('created_at', 'today')  // 只取今天的记录
+            ->whereTime('created_at', 'today')
             ->where('status','<>', 4)
             ->group('status')
             ->select();
-
         $result = [
             '待处理' => 0,
             '处理中' => 0,
@@ -76,84 +107,107 @@ class WorkOrder extends Api
     }
 
     /**
-     * 清空队列
+     * 清空停止队列(同时删除近30分钟的队列日志,并停止 systemd 队列服务)
      */
     public function stopQueueProcesses()
     {
-        $redis = new \Redis();
-        $redis->connect('127.0.0.1', 6379);
-        $redis->select(15);
+        try {
+            // 连接 Redis
+            $redis = new \Redis();
+            $redis->connect('127.0.0.1', 6379);
+            $redis->select(15);
 
-        $key = 'queues:default';
-        $count = $redis->lLen($key);
+            $key = 'queues:default';
+            $count = $redis->lLen($key);
 
-        if ($count === 0) {
-            return json([
-                'code' => 1,
-                'msg'  => '暂无队列需要停止'
-            ]);
-        }
+            // 计算时间:当前时间前 30 分钟
+            $time = date('Y-m-d H:i:s', strtotime('-30 minutes'));
 
-        $redis->del($key);
+            // 删除数据库中状态为0且在近30分钟的数据
+            $deleteCount = Db::name('queue_log')
+                ->where('status', 0)
+                ->where('created_at', '>=', $time)
+                ->delete();
 
-        Db::name('queue_log')
-            ->where('status', 0)
-            ->whereTime('created_at', 'today')
-            ->update([
-                'status'     => 4,
-                'updated_at' => date('Y-m-d H:i:s')
-            ]);
+            // 如果 Redis 队列不为空,则清空
+            if ($count > 0) {
+                $redis->del($key);
+            }
 
-        return json([
-            'code' => 0,
-            'msg'  => '已成功停止队列任务,共清除 ' . $count . ' 条'
-        ]);
-    }
+            // 尝试停止 systemd 队列服务
+            exec('sudo /bin/systemctl stop think-queue.service', $output, $status);
 
-    /**
-     * 显示当前运行中的队列监听进程
-     */
-    public function viewQueueStatus()
-    {
-//        // 构建 10 条模拟任务
-//        $mockTasks = [];
-//        for ($i = 1; $i <= 10; $i++) {
-//            $mockTasks[] = [
-//                'job' => 'app\\job\\ImageJob',
-//                'data' => [
-//                    'dir_name'     => '/uploads/operate/ai/Preview/20250511/',
-//                    'file_name'    => "46c086fa3214f7548a0dc4f7595be140.png",
-//                    'prompt'       => '请按以下要求分析图案,详细描述图案信息提示词,描述信息仅限图案本体,生成的描述信息',
-//                    'outputDirRaw' => '/uploads/operate/ai/dall-e/',
-//                    'width'        => '679',
-//                    'height'       => '862'
-//                ],
-//                'id' => 'mockID' . $i,
-//                'attempts' => rand(1, 10)
-//            ];
+            if ($status === 0) {
+                return json([
+                    'code' => 0,
+                    'msg'  => "队列服务已成功停止"
+                ]);
+            } else {
+                return json([
+                    'code' => 2,
+                    'msg'  => "但服务停止失败,请检查权限"
+                ]);
+            }
+        } catch (\Exception $e) {
+            return json([
+                'code' => 500,
+                'msg'  => '处理异常:' . $e->getMessage()
+            ]);
+        }
+    }
+//    public function stopQueueProcesses()
+//    {
+//        $redis = new \Redis();
+//        $redis->connect('127.0.0.1', 6379);
+//        $redis->select(15);
+//
+//        $key = 'queues:default';
+//        $count = $redis->lLen($key);
+//        // 计算时间:当前时间前30分钟
+//        $time = date('Y-m-d H:i:s', strtotime('-30 minutes'));
+//
+//        if ($count === 0) {
+//            // 删除数据库中状态为0且 created_at 在最近30分钟的数据
+//            $deleteCount = Db::name('queue_log')
+//                ->where('created_at', '>=', $time)
+//                ->delete();
+//            return json([
+//                'code' => 1,
+//                'msg'  => '暂无队列需要停止'
+//            ]);
 //        }
 //
+//        // 清空 Redis 队列
+//        $redis->del($key);
+//
+//
+//
+//        // 删除数据库中状态为0且 created_at 在最近30分钟的数据
+//        $deleteCount = Db::name('queue_log')
+//            ->where('created_at', '>=', $time)
+//            ->delete();
+//
 //        return json([
 //            'code' => 0,
-//            'msg'  => '查询成功(模拟10条)',
-//            'count' => count($mockTasks),
-//            'tasks_preview' => $mockTasks
+//            'msg'  => '已成功停止队列任务'
 //        ]);
-//        die;
+//    }
+
 
+    /**
+     * 显示当前运行中的队列监听进程
+     */
+    public function viewQueueStatus()
+    {
         $redis = new \Redis();
         $redis->connect('127.0.0.1', 6379);
         $redis->select(15);
-
         $key = 'queues:default';
-
         $count = $redis->lLen($key);
         $list = $redis->lRange($key, 0, 9);
-
         $parsed = array_map(function ($item) {
             return json_decode($item, true);
         }, $list);
-
         return json([
             'code' => 0,
             'msg'  => '查询成功',
@@ -163,17 +217,6 @@ class WorkOrder extends Api
     }
 
 
-    //单个调用
-//    protected $config = [
-//        'gpt' => [
-//            'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
-//            'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
-//        ],
-//        'dalle' => [
-//            'api_key' => 'sk-e0JuPjMntkbgi1BoMjrqyyzMKzAxILkQzyGMSy3xiMupuoWY',
-//            'api_url' => 'https://niubi.zeabur.app/v1/images/generations'
-//        ]
-//    ];
 //    public function imageToTexts()
 //    {
 //        $params = $this->request->param();
@@ -457,13 +500,58 @@ class WorkOrder extends Api
 ////            return json(['code' => 1, 'msg' => '文生图失败:' . $e->getMessage()]);
 ////        }
 //    }
+
+
+
+    protected $config = [
+        'gpt' => [
+            'api_key' => 'sk-Bhos1lXTRpZiAAmN06624a219a874eCd91Dc068b902a3e73',
+            'api_url' => 'https://one.opengptgod.com/v1/chat/completions'
+        ],
+        'dalle' => [
+            'api_key' => 'sk-e0JuPjMntkbgi1BoMjrqyyzMKzAxILkQzyGMSy3xiMupuoWY',
+            'api_url' => 'https://niubi.zeabur.app/v1/images/generations'
+        ]
+    ];
+
+
+    public function callDallesssss()
+    {
+        // 确保目录存在
+        $rootPath = str_replace('\\', '/', ROOT_PATH);
+        $imgDir = rtrim($rootPath . 'public/' . '/uploads/img/', '/') . '/';
+        if (!is_dir($imgDir)) mkdir($imgDir, 0755, true);
+
+        $filename = 'dalle_' . date('Ymd_His') . '_' . rand(1000, 9999) . '.png';
+        $savePath = $imgDir . $filename;
+
+        $prompt = "这幅图案呈现了一棵精致的树木,树干与枝条采用金色设计,树上盛开着纯白色的花朵。花瓣层层叠加,呈现出优雅的立体感,花心部分呈现细腻的黄色。树叶也以金色为主,整体色调偏向温暖的金白色,背景简洁纯净,整体给人一种高雅且现代的艺术感";
+        $response = $this->callDalleApi($prompt);
+        if (!isset($response['data'][0]['url'])) {
+            throw new \Exception("图像生成失败,未返回图片链接。返回内容:" . json_encode($response));
+        }
+        $imageUrl = $response['data'][0]['url'];
+        // 下载图片到本地目录
+        $imgContent = file_get_contents($imageUrl);
+        if ($imgContent === false) {
+            throw new \Exception("无法下载生成的图像:{$imageUrl}");
+        }
+        file_put_contents($savePath, $imgContent);
+        // 日志记录
+        $logDir = $rootPath . 'runtime/logs/';
+        if (!is_dir($logDir)) mkdir($logDir, 0755, true);
+        file_put_contents($logDir . 'prompt_log.txt', date('Y-m-d H:i:s') . " prompt: {$prompt}\nURL: {$imageUrl}\n", FILE_APPEND);
+        echo "图像生成成功:public/uploads/img/{$filename}\n";
+    }
+
+
 //
 //    /**
 //     * 调用 DALL·E 接口
 //     * 文生图
 //     */
-//    public function callDalleApi($prompt)
-//    {
+    public function callDalleApi($prompt)
+    {
 //        $data = [
 //            'prompt' => $prompt,
 //            'model'  => 'dall-e-2',
@@ -471,82 +559,67 @@ class WorkOrder extends Api
 //            'size'   => '1024x1024'
 //        ];
 //        return $this->callApi($this->config['dalle']['api_url'], $this->config['dalle']['api_key'], $data);
-////        $data = [
-////            'prompt'  => "A stylized representation of a Dallas football team logo, featuring a helmet in shades of gray and white with blue and black accents. The word 'Dallas' in bold, italicized, gray-white capital letters on a dark blue curved banner, with the year '1960' in smaller font at the bottom, matching the helmet's color scheme. The design reflects the visual elements and style typical of American football culture, presented on a plain black background.",
-////            'model'   => 'dall-e-2',
-////            'n'       => 1,
-////            'size'    => '1024x1024',
-////            'quality' => 'standard',
-////            'style'   => 'vivid'
-////        ];
-////
-////        return $this->callApi($this->config['dalle']['api_url'], $this->config['dalle']['api_key'], $data);
-//    }
-//
-//    /**
-//     * 翻译为英文
-//     */
-//    public function translateToEnglish($text)
-//    {
-//        $data = [
-//            'model' => 'gpt-3.5-turbo',
-//            'messages' => [[
-//                'role' => 'user',
-//                'content' => "请将以下内容翻译为英文,仅输出英文翻译内容,不需要解释:\n\n{$text}"
-//            ]],
-//            'max_tokens' => 300,
-//            'temperature' => 0.3
-//        ];
-//
-//        $response = $this->callApi($this->config['gpt']['api_url'], $this->config['gpt']['api_key'], $data);
-//        return trim($response['choices'][0]['message']['content'] ?? '');
-//    }
-//
-//
-//    /**
-//     * 通用API调用方法
-//     */
-//    public function callApi($url, $apiKey, $data)
-//    {
-//        $maxRetries = 2;
-//        $attempt = 0;
-//        $lastError = '';
-//
-//        while ($attempt <= $maxRetries) {
-//            $ch = curl_init();
-//            curl_setopt_array($ch, [
-//                CURLOPT_URL => $url,
-//                CURLOPT_RETURNTRANSFER => true,
-//                CURLOPT_POST => true,
-//                CURLOPT_POSTFIELDS => json_encode($data),
-//                CURLOPT_HTTPHEADER => [
-//                    'Content-Type: application/json',
-//                    'Authorization: Bearer ' . $apiKey
-//                ],
-//                CURLOPT_TIMEOUT => 120,
-//                CURLOPT_SSL_VERIFYPEER => false,
-//                CURLOPT_SSL_VERIFYHOST => 0,
-//                CURLOPT_TCP_KEEPALIVE => 1,
-//                CURLOPT_FORBID_REUSE => false
-//            ]);
-//
-//            $response = curl_exec($ch);
-//            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
-//            $curlError = curl_error($ch);
-//            curl_close($ch);
-//
-//            if ($response !== false && $httpCode === 200) {
-//                $result = json_decode($response, true);
-//                return $result;
-//            }
-//
-//            $lastError = $curlError ?: "HTTP错误:{$httpCode}";
-//            $attempt++;
-//            sleep(1);
-//        }
-//
-//        throw new \Exception("请求失败(重试{$maxRetries}次):{$lastError}");
-//    }
+        $data = [
+            'prompt' => $prompt,
+            'model'   => 'dall-e-2',
+//            'model'   => 'gpt-image-1',
+            'n'       => 1,
+            'size'    => '1024x1024',
+            'quality' => 'standard',
+            'style'   => 'vivid',
+            'response_format' => 'url'
+        ];
+        return $this->callApi($this->config['dalle']['api_url'], $this->config['dalle']['api_key'], $data);
+    }
+
+    /**
+     * 通用API调用方法
+     */
+    public function callApi($url, $apiKey, $data)
+    {
+        $maxRetries = 2;
+        $attempt = 0;
+        $lastError = '';
+
+        while ($attempt <= $maxRetries) {
+            $ch = curl_init();
+            curl_setopt_array($ch, [
+                CURLOPT_URL => $url,
+                CURLOPT_RETURNTRANSFER => true,
+                CURLOPT_POST => true,
+                CURLOPT_POSTFIELDS => json_encode($data),
+                CURLOPT_HTTPHEADER => [
+                    'Content-Type: application/json',
+                    'Authorization: Bearer ' . $apiKey
+                ],
+                CURLOPT_TIMEOUT => 120,
+                CURLOPT_SSL_VERIFYPEER => false,
+                CURLOPT_SSL_VERIFYHOST => 0,
+                CURLOPT_TCP_KEEPALIVE => 1,
+                CURLOPT_FORBID_REUSE => false
+            ]);
+
+            $response = curl_exec($ch);
+            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+            $curlError = curl_error($ch);
+            curl_close($ch);
+
+            if ($response !== false && $httpCode === 200) {
+                $result = json_decode($response, true);
+                return $result;
+            }
+
+            $lastError = $curlError ?: "HTTP错误:{$httpCode}";
+            $attempt++;
+            sleep(1);
+        }
+
+        throw new \Exception("请求失败(重试{$maxRetries}次):{$lastError}");
+    }
+
+
+
+
 //
 //    /**
 //     * 记录到数据库