ソースを参照

Dev comfy Queue, generate.db

tsurumure 1 ヶ月 前
コミット
9d22f519b6

+ 23 - 0
db/crt_generate_image.sql

@@ -0,0 +1,23 @@
+/**
+Source Server Version: 8.0.31
+Source Database: backendsys
+Date: 2025/06/03 10:09:22
+*/
+
+DROP TABLE IF EXISTS `crt_generate_image`;
+CREATE TABLE `crt_generate_image` (
+    PRIMARY KEY (`id`),
+    `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
+    `user_id` BIGINT COMMENT '用户ID',
+    `name` VARCHAR(255) NOT NULL COMMENT '图片名称',
+    `url_origin` VARCHAR(2000) NOT NULL COMMENT '原图',
+    `url` VARCHAR(2000) COMMENT '转存图',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    INDEX `idx_user_id` (`user_id`)
+) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短剧创作-生成图片记录表';
+
+# INSERT INTO crt_generate_image(id, name, lora_figure_name, lora_figure_path) VALUES
+#     (1, '柳王妃', 'liuwangfei-0012', '/etc/ComfyUI/custom_nodes/xxx/liuwangfei-0012.safetensors'),
+#     (2, '陆司明', 'lusiming-0001', '/etc/ComfyUI/custom_nodes/xxx/lusiming-0001.safetensors')
+# ;

+ 718 - 0
src/main/java/com/backendsys/modules/crt/comfyui-api.md

@@ -0,0 +1,718 @@
+---
+title: comfyui-api v1.0.0
+language_tabs:
+  - shell: Shell
+  - http: HTTP
+  - javascript: JavaScript
+  - ruby: Ruby
+  - python: Python
+  - php: PHP
+  - java: Java
+  - go: Go
+toc_footers: []
+includes: []
+search: true
+code_clipboard: true
+highlight_theme: darkula
+headingLevel: 2
+generator: "@tarslib/widdershins v4.0.17"
+
+---
+
+# comfyui-api
+
+> v1.0.0
+
+Base URLs:
+
+# Authentication
+
+# comfyui-api文档
+
+## GET /history
+
+GET /history
+
+获取所有历史任务数据
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|prompt_id|query|string| 否 |8b918008-751f-414c-9575-7174e841ceac|
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /embeddings
+
+GET /embeddings
+
+获取一个列表
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /history/{prompt_id}
+
+GET /history/8b918008-751f-414c-9575-7174e841ceac
+
+获取历史任务数据(根据任务id获取历史数据)
+
+> 返回示例
+
+> 成功
+
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /extensions
+
+GET /extensions
+
+获取扩展节点文件列表
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## POST /upload/image
+
+POST /upload/image
+
+上传图片接口
+
+> Body 请求参数
+
+```yaml
+image: string
+
+```
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|body|body|object| 否 |none|
+|» image|body|string(binary)| 是 |图片将以二进制格式发送到服务器|
+
+> 返回示例
+
+> 成功
+
+```json
+{
+  "name": "aaa (7).webp",
+  "subfolder": "",
+  "type": "input"
+}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## POST /upload/mask
+
+POST /upload/mask
+
+上传蒙版图片接口,一般用于局部重绘
+
+> Body 请求参数
+
+```yaml
+image: string
+type: input
+subfolder: clipspace
+original_ref: "{“filename”:”下载.png”,”type”:”input”,”subfolder”:”clipspace”}"
+
+```
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|body|body|object| 否 |none|
+|» image|body|string(binary)| 是 |图片将以二进制格式发送到服务器|
+|» type|body|string| 否 |上传图片的目标文件夹|
+|» subfolder|body|string| 否 |上传图片的目标子文件夹|
+|» original_ref|body|string| 是 |无|
+
+> 返回示例
+
+> 成功
+
+```json
+{
+  "name": "下载.png",
+  "subfolder": "clipspace",
+  "type": "input"
+}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /view
+
+GET /view
+
+图片的在线预览接口(上传图像,生图图像,蒙蔽图像,均通过该接口预览)
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|filename|query|string| 是 |图片名称|
+|type|query|string| 否 |图片存放位置的文件夹(input为长传图片,output为生成的图片)|
+|subfolder|query|string| 否 |子文件夹(没有可不填)|
+|preview|query|string| 否 |预览|
+|channel|query|string| 否 |无|
+
+> 返回示例
+
+> 成功
+
+```json
+"<img src=\"blob:file:///88efa21f-8f36-4540-aa34-436aa404ce3f\" alt=\"runapi直接显示图片\" />"
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## POST /view_metadata/{folder_name}
+
+POST /view_metadata/{folder_name}
+
+无
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|folder_name|path|string| 是 |none|
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /system_stats
+
+GET /system_stats
+
+系统统计信息接口
+
+> 返回示例
+
+> 成功
+
+```json
+{
+  "system": {
+    "os": "posix",
+    "python_version": "3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0]",
+    "embedded_python": false
+  },
+  "devices": [
+    {
+      "name": "cuda:0 NVIDIA GeForce RTX 2080 Ti : cudaMallocAsync",
+      "type": "cuda",
+      "index": 0,
+      "vram_total": 23266590720,
+      "vram_free": 13600655680,
+      "torch_vram_total": 6811549696,
+      "torch_vram_free": 26970432
+    }
+  ]
+}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /prompt
+
+GET /prompt
+
+获取服务器当前剩余任务列队的数量
+
+> 返回示例
+
+> 成功
+
+```json
+{
+  "exec_info": {
+    "queue_remaining": 1
+  }
+}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## POST /prompt
+
+POST /prompt
+
+绘图任务的下发接口,此接口只做任务下发,返回任务ID信息。
+此接口只做任务下发,返回任务ID信息。
+
+> Body 请求参数
+
+```json
+{
+  "client_id": "533ef3a3-39c0-4e39-9ced-37d290f371f8",
+  "prompt": {
+    "3": {
+      "inputs": {
+        "seed": 764714814161513,
+        "steps": 26,
+        "cfg": 5,
+        "sampler_name": "dpmpp_3m_sde_gpu",
+        "scheduler": "karras",
+        "denoise": 1,
+        "model": [
+          "40",
+          0
+        ],
+        "positive": [
+          "49",
+          0
+        ],
+        "negative": [
+          "6",
+          0
+        ],
+        "latent_image": [
+          "5",
+          0
+        ]
+      },
+      "class_type": "KSampler"
+    },
+    "5": {
+      "inputs": {
+        "width": 1024,
+        "height": 768,
+        "batch_size": 1
+      },
+      "class_type": "EmptyLatentImage"
+    },
+    "6": {
+      "inputs": {
+        "text": "",
+        "clip": [
+          "40",
+          1
+        ]
+      },
+      "class_type": "CLIPTextEncode"
+    },
+    "8": {
+      "inputs": {
+        "samples": [
+          "3",
+          0
+        ],
+        "vae": [
+          "40",
+          2
+        ]
+      },
+      "class_type": "VAEDecode"
+    },
+    "9": {
+      "inputs": {
+        "filename_prefix": "ComfyUI",
+        "images": [
+          "8",
+          0
+        ]
+      },
+      "class_type": "SaveImage"
+    },
+    "13": {
+      "inputs": {
+        "clip_vision": [
+          "39",
+          0
+        ],
+        "image": [
+          "34",
+          0
+        ]
+      },
+      "class_type": "CLIPVisionEncode"
+    },
+    "19": {
+      "inputs": {
+        "strength": 1,
+        "noise_augmentation": 0,
+        "conditioning": [
+          "42",
+          0
+        ],
+        "clip_vision_output": [
+          "13",
+          0
+        ]
+      },
+      "class_type": "unCLIPConditioning"
+    },
+    "34": {
+      "inputs": {
+        "image": "clipspace/clipspace-mask-1645940.7000000002.png [input]",
+        "choose file to upload": "image"
+      },
+      "class_type": "LoadImage"
+    },
+    "36": {
+      "inputs": {
+        "clip_vision": [
+          "39",
+          0
+        ],
+        "image": [
+          "38",
+          0
+        ]
+      },
+      "class_type": "CLIPVisionEncode"
+    },
+    "37": {
+      "inputs": {
+        "strength": 0.75,
+        "noise_augmentation": 0,
+        "conditioning": [
+          "19",
+          0
+        ],
+        "clip_vision_output": [
+          "36",
+          0
+        ]
+      },
+      "class_type": "unCLIPConditioning"
+    },
+    "38": {
+      "inputs": {
+        "image": "beijing1 (2).webp",
+        "choose file to upload": "image"
+      },
+      "class_type": "LoadImage"
+    },
+    "39": {
+      "inputs": {
+        "clip_name": "clip_vision_g.safetensors"
+      },
+      "class_type": "CLIPVisionLoader"
+    },
+    "40": {
+      "inputs": {
+        "ckpt_name": "sd_xl_base_1.0.safetensors"
+      },
+      "class_type": "CheckpointLoaderSimple"
+    },
+    "42": {
+      "inputs": {
+        "conditioning": [
+          "6",
+          0
+        ]
+      },
+      "class_type": "ConditioningZeroOut"
+    },
+    "43": {
+      "inputs": {
+        "safe": "enable"
+      },
+      "class_type": "HEDPreprocessor"
+    },
+    "44": {
+      "inputs": {
+        "safe": "enable",
+        "image": [
+          "34",
+          0
+        ]
+      },
+      "class_type": "HEDPreprocessor"
+    },
+    "45": {
+      "inputs": {
+        "images": [
+          "44",
+          0
+        ]
+      },
+      "class_type": "PreviewImage"
+    },
+    "46": {
+      "inputs": {
+        "control_net_name": "control-lora-depth-rank256.safetensors"
+      },
+      "class_type": "ControlNetLoader"
+    },
+    "47": {
+      "inputs": {
+        "image": [
+          "34",
+          0
+        ]
+      },
+      "class_type": "ScribblePreprocessor"
+    },
+    "48": {
+      "inputs": {
+        "images": [
+          "47",
+          0
+        ]
+      },
+      "class_type": "PreviewImage"
+    },
+    "49": {
+      "inputs": {
+        "strength": 0.5,
+        "conditioning": [
+          "37",
+          0
+        ],
+        "control_net": [
+          "46",
+          0
+        ],
+        "image": [
+          "47",
+          0
+        ]
+      },
+      "class_type": "ControlNetApply"
+    }
+  }
+}
+```
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|body|body|object| 否 |none|
+
+> 返回示例
+
+> 成功
+
+```json
+{
+  "prompt_id": "352c1fc4-7382-4c4a-965f-583c4b126a1b",
+  "number": 38,
+  "node_errors": {}
+}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /object_info
+
+GET /object_info
+
+获取系统中所有组件以及可用参数
+
+> 返回示例
+
+> 成功
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /object_info/{node_class}
+
+GET /object_info/KSampler
+
+根据组件名称获取系统中组件参数
+
+> 返回示例
+
+> 成功
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /queue
+
+GET /queue
+
+获取详细任务队列信息,正在运行的以及挂起的
+
+> 返回示例
+
+> 成功
+
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## POST /queue
+
+POST /queue
+
+删除列队/无返回信息则为成功
+
+> Body 请求参数
+
+```json
+{
+  "delete": "string"
+}
+```
+
+### 请求参数
+
+|名称|位置|类型|必选|说明|
+|---|---|---|---|---|
+|body|body|object| 否 |none|
+|» delete|body|string| 是 |包含任务id的列表|
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+## GET /interrupt
+
+GET /interrupt
+
+取消当前任务/不需任何参数
+
+> 返回示例
+
+> 200 Response
+
+```json
+{}
+```
+
+### 返回结果
+
+|状态码|状态码含义|说明|数据模型|
+|---|---|---|---|
+|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|成功|Inline|
+
+### 返回数据结构
+
+# 数据模型
+

+ 8 - 0
src/main/java/com/backendsys/modules/crt/controller/CrtGenerateController.java

@@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RestController;
@@ -21,6 +22,13 @@ public class CrtGenerateController {
     @Autowired
     private CrtGenerateService crtGenerateService;
 
+    @PreAuthorize("@sr.hasPermission('36.3')")
+    @Operation(summary = "查询任务队列")
+    @GetMapping("/api/crt/generate/getQueue")
+    public Result getQueue() {
+        return Result.success().put("data", crtGenerateService.getQueue());
+    }
+
     // 生成图片
     @PreAuthorize("@sr.hasPermission('36.3')")
     @Operation(summary = "生成图片")

+ 9 - 0
src/main/java/com/backendsys/modules/crt/dao/CrtGenerateImageDao.java

@@ -0,0 +1,9 @@
+package com.backendsys.modules.crt.dao;
+
+import com.backendsys.modules.crt.entity.CrtGenerateImage;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface CrtGenerateImageDao extends BaseMapper<CrtGenerateImage> {
+}

+ 0 - 4
src/main/java/com/backendsys/modules/crt/entity/CrtDramaProjectStoryboard.java

@@ -36,10 +36,6 @@ public class CrtDramaProjectStoryboard {
     @NotNull(message = "分镜ID不能为空", groups = { Update.class, GenerateImage.class })
     private Long drama_project_storyboard_id;
 
-    @TableField(exist = false)
-    @NotEmpty(message = "生成任务ID不能为空", groups = { GenerateImage.class })
-    private String client_id;
-
     private Long user_id;
 
     @NotNull(message = "项目ID不能为空", groups = { Create.class, StoryboardDetail.class, Clear.class, Delete.class  })

+ 1 - 1
src/main/java/com/backendsys/modules/crt/entity/CrtDramaTask.java

@@ -12,7 +12,7 @@ public class CrtDramaTask {
     @TableId(type = IdType.AUTO)
     private Long id;
     private Long user_id;                       // 用户ID
-    private Long drama_project_id;          // 项目ID
+    private Long drama_project_id;              // 项目ID
     private String task_id;                     // 任务ID
     private Integer task_type;                  // 任务类型 (1:图像, 2:视频)
     private Integer task_status;                // 任务状态 (-1:未开始, 1:进行中, 2:成功, 3:失败)

+ 27 - 0
src/main/java/com/backendsys/modules/crt/entity/CrtGenerateImage.java

@@ -0,0 +1,27 @@
+package com.backendsys.modules.crt.entity;
+
+import com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeAdapter;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.google.gson.annotations.JsonAdapter;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("crt_generate_image")
+public class CrtGenerateImage {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long user_id;                       // 用户ID
+    private String name;                        // 图片名称
+    private String url_origin;                  // 原图
+    private String url;                          // 转存图
+
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime create_time;
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime update_time;
+}

+ 3 - 0
src/main/java/com/backendsys/modules/crt/service/CrtGenerateService.java

@@ -6,6 +6,9 @@ import java.util.Map;
 
 public interface CrtGenerateService {
 
+    // [ComfyUI] 查询任务队列
+    Map<String, Object> getQueue();
+
     // 短剧创作-生成图片
     Map<String, Object> generateImage(CrtDramaProjectStoryboard crtDramaProjectStoryboard);
 

+ 66 - 17
src/main/java/com/backendsys/modules/crt/service/impl/CrtGenerateServiceImpl.java

@@ -9,6 +9,7 @@ import com.backendsys.modules.crt.dao.CrtDramaProjectStoryboardDao;
 import com.backendsys.modules.crt.entity.CrtDramaProjectStoryboard;
 import com.backendsys.modules.crt.service.CrtGenerateService;
 import com.backendsys.modules.sdk.comfyui.entity.CFPromptResponse;
+import com.backendsys.modules.sdk.comfyui.entity.CFQueue;
 import com.backendsys.modules.sdk.comfyui.service.ComfyUIService;
 import com.backendsys.modules.sdk.comfyui.service.ComfyUISocketService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -31,6 +32,21 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
     @Autowired
     private CrtDramaProjectStoryboardDao crtDramaProjectStoryboardDao;
 
+    /**
+     * [ComfyUI] 查询任务队列
+     */
+    @Override
+    public Map<String, Object> getQueue() {
+        // [ComfyUI] 执行任务
+        Mono<CFQueue> cfQueueMono = comfyUIService.getQueue();
+        CFQueue response = cfQueueMono.block();
+        System.out.println("结果: " + response);
+
+        Map<String, Object> resp = new LinkedHashMap<>();
+        resp.put("response", response);
+        return resp;
+    }
+
     /**
      * 短剧创作-生成图片
      */
@@ -43,16 +59,13 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
         if (detail == null) throw new CustException("分镜不存在");
 
         // -- 前端生成的UUID ---------------------------------------------
-//        String client_id = Convert.toStr(UUID.randomUUID());
-        String client_id = crtDramaProjectStoryboard.getClient_id();
+        String client_id = Convert.toStr(UUID.randomUUID());
 
         // -- [ComfyUI] 创建 WebSocket 监听连接 ---------------------------
-        comfyUISocketService.connectToSse(client_id, "ws://43.128.1.201:8007/ws").subscribe();
+        comfyUISocketService.connectToSse(client_id, "ws://43.128.1.201:8001/ws").subscribe();
 
         // -- [ComfyUI] 执行任务 -----------------------------------------
 
-
-
         // [Demo-基础生图]
         String prompt = "{" +
                     "\"3\": {" +
@@ -66,41 +79,77 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
                             "\"model\": [\"4\", 0]," +
                             "\"positive\": [\"6\", 0]," +
                             "\"negative\": [\"7\", 0]," +
-                            "\"latent_image\": [\"5\", 0]," +
-                            "\"class_type\": \"KSampler\"," +
-                            "\"_meta\": {" +
-                                "\"title\": \"K采样器\"" +
-                            "}," +
-                        "}" +
+                            "\"latent_image\": [\"5\", 0]" +
+                        "}," +
+                        "\"class_type\": \"KSampler\"," +
+                        "\"_meta\": { \"title\": \"K采样器\" }" +
                     "}," +
                     "\"4\": {" +
+                        "\"inputs\": {" +
+                            "\"ckpt_name\": \"v1-5-pruned-emaonly-fp16.safetensors\"" +
+                        "}," +
+                        "\"class_type\": \"CheckpointLoaderSimple\"," +
+                        "\"_meta\": { \"title\": \"Checkpoint加载器(简易)\" }" +
                     "}," +
                     "\"5\": {" +
+                        "\"inputs\": {" +
+                            "\"width\": 512, \"height\": 512, \"batch_size\": 1" +
+                        "}," +
+                        "\"class_type\": \"EmptyLatentImage\"," +
+                        "\"_meta\": { \"title\": \"空Latent图像\" }" +
                     "}," +
                     "\"6\": {" +
+                        "\"inputs\": {" +
+                            "\"text\": \"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,\"," +
+                            "\"speak_and_recognation\": {" +
+                                "\"__value__\": [false, true]" +
+                            "}," +
+                            "\"clip\": [\"4\", 1]" +
+                        "}," +
+                        "\"class_type\": \"CLIPTextEncode\"," +
+                        "\"_meta\": { \"title\": \"CLIP文本编码\" }" +
                     "}," +
                     "\"7\": {" +
+                        "\"inputs\": {" +
+                            "\"text\": \"text, watermark\"," +
+                            "\"speak_and_recognation\": {" +
+                                "\"__value__\": [false, true]" +
+                            "}," +
+                            "\"clip\": [\"4\", 1]" +
+                        "}," +
+                        "\"class_type\": \"CLIPTextEncode\"," +
+                        "\"_meta\": { \"title\": \"CLIP文本编码\" }" +
                     "}," +
                     "\"8\": {" +
+                        "\"inputs\": {" +
+                            "\"samples\": [\"3\", 0]," +
+                            "\"vae\": [\"4\", 2]," +
+                        "}," +
+                        "\"class_type\": \"VAEDecode\"," +
+                        "\"_meta\": { \"title\": \"VAE解码\" }" +
                     "}," +
                     "\"9\": {" +
+                        "\"inputs\": {" +
+                            "\"filename_prefix\": \"ComfyUI\"," +
+                            "\"images\": [\"8\", 0]" +
+                        "}," +
+                        "\"class_type\": \"SaveImage\"," +
+                        "\"_meta\": { \"title\": \"保存图像\" }" +
                     "}," +
                 "}";
 
-
-
-
         JSONObject prompt_object = JSONUtil.parseObj(prompt);
 
         System.out.println("prompt_object: " + prompt_object);
 
-        Mono<CFPromptResponse> cfPromptResponse = comfyUIService.prompt(client_id, prompt_object);
-        CFPromptResponse response = cfPromptResponse.block();
+        // [ComfyUI] 执行任务
+        Mono<CFPromptResponse> cfPromptResponseMono = comfyUIService.prompt(client_id, prompt_object);
+        CFPromptResponse response = cfPromptResponseMono.block();
+        response.setClient_id(client_id);
         System.out.println("结果: " + response);
 
         Map<String, Object> resp = new LinkedHashMap<>();
         resp.put("drama_project_storyboard_id", drama_project_storyboard_id);
-        resp.put("client_id", client_id);
         resp.put("response", response);
         return resp;
     }

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/comfyui/controller/ComfyUIDemoController.java

@@ -33,7 +33,7 @@ public class ComfyUIDemoController {
     @Anonymous
     @PostMapping("/api/comfyui/ws/connect")
     public String connect(String clientId) {
-        comfyUISocketService.connect(clientId, "ws://43.128.1.201:8007/ws").subscribe();
+        comfyUISocketService.connect(clientId, "ws://43.128.1.201:8001/ws").subscribe();
         return "Connection initiated for: " + clientId;
     }
 

+ 1 - 1
src/main/java/com/backendsys/modules/sdk/comfyui/entity/CFPromptResponse.java

@@ -4,7 +4,7 @@ import lombok.Data;
 
 @Data
 public class CFPromptResponse {
-
+    private String client_id;       // 任务ID
     private String prompt_id;       // 任务ID
     private Integer number;         // 当前任务序号,可用于后续获取需要等待任务数的计算
     private Object node_errors;     // 错误信息

+ 10 - 0
src/main/java/com/backendsys/modules/sdk/comfyui/entity/CFQueue.java

@@ -0,0 +1,10 @@
+package com.backendsys.modules.sdk.comfyui.entity;
+
+import cn.hutool.json.JSONArray;
+import lombok.Data;
+
+@Data
+public class CFQueue {
+    private JSONArray queue_running;
+    private JSONArray queue_pending;
+}

+ 4 - 0
src/main/java/com/backendsys/modules/sdk/comfyui/service/ComfyUIService.java

@@ -2,10 +2,14 @@ package com.backendsys.modules.sdk.comfyui.service;
 
 import cn.hutool.json.JSONObject;
 import com.backendsys.modules.sdk.comfyui.entity.CFPromptResponse;
+import com.backendsys.modules.sdk.comfyui.entity.CFQueue;
 import reactor.core.publisher.Mono;
 
 public interface ComfyUIService {
 
+    // [ComfyUI] 查询任务队列
+    Mono<CFQueue> getQueue();
+
     // [ComfyUI] 执行任务
     Mono<CFPromptResponse> prompt(String client_id, JSONObject prompt);
 

+ 22 - 16
src/main/java/com/backendsys/modules/sdk/comfyui/service/impl/ComfyUIServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.json.JSONObject;
 import com.backendsys.modules.common.Filter.WebClientFilter;
 import com.backendsys.modules.sdk.comfyui.entity.CFPromptRequest;
 import com.backendsys.modules.sdk.comfyui.entity.CFPromptResponse;
+import com.backendsys.modules.sdk.comfyui.entity.CFQueue;
 import com.backendsys.modules.sdk.comfyui.service.ComfyUIService;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpHeaders;
@@ -16,21 +17,23 @@ import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.util.UriComponentsBuilder;
 import reactor.core.publisher.Mono;
 
+import java.util.Map;
 import java.util.UUID;
 import java.util.function.Consumer;
 
 @Service
 public class ComfyUIServiceImpl implements ComfyUIService {
 
-
+    @Value("${comfyui.url}")
+    private String COMFYUI_URL;
     @Value("${comfyui.token}")
     private String COMFYUI_TOKEN;
-    private final String BASE_URL = "http://43.128.1.201:8007";
 
     private WebClient webClient;
     public WebClient getWebClient() {
         if (webClient == null) {
-            webClient = WebClient.builder().baseUrl(BASE_URL).filter(WebClientFilter.logFilter).build();
+//            webClient = WebClient.builder().baseUrl(COMFYUI_URL + ":8001").filter(WebClientFilter.logFilter).build();
+            webClient = WebClient.builder().filter(WebClientFilter.logFilter).build();
         }
         return webClient;
     }
@@ -45,8 +48,18 @@ public class ComfyUIServiceImpl implements ComfyUIService {
         return httpHeaders;
     }
 
-    public CFPromptResponse mapErrorResponse(CFPromptResponse response) {
-        return response;
+
+    /**
+     * [ComfyUI] 查询任务队列
+     */
+    public Mono<CFQueue> getQueue() {
+        String url = COMFYUI_URL + "/queue";
+        WebClient webClient = getWebClient();
+        return webClient.get()
+                .uri(url)
+                .headers(getHttpHeaders())
+                .accept(MediaType.APPLICATION_JSON)
+                .exchangeToMono(response -> response.bodyToMono(CFQueue.class));
     }
 
     /**
@@ -59,23 +72,16 @@ public class ComfyUIServiceImpl implements ComfyUIService {
         bodyValue.setClient_id(client_id);
         bodyValue.setPrompt(prompt);
 
-//        String url = "http://43.128.1.201:8007/prompt?token=" + COMFYUI_TOKEN;
-        String url = "http://43.128.1.201:8007/prompt";
-        String uri = UriComponentsBuilder.fromUriString(url).toUriString();
+        String url = COMFYUI_URL + ":8001/prompt";
 
         WebClient webClient = getWebClient();
         return webClient.post()
-                .uri(uri)
+                .uri(url)
                 .headers(getHttpHeaders())
                 .accept(MediaType.APPLICATION_JSON)
                 .bodyValue(bodyValue)
-                .exchangeToMono(response -> {
-                    if (response.statusCode().is2xxSuccessful()) {
-                        return response.bodyToMono(CFPromptResponse.class);
-                    } else {
-                        return response.bodyToMono(CFPromptResponse.class).map(e -> mapErrorResponse(e));
-                    }
-                }).onErrorResume(e -> {
+                .exchangeToMono(response -> response.bodyToMono(CFPromptResponse.class))
+                .onErrorResume(e -> {
                     // 捕获所有异常(包括上面抛出的 RuntimeException)
                     CFPromptResponse response = new CFPromptResponse();
                     response.setNode_errors(e.getMessage());

+ 19 - 4
src/main/java/com/backendsys/modules/sdk/comfyui/service/impl/ComfyUISocketServiceImpl.java

@@ -1,5 +1,8 @@
 package com.backendsys.modules.sdk.comfyui.service.impl;
 
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.backendsys.modules.ai.chat.entity.ChatSseMessage;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.sdk.comfyui.service.ComfyUISocketService;
@@ -100,15 +103,26 @@ public class ComfyUISocketServiceImpl implements ComfyUISocketService {
                 sessions.put(clientId, session);
                 // 接收消息
 
-//                // [SSE] 发送消息
-//                ChatSseMessage chatSearchSseMessage = new ChatSseMessage("SEARCH", context, null, history_code);
-
+                System.out.println("------ wsUrl: " + wsUrl + " ------");
+                System.out.println("------ connectToSse clientId: " + clientId + ", user_id: " + user_id + " ------");
 
                 Flux<String> incomingMessages = session.receive()
                         .map(WebSocketMessage::getPayloadAsText)
                         .doOnNext(message -> {
                             System.out.println("(doOnNext) Received from " + clientId + ": " + message);
-                            sseUtil.send(user_id, new SseResponse(SseResponseEnum.COMFYUI, message).toJsonStr());
+
+                            JSONObject messageObj = new JSONObject();
+                            if (JSONUtil.isTypeJSON(message)) {
+
+                                messageObj = JSONUtil.parseObj(message);
+                                // 记录生成数据
+                                JSONObject output = JSONUtil.parseObj(messageObj.get("output"));
+                                JSONArray images = JSONUtil.parseArray(output.get("images"));
+                                // [{"filename": "ComfyUI_00122_.png", "subfolder": "", "type": "output"}]
+                                // http://43.128.1.201:8001/api/view?filename=ComfyUI_00118_.png
+                            }
+
+                            sseUtil.send(user_id, new SseResponse(SseResponseEnum.COMFYUI, messageObj).toJsonStr());
                         })
                         .doOnError(e -> {
                             System.err.println("(doOnError) Error for " + clientId + ": " + e.getMessage());
@@ -118,6 +132,7 @@ public class ComfyUISocketServiceImpl implements ComfyUISocketService {
                             System.out.println("(doFinally) Connection closed for " + clientId + ": " + signal);
                             sseUtil.send(user_id, new SseResponse(SseResponseEnum.COMFYUI, signal).toJsonStr());
                             sessions.remove(clientId);
+                            System.out.println("---------------------------------------------------------");
                         });
                 // 需要返回一个Mono<Void>来表示处理完成
                 return incomingMessages.then();

+ 1 - 0
src/main/resources/application-dev.yml

@@ -191,4 +191,5 @@ klingai:
   token-duration-time: 10000
 
 comfyui:
+  url: http://127.0.0.1
   token: $2b$12$.MR4qGaFetN1FPQzbfyIrehsyjnPJ12xAZhR/l7KZpLkUPQTCG4gy

+ 1 - 0
src/main/resources/application-local.yml

@@ -204,4 +204,5 @@ klingai:
   token-duration-time: 10000
 
 comfyui:
+  url: http://43.128.1.201
   token: $2b$12$.MR4qGaFetN1FPQzbfyIrehsyjnPJ12xAZhR/l7KZpLkUPQTCG4gy

+ 1 - 0
src/main/resources/application-prod.yml

@@ -192,4 +192,5 @@ klingai:
   token-duration-time: 10000
 
 comfyui:
+  url: http://127.0.0.1
   token: $2b$12$.MR4qGaFetN1FPQzbfyIrehsyjnPJ12xAZhR/l7KZpLkUPQTCG4gy