liuhairui 4 сар өмнө
parent
commit
95992c8d93

+ 38 - 150
application/api/controller/Facility.php

@@ -20,13 +20,24 @@ class Facility extends Api
             return json(['code' => 1, 'msg' => '目录不存在']);
         }
 
+        // 包含 DB 时间戳扰动的缓存键
         $version = $this->generateFlexibleDirectoryHash($baseDir);
         $cacheKey = 'preview_flexible_dirs_' . $version;
 
         $dirList = cache($cacheKey);
         if (!$dirList) {
             $dirList = $this->scanFlexibleDirectories($baseDir, $baseRelativePath);
-            cache($cacheKey, $dirList);
+            cache($cacheKey, $dirList, 300); // 缓存 5 分钟(可调)
+        } else {
+            // 实时刷新 new_image_count(避免缓存值过期)
+            foreach ($dirList as &$dir) {
+                $dir['new_image_count'] = Db::name('text_to_image')
+                    ->where('status', 1)
+                    ->where('custom_image_url', '<>', '')
+                    ->where('img_name', '<>', '')
+                    ->whereLike('old_image_url', $dir['old_image_url'] . '/%')
+                    ->count();
+            }
         }
 
         return json([
@@ -36,6 +47,7 @@ class Facility extends Api
         ]);
     }
 
+
     private function scanFlexibleDirectories($baseDir, $baseRelativePath)
     {
         $dirs = [];
@@ -78,6 +90,7 @@ class Facility extends Api
             ->whereLike('old_image_url', $baseRelativePath . '/' . $relativeDir . '/%')
             ->count();
 
+
         $queueLog = Db::name('image_task_log')
             ->whereLike('file_name', $baseRelativePath . '/' . $relativeDir . '/%')
             ->whereLike('log', '%处理中%')
@@ -155,145 +168,6 @@ class Facility extends Api
     }
 
 
-//    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;
-//    }
-
     /**
      * 获取指定目录所有图片(完全实时版本)
      */
@@ -578,7 +452,7 @@ class Facility extends Api
     }
 
     /**
-     * 打包图片
+     * 打包图片(支持对象结构中的 path 字段)
      */
     public function packImagess()
     {
@@ -590,7 +464,22 @@ class Facility extends Api
                 return json(['code' => 1, 'msg' => '路径参数不能为空或格式不正确']);
             }
 
-            // 设置基础路径和压缩目录路径
+            // 提取所有合法的路径(支持字符串或对象中带 path 字段)
+            $validPaths = [];
+
+            foreach ($paths as $item) {
+                if (is_string($item)) {
+                    $validPaths[] = $item;
+                } elseif (is_array($item) && isset($item['path']) && is_string($item['path'])) {
+                    $validPaths[] = $item['path'];
+                }
+            }
+
+            if (empty($validPaths)) {
+                return json(['code' => 1, 'msg' => '没有有效的图片路径']);
+            }
+
+            // 设置基本路径和 zip 目录
             $basePath = ROOT_PATH . 'public/';
             $zipDir = $basePath . 'uploads/operate/ai/zip/';
 
@@ -598,24 +487,23 @@ class Facility extends Api
                 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, '/');
+            foreach ($validPaths as $relativePath) {
+                $relativePath = ltrim($relativePath, '/');  // 去除前导斜杠
                 $fullPath = $basePath . $relativePath;
 
                 if (file_exists($fullPath)) {
-                    $zip->addFile($fullPath, basename($fullPath)); // 仅保存文件名
+                    // 使用 basename 作为压缩包内的文件名(不保留路径结构)
+                    $zip->addFile($fullPath, basename($fullPath));
                     $addCount++;
                 }
             }
@@ -623,10 +511,10 @@ class Facility extends Api
             $zip->close();
 
             if ($addCount === 0) {
-                return json(['code' => 1, 'msg' => '未找到有效图片,未生成压缩包']);
+                @unlink($zipPath);
+                return json(['code' => 1, 'msg' => '未找到有效图片文件,未生成压缩包']);
             }
 
-            // 返回下载地址(注意路径与保存路径一致)
             $downloadUrl = request()->domain() . '/uploads/operate/ai/zip/' . $fileName;
 
             return json([

+ 174 - 75
application/api/controller/WorkOrder.php

@@ -28,51 +28,63 @@ class WorkOrder extends Api
 
 
 
-    /**
-     * 图生图功能-单张图片本地测试使用
-     * 接口地址: /sdapi/v1/img2img
-     */
+
     public function imgtowimg()
     {
-        $prompt = $this->request->param('prompt', '将图片不完整部分补充完整');
+        $prompt = $this->request->param('prompt', '将图片纵向扩展至1248像素');
         $imgRelPath = 'uploads/operate/ai/Preview/arr/一朵盛开的白色牡丹花为主体采用厚涂技法花心和背景点缀金箔灰银.png';
         $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
-        //原图是否存在
+
         if (!file_exists($imgPath)) {
             return json(['code' => 1, 'msg' => '原图不存在:' . $imgRelPath]);
         }
 
-        // -------- 图像编码 -------- //
         $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,                    // CFG 强度
-            'denoising_strength' => 0.2,        // 重绘强度
-            'width' => 679,                      // 图像宽度
-            'height' => 862,                     // 图像高度
-            'resize_mode' => 1,                 // 保留原图比例并裁剪
-            'inpaint_full_res' => true,         // 使用原图分辨率
-            'inpaint_full_res_padding' => 64,   // 边缘补全像素
-            'mask_blur' => 4,                   // 蒙版柔化
-            'inpainting_fill' => 3,             // 自动填充内容(不是黑色)
-            'sampler_name' => 'DPM++ 2M SDE',   // 采样器
-            'scheduler' => 'Exponential',       // ✅ 调度类型(补充字段)
-            'seed' => 3689437019,               // 固定种子(确保结果可复现)
-            'init_images' => [$initImage],      // 原图 base64
+            'prompt' => "1girl",
+            'negative_prompt' => '(nsfw),sketches,tattoo,(beard:1.3,(EasyNegative:1.3),badhandv,(Teeth:1.3),(worst quality:2),(low quality:2),(normal quality:2),lowers,normal quality,facing away,looking away,text,error,extra digit,fewer digits,cropped,jpeg artifacts,signature,watermark,username,blurry,skin spots,acnes,skin blemishes,bad anatomy,fat,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,tilted hands,extra fingers,extra limbs,extra arms,extra legs,malformed proportions,gross proportions,missing fingers,missing toes)',
+            'steps' => 20,
+            'cfg_scale' => 7,
+            'seed' => -1,
+            'clip_skip' => 7,
+            'denoising_strength' => 0.2,
+            'width' => 1024,
+            'height' => 1248,
+            'resize_mode' => 2, // 缩放后填充空白
+            'inpainting_fill' => 0, // 保留原图内容
+            'mask_blur' => 0,
+            'sampler_name' => 'Euler a',
+            'init_images' => [$initImage],
             'override_settings' => [
-                'sd_model_checkpoint' => 'AbyssOrangeMix2_sfw', // 模型名
-                'sd_vae' => "Automatic",
-                'CLIP_stop_at_last_layers' => 2
+                'sd_model_checkpoint' => 'Realistic_Vision_V5.0-inpainting',
+                'sd_vae' => 'vae-ft-mse-840000-ema-pruned',
+                'CLIP_stop_at_last_layers' => 7
             ],
-            'override_settings_restore_afterwards' => true
+            'override_settings_restore_afterwards' => true,
+            'alwayson_scripts' => [
+                'ControlNet' => [
+                    'args' => [[
+                        'input_image' => null,
+                        'module' => 'inpaint_only+lama',
+                        'model' => 'control_v11p_sd15_openpose [cab727d4]',
+                        'weight' => 1.0,
+                        'resize_mode' => 'Resize and Fill',
+                        'lowvram' => false,
+                        'processor_res' => 512,
+                        'threshold_a' => 64,
+                        'threshold_b' => 64,
+                        'guidance_start' => 0.0,
+                        'guidance_end' => 1.0,
+                        'pixel_perfect' => true,
+                        'control_mode' => 2 // 更偏向ControlNet
+                    ]]
+                ]
+            ]
         ]);
 
-        // -------- 发送请求到 SD API -------- //
         $apiUrl = "http://20.0.17.188:45001/sdapi/v1/img2img";
         $headers = ['Content-Type: application/json'];
 
@@ -87,13 +99,15 @@ class WorkOrder extends Api
         $error = curl_error($ch);
         curl_close($ch);
 
-        if ($error) {return json(['code' => 1, 'msg' => '请求失败:' . $error]);}
+        if ($error) {
+            return json(['code' => 1, 'msg' => '请求失败:' . $error]);
+        }
+
         $data = json_decode($response, true);
         if (!isset($data['images'][0])) {
             return json(['code' => 1, 'msg' => '接口未返回图像数据']);
         }
 
-        // -------- 保存生成图像 -------- //
         $resultImg = base64_decode($data['images'][0]);
         $saveDir = ROOT_PATH . 'public/uploads/img2img/';
         if (!is_dir($saveDir)) {
@@ -101,7 +115,7 @@ class WorkOrder extends Api
         }
 
         $originalBaseName = pathinfo($imgRelPath, PATHINFO_FILENAME);
-        $fileName = $originalBaseName . '-' . time() . '-1.png';
+        $fileName = $originalBaseName . '-' . time() . '-1024x1248.png';
         $savePath = $saveDir . $fileName;
         file_put_contents($savePath, $resultImg);
 
@@ -113,6 +127,91 @@ class WorkOrder extends Api
             ]
         ]);
     }
+//    /**
+//     * 图生图功能-单张图片本地测试使用
+//     * 接口地址: /sdapi/v1/img2img
+//     */
+//    public function imgtowimg()
+//    {
+//        $prompt = $this->request->param('prompt', '将图片不完整部分补充完整');
+//        $imgRelPath = 'uploads/operate/ai/Preview/arr/一朵盛开的白色牡丹花为主体采用厚涂技法花心和背景点缀金箔灰银.png';
+//        $imgPath = ROOT_PATH . 'public/' . $imgRelPath;
+//        //原图是否存在
+//        if (!file_exists($imgPath)) {
+//            return json(['code' => 1, 'msg' => '原图不存在:' . $imgRelPath]);
+//        }
+//
+//        // -------- 图像编码 -------- //
+//        $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,                    // CFG 强度
+//            'denoising_strength' => 0.2,        // 重绘强度
+//            'width' => 679,                      // 图像宽度
+//            'height' => 862,                     // 图像高度
+//            'resize_mode' => 1,                 // 保留原图比例并裁剪
+//            'inpaint_full_res' => true,         // 使用原图分辨率
+//            'inpaint_full_res_padding' => 64,   // 边缘补全像素
+//            'mask_blur' => 4,                   // 蒙版柔化
+//            'inpainting_fill' => 3,             // 自动填充内容(不是黑色)
+//            'sampler_name' => 'DPM++ 2M SDE',   // 采样器
+//            'scheduler' => 'Exponential',       // ✅ 调度类型(补充字段)
+//            'seed' => 3689437019,               // 固定种子(确保结果可复现)
+//            'init_images' => [$initImage],      // 原图 base64
+//            'override_settings' => [
+//                'sd_model_checkpoint' => 'AbyssOrangeMix2_sfw', // 模型名
+//                'sd_vae' => "Automatic",
+//                'CLIP_stop_at_last_layers' => 2
+//            ],
+//            'override_settings_restore_afterwards' => true
+//        ]);
+//
+//        // -------- 发送请求到 SD API -------- //
+//        $apiUrl = "http://20.0.17.188: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' => '接口未返回图像数据']);
+//        }
+//
+//        // -------- 保存生成图像 -------- //
+//        $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 . '-' . time() . '-1.png';
+//        $savePath = $saveDir . $fileName;
+//        file_put_contents($savePath, $resultImg);
+//
+//        return json([
+//            'code' => 0,
+//            'msg' => '图像生成成功',
+//            'data' => [
+//                'origin_url' => '/uploads/img2img/' . $fileName
+//            ]
+//        ]);
+//    }
 
 
     /**
@@ -248,55 +347,55 @@ class WorkOrder extends Api
      * 接口地址: /sdapi/v1/sd-models
      */
     public function sd_models() {
-        $url = "http://20.0.17.188:45001/sdapi/v1/sd-models";
-
-        // 初始化 cURL
-        $ch = curl_init();
-
-        // 设置请求参数
-        curl_setopt($ch, CURLOPT_URL, $url);
-        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
-        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
-        curl_setopt($ch, CURLOPT_HTTPHEADER, [
-            'Content-Type: application/json',
-            'Accept: application/json',
-        ]);
-
-        // 发送请求
-        $response = curl_exec($ch);
-
-        // 错误处理
-        if (curl_errno($ch)) {
-            curl_close($ch);
-            return json([
-                'code' => 1,
-                'msg'  => '请求失败: ' . curl_error($ch),
-                'data' => [],
-                'count' => 0
-            ]);
-        }
-
-        curl_close($ch);
-
-        // 解析 JSON 响应
-        $result = json_decode($response, true);
-
-        // 判断返回数据是否有效
-        if (!is_array($result)) {
-            return json([
-                'code' => 1,
-                'msg'  => '数据解析失败',
-                'data' => [],
-                'count' => 0
-            ]);
-        }
+        // $url = "http://20.0.17.188:45001/sdapi/v1/sd-models";
+
+        // // 初始化 cURL
+        // $ch = curl_init();
+
+        // // 设置请求参数
+        // curl_setopt($ch, CURLOPT_URL, $url);
+        // curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+        // curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+        // curl_setopt($ch, CURLOPT_HTTPHEADER, [
+        //     'Content-Type: application/json',
+        //     'Accept: application/json',
+        // ]);
+
+        // // 发送请求
+        // $response = curl_exec($ch);
+
+        // // 错误处理
+        // if (curl_errno($ch)) {
+        //     curl_close($ch);
+        //     return json([
+        //         'code' => 1,
+        //         'msg'  => '请求失败: ' . curl_error($ch),
+        //         'data' => [],
+        //         'count' => 0
+        //     ]);
+        // }
+
+        // curl_close($ch);
+
+        // // 解析 JSON 响应
+        // $result = json_decode($response, true);
+
+        // // 判断返回数据是否有效
+        // if (!is_array($result)) {
+        //     return json([
+        //         'code' => 1,
+        //         'msg'  => '数据解析失败',
+        //         'data' => [],
+        //         'count' => 0
+        //     ]);
+        // }
 
         // 正常返回
         return json([
             'code' => 0,
             'msg'  => '查询成功',
-            'data' => $result,
-            'count' => count($result)
+            'data' => '',
+            'count' => 2,
         ]);
     }
 

+ 0 - 103
application/job/ImageToImageJob.php

@@ -124,109 +124,6 @@ class ImageToImageJob{
         echo "ImageJob failed: " . json_encode($data);
     }
 
-//    public function ImageToImage($fileName, $outputDirRaw, $new_image_url,$img_name, $width, $height)
-//    {
-//        // 统一路径分隔符
-//        $rootPath = str_replace('\\', '/', ROOT_PATH);
-//
-//        // 输出目录,如:ROOT/public/uploads/operate/ai/dall-e/hua/
-//        $outputDir = rtrim($rootPath . 'public/' . $outputDirRaw, '/') . '/';
-//
-//        // 当前日期目录,如:2025-06-16/
-//        $dateDir = date('Y-m-d') . '/';
-//
-//        // 完整基本路径,如:ROOT/public/uploads/operate/ai/dall-e/hua/2025-06-16/
-//        $fullBaseDir = $outputDir . $dateDir;
-//
-//        // 只创建 img_679x862 目录
-//        $saveDir = $fullBaseDir . 'new_679x862/';
-//        if (!is_dir($saveDir)) {
-//            mkdir($saveDir, 0755, true);
-//        }
-//
-//        // 从数据库中查询原图记录
-//        $record = Db::name('text_to_image')
-//            ->where('old_image_url', 'like', "%{$fileName}")
-//            ->order('id desc')
-//            ->find();
-//
-//        if (!$record) {
-//            return json([
-//                'code' => 1,
-//                'msg' => '没有找到匹配的图像记录'
-//            ]);
-//        }
-//
-//        // 调用图生图 API
-//        $ai = new AIGatewayService();
-//        $res = $ai->imgtoimgGptApi('', $new_image_url);
-//
-//        // 检查返回结果
-//        if (!isset($res['code']) || $res['code'] !== 0) {
-//            return json([
-//                'code' => 1,
-//                'msg' => $res['msg'] ?? '图像生成失败'
-//            ]);
-//        }
-//
-//        // 保存图片路径
-//        $img_name = mb_substr(preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name), 0, 30);
-//        $filename = $img_name . '.png';
-//        $path = $saveDir . $filename;
-//
-//        // 解码图像 base64 数据
-//        $imgData = base64_decode($res['data']['url']);
-//
-//        // 解析图像内容
-//        try {
-//            $im = \imagecreatefromstring($imgData);
-//            if (!$im) {
-//                file_put_contents('/tmp/corrupted.png', $imgData);
-//                throw new \Exception("❌ 图像无法解析,写入 /tmp/corrupted.png");
-//            }
-//        } catch (\Throwable $e) {
-//            file_put_contents('/tmp/corrupted.png', $imgData);
-//            throw new \Exception("❌ 图像处理异常:" . $e->getMessage());
-//        }
-//
-//        // 裁剪
-//        $srcW = imagesx($im);
-//        $srcH = imagesy($im);
-//        $srcRatio = $srcW / $srcH;
-//        $dstRatio = $width / $height;
-//
-//        if ($srcRatio > $dstRatio) {
-//            $cropW = intval($srcH * $dstRatio);
-//            $cropH = $srcH;
-//            $srcX = intval(($srcW - $cropW) / 2);
-//            $srcY = 0;
-//        } else {
-//            $cropW = $srcW;
-//            $cropH = intval($srcW / $dstRatio);
-//            $srcX = 0;
-//            $srcY = intval(($srcH - $cropH) / 2);
-//        }
-//
-//        $dstImg = imagecreatetruecolor($width, $height);
-//        imagecopyresampled($dstImg, $im, 0, 0, $srcX, $srcY, $width, $height, $cropW, $cropH);
-//
-//        // 保存裁剪图
-//        imagepng($dstImg, $path);
-//        imagedestroy($im);
-//        imagedestroy($dstImg);
-//
-//        // 更新数据库记录
-//        Db::name('text_to_image')->where('id', $record['id'])->update([
-//            'imgtoimg_url' => str_replace($rootPath . 'public/', '', $path),
-//            'status_name' => '图生图',
-//            'error_msg' => '',
-//            'update_time' => date('Y-m-d H:i:s')
-//        ]);
-//
-//        return '成功';
-//    }
-
-
     public function ImageToImage($fileName, $outputDirRaw, $new_image_url, $width, $height)
     {
         // 统一路径分隔符

+ 6 - 6
application/job/ImageToSingleJob.php

@@ -163,12 +163,12 @@ class ImageToSingleJob{
             return json(['code' => 1, 'msg' => '保存图片失败']);
         }
 
-//        Db::name('text_to_image')->where('id', $record['id'])->update([
-//            'imgtoimg_url' => $outputDirRaw . '/' . $dateDir . 'high_definition/' . $finalFileName,
-//            'status_name' => '高清放大',
-//            'error_msg' => '',
-//            'update_time' => date('Y-m-d H:i:s')
-//        ]);
+        Db::name('text_to_image')->where('id', $record['id'])->update([
+            'imgtoimg_url' => $outputDirRaw . '/' . $dateDir . 'high_definition/' . $finalFileName,
+            'status_name' => '高清放大',
+            'error_msg' => '',
+            'update_time' => date('Y-m-d H:i:s')
+        ]);
 
         return '成功';
     }

+ 57 - 98
application/job/TextToImageJob.php

@@ -145,48 +145,31 @@ class TextToImageJob
      * 文生图处理函数
      * 描述:根据提示词调用图像生成接口,保存图像文件,并更新数据库
      */
-    public function textToImage($fileName, $outputDirRaw, $width, $height, $prompt, $img_name,$selectedOption)
+    public function textToImage($fileName, $outputDirRaw, $width, $height, $prompt, $img_name, $selectedOption)
     {
         $rootPath = str_replace('\\', '/', ROOT_PATH);
         $outputDir = rtrim($rootPath . 'public/' . $outputDirRaw, '/') . '/';
         $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);
-            }
+            if (!is_dir($dir)) mkdir($dir, 0755, true);
         }
 
-        // 查询数据库记录
+        // 获取图像记录
         $record = Db::name('text_to_image')
             ->where('old_image_url', 'like', "%{$fileName}")
             ->order('id desc')
             ->find();
 
-        if (!$record) {
-            return '没有找到匹配的图像记录';
-        }
-
-        // 写入 prompt 日志
-        $logDir = $rootPath . 'runtime/logs/';
-        if (!is_dir($logDir)) mkdir($logDir, 0755, true);
+        if (!$record) return '没有找到匹配的图像记录';
 
-        // 调用文生图模型接口生成图像
-        $startTime = microtime(true);
-
-        // 清理 prompt 的换行
+        // 过滤关键词
         $prompt = preg_replace('/[\r\n\t]+/', ' ', $prompt);
-
-        // 定义要跳过的关键词(可按需扩展)
-        $skipKeywords = ['几何', 'geometry', 'geometric'];
-        foreach ($skipKeywords as $keyword) {
-            // 判断提示词中是否包含关键词(不区分大小写)
+        foreach (['几何', 'geometry', 'geometric'] as $keyword) {
             if (stripos($prompt, $keyword) !== false) {
-                $skipId = $record['id'];
-                echo "🚫 跳过生成:包含关键词“{$keyword}”,记录 ID:{$skipId}\n";
-                Db::name('text_to_image')->where('id', $skipId)->update([
+                Db::name('text_to_image')->where('id', $record['id'])->update([
                     'status' => 3,
                     'error_msg' => "包含关键词".$keyword,
                     'update_time' => date('Y-m-d H:i:s')
@@ -195,95 +178,70 @@ class TextToImageJob
             }
         }
 
-        //文生图调用
-        $startTime = microtime(true);
-
+        // AI 图像生成调用
         $ai = new AIGatewayService();
-        $dalle1024 = $ai->callDalleApi($prompt,$selectedOption);
-
-        $endTime = microtime(true);
-        $executionTime = $endTime - $startTime;
-        echo "✅ API 调用耗时: " . round($executionTime, 3) . " 秒\n";
-
-        // 错误检查
-        if (isset($dalle1024['error'])) {
-            throw new \Exception("❌ 图像生成接口错误:" . ($dalle1024['error']['message'] ?? '未知错误'));
+        $response = $ai->callDalleApi($prompt, $selectedOption);
+        if (isset($response['error'])) {
+            throw new \Exception("❌ 图像生成失败:" . $response['error']['message']);
         }
 
-        // 提取 url 图像
-//        $base64Image = $dalle1024['data'][0]['url'] ?? null;
-
-        // 提取 base64 图像
-        $base64Image = $dalle1024['data'][0]['b64_json'] ?? null;
-
-        if (!$base64Image || strlen($base64Image) < 1000) {
-            file_put_contents('/tmp/empty_image_base64.txt', json_encode($dalle1024, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
-            throw new \Exception("❌ 图像内容为空或异常,详情写入 /tmp/empty_image_base64.txt");
+        // 支持 URL 格式(为主)和 base64
+        $imgData = null;
+        if (isset($response['data'][0]['url'])) {
+            $imgData = @file_get_contents($response['data'][0]['url']);
+        } elseif (isset($response['data'][0]['b64_json'])) {
+            $imgData = base64_decode($response['data'][0]['b64_json']);
         }
 
-        // 解码图片
-        $imgData = base64_decode($base64Image);
-
-        // 判断是否为图像
-        $finfo = finfo_open(FILEINFO_MIME_TYPE);
-        $mimeType = finfo_buffer($finfo, $imgData);
-        finfo_close($finfo);
+        if (!$imgData || strlen($imgData) < 1000) {
+            throw new \Exception("❌ 图像内容为空或异常!");
+        }
 
-        // 保存图片路径
+        // 保存文件路径定义
         $img_name = mb_substr(preg_replace('/[^\x{4e00}-\x{9fa5}A-Za-z0-9_\- ]/u', '', $img_name), 0, 30);
         $filename = $img_name . '.png';
+        $path512 = $fullBaseDir . '1024x1024/' . $filename;
+        $pathCustom = $fullBaseDir . "{$width}x{$height}/" . $filename;
 
-        $saveDir1024 = $fullBaseDir . '1024x1024/';
-        $saveDirCustom = $fullBaseDir . "{$width}x{$height}/";
-        @mkdir($saveDir1024, 0755, true);
-        @mkdir($saveDirCustom, 0755, true);
-
-        $path1024 = $saveDir1024 . $filename;
-        $pathCustom = $saveDirCustom . $filename;
+        // 保存原图
+        file_put_contents($path512, $imgData);
 
-        file_put_contents($path1024, $imgData);
-
-        // 解析图像内容
+        // ➤ 是否执行裁剪(如不想裁剪,可注释以下 try-catch 整块)
         try {
-            $im = \imagecreatefromstring($imgData);
-            if (!$im) {
-                file_put_contents('/tmp/corrupted.png', $imgData);
-                throw new \Exception("❌ 图像无法解析,写入 /tmp/corrupted.png");
+            $im = imagecreatefromstring($imgData);
+            if (!$im) throw new \Exception("图像无法解析");
+
+            $srcW = imagesx($im);
+            $srcH = imagesy($im);
+            $srcRatio = $srcW / $srcH;
+            $dstRatio = $width / $height;
+
+            if ($srcRatio > $dstRatio) {
+                $cropW = intval($srcH * $dstRatio);
+                $cropH = $srcH;
+                $srcX = intval(($srcW - $cropW) / 2);
+                $srcY = 0;
+            } else {
+                $cropW = $srcW;
+                $cropH = intval($srcW / $dstRatio);
+                $srcX = 0;
+                $srcY = intval(($srcH - $cropH) / 2);
             }
-        } catch (\Throwable $e) {
-            file_put_contents('/tmp/corrupted.png', $imgData);
-            throw new \Exception("❌ 图像处理异常:" . $e->getMessage());
-        }
 
-        // 裁剪
-        $srcW = imagesx($im);
-        $srcH = imagesy($im);
-        $srcRatio = $srcW / $srcH;
-        $dstRatio = $width / $height;
-
-        if ($srcRatio > $dstRatio) {
-            $cropW = intval($srcH * $dstRatio);
-            $cropH = $srcH;
-            $srcX = intval(($srcW - $cropW) / 2);
-            $srcY = 0;
-        } else {
-            $cropW = $srcW;
-            $cropH = intval($srcW / $dstRatio);
-            $srcX = 0;
-            $srcY = intval(($srcH - $cropH) / 2);
+            $dstImg = imagecreatetruecolor($width, $height);
+            imagecopyresampled($dstImg, $im, 0, 0, $srcX, $srcY, $width, $height, $cropW, $cropH);
+            imagepng($dstImg, $pathCustom);
+            imagedestroy($im);
+            imagedestroy($dstImg);
+        } catch (\Throwable $e) {
+            file_put_contents('/tmp/crop_error.png', $imgData);
+            throw new \Exception("图像裁剪失败:" . $e->getMessage());
         }
 
-        $dstImg = imagecreatetruecolor($width, $height);
-        imagecopyresampled($dstImg, $im, 0, 0, $srcX, $srcY, $width, $height, $cropW, $cropH);
-
-        // 保存裁剪图
-        imagepng($dstImg, $pathCustom);
-        imagedestroy($im);
-        imagedestroy($dstImg);
-
-        // 写入数据库
+        // 数据库更新
         Db::name('text_to_image')->where('id', $record['id'])->update([
-            'new_image_url' => str_replace($rootPath . 'public/', '', $path1024),
+            'new_image_url' => str_replace($rootPath . 'public/', '', $path512),
+            // 注释以下一行则不保存裁剪路径(适配你的配置)
             'custom_image_url' => str_replace($rootPath . 'public/', '', $pathCustom),
             'img_name' => $img_name,
             'model' => $selectedOption,
@@ -295,6 +253,7 @@ class TextToImageJob
             'error_msg' => '',
             'update_time' => date('Y-m-d H:i:s')
         ]);
+
         return "成功";
     }
 }

+ 24 - 7
application/service/AIGatewayService.php

@@ -28,8 +28,12 @@ class AIGatewayService{
         ],
         //文生图-dall-e-3
         'txttoimg' => [
-            'api_key' => 'sk-MB6SR8qNaTjO80U7HJl4ztivX3zQKPgKVka9oyfVSXIkHSYZ',
-            'api_url' => 'https://chatapi.onechats.top/v1/images/generations'
+            // 'api_key' => 'sk-MB6SR8qNaTjO80U7HJl4ztivX3zQKPgKVka9oyfVSXIkHSYZ',
+            'api_key' => 'sk-iURfrAgzAjhZ4PpPLwzmWIAhM7zKfrkwDvyxk4RVBQ4ouJNK',
+
+
+            'api_url' => 'https://chatapi.onechats.ai/v1/images/generations'
+
         ]
     ];
 
@@ -42,7 +46,7 @@ class AIGatewayService{
     {
         //方式一
         $data = [
-            "model" => "gemini-2.5-flash-preview",
+            "model" => "gemini-2.5-flash-lite-preview-06-17",
             "messages" => [[
                 "role" => "user",
                 "content" => [
@@ -148,20 +152,33 @@ class AIGatewayService{
                 'prompt'  => $prompt,
                 'model'   => $selectedOption,
                 'n'       => 1,
+                // 'size'    => '512x512',
+                'size'    => '1024x1024',
+                'quality' => 'hd',
+                'style'   => 'vivid',
+                'response_format' => 'url',
+            ];
+        } else if ($selectedOption === 'black-forest-labs/FLUX.1-kontext-pro') {
+            $data = [
+                'prompt'  => $prompt,
+                'model'   => $selectedOption,
+                'n'       => 1,
+                // 'size'    => '512x512',
                 'size'    => '1024x1024',
                 'quality' => 'hd',
                 'style'   => 'vivid',
-                'response_format' => 'b64_json',
+                'response_format' => 'url',
             ];
-        } else {
+        }else{
             $data = [
                 'prompt'  => $prompt,
                 'model'   => $selectedOption,
                 'n'       => 1,
+                // 'size'    => '512x512',
                 'size'    => '1024x1024',
                 'quality' => 'hd',
                 'style'   => 'vivid',
-                'response_format' => 'b64_json',
+                'response_format' => 'url',
             ];
         }
         return $this->callApi($this->config['txttoimg']['api_url'],$this->config['txttoimg']['api_key'],$data);
@@ -193,7 +210,7 @@ class AIGatewayService{
             'height' => 862,
             'resize_mode' => 2,
             'sampler_name' => 'DPM++ 2M SDE Heun',
-            'seed' => 611075477, // 使用-1表示随机种子
+            'seed' => -1, // 使用-1表示随机种子
             'inpaint_full_res' => true,
             'inpainting_fill' => 1,
             'override_settings' => [