Explorar o código

Merge branch 'dev-yhq' into develop

tsurumure hai 1 mes
pai
achega
80d21a67e6
Modificáronse 26 ficheiros con 281 adicións e 177 borrados
  1. 0 0
      configuration/nginx/conf.d/ai.daogu.api.conf
  2. 0 0
      configuration/nginx/conf.d/ai.daogu.manage.conf
  3. 13 0
      configuration/nginx/conf.d/comfyui_output.conf
  4. 51 0
      configuration/nginx/conf.d/o.daogu.conf
  5. 19 19
      db/ai_media_ttv_timbre.sql
  6. 2 6
      db/crt_generate_image.sql
  7. 0 107
      src/main/java/com/backendsys/config/WebSocket/WebSocketConfig.java
  8. 2 1
      src/main/java/com/backendsys/modules/ai/media/service/impl/MediaTtvServiceImpl.java
  9. 1 0
      src/main/java/com/backendsys/modules/common/config/security/utils/SecurityUtil.java
  10. 53 11
      src/main/java/com/backendsys/modules/common/utils/CommonUtil.java
  11. 2 0
      src/main/java/com/backendsys/modules/crt/entity/CrtGenerateImage.java
  12. 15 2
      src/main/java/com/backendsys/modules/crt/service/impl/CrtGenerateServiceImpl.java
  13. 15 2
      src/main/java/com/backendsys/modules/sdk/comfyui/controller/ComfyUIDemoController.java
  14. 3 0
      src/main/java/com/backendsys/modules/sdk/comfyui/service/ComfyUISocketService.java
  15. 0 1
      src/main/java/com/backendsys/modules/sdk/comfyui/service/impl/ComfyUIServiceImpl.java
  16. 52 2
      src/main/java/com/backendsys/modules/sdk/comfyui/service/impl/ComfyUISocketServiceImpl.java
  17. 2 0
      src/main/java/com/backendsys/modules/sdk/douyincloud/tos/service/DouyinTosService.java
  18. 19 5
      src/main/java/com/backendsys/modules/sdk/douyincloud/tos/service/impl/DouyinTosServiceImpl.java
  19. 2 0
      src/main/java/com/backendsys/modules/sdk/tencentcloud/cos/service/TencentCosService.java
  20. 18 12
      src/main/java/com/backendsys/modules/sdk/tencentcloud/cos/service/impl/TencentCosServiceImpl.java
  21. 1 0
      src/main/java/com/backendsys/modules/upload/entity/SysFileResult.java
  22. 1 1
      src/main/java/com/backendsys/modules/upload/service/SysFileService.java
  23. 7 5
      src/main/java/com/backendsys/modules/upload/service/impl/SysFileServiceImpl.java
  24. 1 1
      src/main/resources/application-dev.yml
  25. 1 1
      src/main/resources/application-local.yml
  26. 1 1
      src/main/resources/application-prod.yml

+ 0 - 0
configuration/nginx/conf.d/ai.api.conf → configuration/nginx/conf.d/ai.daogu.api.conf


+ 0 - 0
configuration/nginx/conf.d/ai.manage.conf → configuration/nginx/conf.d/ai.daogu.manage.conf


+ 13 - 0
configuration/nginx/conf.d/comfyui_output.conf

@@ -0,0 +1,13 @@
+# 在 ComfyUI 主机中配置,将 9999 端口暴露出来
+server {
+    listen 9999;
+    server_name _;
+    location /8000/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_0/output/; }
+    location /8001/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_1/output/; }
+    location /8002/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_2/output/; }
+    location /8003/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_3/output/; }
+    location /8004/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_4/output/; }
+    location /8005/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_5/output/; }
+    location /8006/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_6/output/; }
+    location /8007/ { alias /mnt/nvme0n1/ComfyUI/ComfyUI_7/output/; }
+}

+ 51 - 0
configuration/nginx/conf.d/o.daogu.conf

@@ -0,0 +1,51 @@
+server {
+    listen          80;
+    server_name     o.daogu.ai;
+
+    location ^~ /.well-known/acme-challenge/ {
+        root /home/webroot;
+        allow all;
+    }
+
+    location / {
+        proxy_pass http://127.0.0.1:9999;
+        proxy_http_version 1.1;
+        proxy_connect_timeout 4s;
+        proxy_read_timeout 60s;
+        proxy_send_timeout 12s;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header Host $http_host;
+        proxy_set_header X-NginX-Proxy true;
+        proxy_redirect off;
+    }
+}
+
+#server {
+#    listen       443 ssl;
+#    server_name  o.daogu.ai;
+#
+#    ssl_certificate      /root/.acme.sh/o.daogu.ai/fullchain.cer;
+#    ssl_certificate_key  /root/.acme.sh/o.daogu.ai/o.daogu.ai.key;
+#    ssl_session_cache    shared:SSL:1m;
+#    ssl_session_timeout  5m;
+#    ssl_ciphers  HIGH:!aNULL:!MD5;
+#    ssl_prefer_server_ciphers  on;
+#
+#    location / {
+#        proxy_pass http://127.0.0.1:9999;
+#        proxy_http_version 1.1;
+#        proxy_connect_timeout 4s;
+#        proxy_read_timeout 60s;
+#        proxy_send_timeout 12s;
+#        proxy_set_header Upgrade $http_upgrade;
+#        proxy_set_header Connection "upgrade";
+#        proxy_set_header X-Real-IP $remote_addr;
+#        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+#        proxy_set_header Host $http_host;
+#        proxy_set_header X-NginX-Proxy true;
+#        proxy_redirect off;
+#    }
+#}

+ 19 - 19
db/ai_media_ttv_timbre.sql

@@ -19,23 +19,23 @@ CREATE TABLE `ai_media_ttv_timbre` (
 
 
 INSERT INTO ai_media_ttv_timbre(timbre_key, category_id, name, tag, description, avatar, audio_url) VALUES
-    ('0', '2', '小美-女', '成熟女声', '可使用于金融、零售、广告场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/0:小美-女.mp3'),
-    ('1', '1', '小宇-男', '温润男声', '可使用于小说、故事场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/1:小宇-男.mp3'),
-    ('3', '1', '小云-男', '磁性男声', '可使用于通用场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/3:小云-男.mp3'),
-    ('4', '4', '小丫-女童', '乖巧童声', '可使用于小说、故事场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-child-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4:小丫-女童.mp3'),
-    ('5', '2', '小娇-女', '情感女声', '可使用于小说、故事场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/5:小娇-女.mp3'),
-    ('103', '4', '小朵-女童', '可爱童声', '可使用于通用场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-child-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/103:小朵-女童.mp3'),
-    ('106', '1', '小博-男', '成熟男声', '可使用于新闻、金融、零售场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/106:小博-男.mp3'),
-    ('110', '3', '小童-男童', '活泼童声', '可使用于通用场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-child-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/110:小童-男童.mp3'),
-    ('111', '2', '小萌-女', '萝莉女声', '可使用于社交、游戏场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-child-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/111:小萌-女.mp3'),
-    ('4003', '1', '小耀-男', '磁性男声', '可使用于金融、零售、广告场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4003:小耀-男.mp3'),
-    ('4100', '2', '小雯-女', '成熟女声', '可使用于新闻、金融、零售场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4100:小雯-女.mp3'),
-    ('4103', '3', '小米-男童', '可爱童声', '可使用于通用场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-child-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4103:小米-男童.mp3'),
-    ('4105', '2', '小灵-女', '清澈女声', '可使用于通用场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4105:小灵-女.mp3'),
-    ('4106', '1', '小文-男', '情感男声', '可使用于社交、游戏场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4106:小文-男.mp3'),
-    ('4115', '1', '小贤-男', '情感男声', '可使用于社交、故事场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4115:小贤-男.mp3'),
-    ('4117', '2', '小乔-女', '情感女声', '可使用于社交、故事场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4117:小乔-女.mp3'),
-    ('4119', '2', '小鹿-女', '甜美女声', '可使用于社交、游戏场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/4119:小鹿-女.mp3'),
-    ('5003', '1', '小遥-男', '甜美男声', '可使用于社交、故事场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/5003:小遥-男.mp3'),
-    ('5118', '2', '小婷-女', '甜美女声', '可使用于社交、游戏场景', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daoguyujia.com/materials/baidu/ai-generate-video/audio/5118:小婷-女.mp3')
+    ('0', '2', '小美-女', '成熟女声', '可使用于金融、零售、广告场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/0:小美-女.mp3'),
+    ('1', '1', '小宇-男', '温润男声', '可使用于小说、故事场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/1:小宇-男.mp3'),
+    ('3', '1', '小云-男', '磁性男声', '可使用于通用场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/3:小云-男.mp3'),
+    ('4', '4', '小丫-女童', '乖巧童声', '可使用于小说、故事场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-child-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4:小丫-女童.mp3'),
+    ('5', '2', '小娇-女', '情感女声', '可使用于小说、故事场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/5:小娇-女.mp3'),
+    ('103', '4', '小朵-女童', '可爱童声', '可使用于通用场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-child-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/103:小朵-女童.mp3'),
+    ('106', '1', '小博-男', '成熟男声', '可使用于新闻、金融、零售场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/106:小博-男.mp3'),
+    ('110', '3', '小童-男童', '活泼童声', '可使用于通用场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-child-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/110:小童-男童.mp3'),
+    ('111', '2', '小萌-女', '萝莉女声', '可使用于社交、游戏场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-child-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/111:小萌-女.mp3'),
+    ('4003', '1', '小耀-男', '磁性男声', '可使用于金融、零售、广告场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4003:小耀-男.mp3'),
+    ('4100', '2', '小雯-女', '成熟女声', '可使用于新闻、金融、零售场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4100:小雯-女.mp3'),
+    ('4103', '3', '小米-男童', '可爱童声', '可使用于通用场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-child-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4103:小米-男童.mp3'),
+    ('4105', '2', '小灵-女', '清澈女声', '可使用于通用场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4105:小灵-女.mp3'),
+    ('4106', '1', '小文-男', '情感男声', '可使用于社交、游戏场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4106:小文-男.mp3'),
+    ('4115', '1', '小贤-男', '情感男声', '可使用于社交、故事场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4115:小贤-男.mp3'),
+    ('4117', '2', '小乔-女', '情感女声', '可使用于社交、故事场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4117:小乔-女.mp3'),
+    ('4119', '2', '小鹿-女', '甜美女声', '可使用于社交、游戏场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/4119:小鹿-女.mp3'),
+    ('5003', '1', '小遥-男', '甜美男声', '可使用于社交、故事场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/male-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/5003:小遥-男.mp3'),
+    ('5118', '2', '小婷-女', '甜美女声', '可使用于社交、游戏场景', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/avatar/female-1.png', 'http://cos.daogu.ai/materials/baidu/ai-generate-video/audio/5118:小婷-女.mp3')
 ;

+ 2 - 6
db/crt_generate_image.sql

@@ -9,15 +9,11 @@ CREATE TABLE `crt_generate_image` (
     PRIMARY KEY (`id`),
     `id` BIGINT AUTO_INCREMENT COMMENT 'ID',
     `user_id` BIGINT COMMENT '用户ID',
-    `name` VARCHAR(255) NOT NULL COMMENT '图片名称',
+    `drama_project_storyboard_id` BIGINT COMMENT '分镜ID',
+    `prompt_id` VARCHAR(255) COMMENT '任务ID',
     `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')
-# ;

+ 0 - 107
src/main/java/com/backendsys/config/WebSocket/WebSocketConfig.java

@@ -1,107 +0,0 @@
-//package com.backendsys.config.WebSocket;
-//
-//import cn.hutool.core.util.StrUtil;
-//import com.backendsys.modules.common.config.security.utils.JwtUtil;
-//
-//import io.jsonwebtoken.Claims;
-//import lombok.RequiredArgsConstructor;
-//import org.springframework.beans.factory.annotation.Autowired;
-//import org.springframework.context.annotation.Configuration;
-//import org.springframework.http.HttpHeaders;
-//import org.springframework.messaging.Message;
-//import org.springframework.messaging.MessageChannel;
-//import org.springframework.messaging.simp.config.ChannelRegistration;
-//import org.springframework.messaging.simp.config.MessageBrokerRegistry;
-//import org.springframework.messaging.simp.stomp.StompCommand;
-//import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
-//import org.springframework.messaging.support.ChannelInterceptor;
-//import org.springframework.messaging.support.MessageHeaderAccessor;
-//import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
-//import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
-//import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
-//
-//@Configuration
-//@EnableWebSocketMessageBroker
-//@RequiredArgsConstructor
-//public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
-//
-//
-//    @Autowired
-//    private JwtUtil jwtUtil;
-//
-//    /**
-//     * 注册一个端点,客户端通过这个端点进行连接
-//     */
-//    @Override
-//    public void registerStompEndpoints(StompEndpointRegistry registry) {
-//        registry
-//                .addEndpoint("/ws")   // 注册了一个 /ws 的端点
-//                .setAllowedOriginPatterns("*") // 允许跨域的 WebSocket 连接
-//                .withSockJS();  // 启用 SockJS (浏览器不支持WebSocket,SockJS 将会提供兼容性支持)
-//    }
-//
-//    /**
-//     * 配置消息代理
-//     */
-//    @Override
-//    public void configureMessageBroker(MessageBrokerRegistry registry) {
-//        // 客户端发送消息的请求前缀
-//        registry.setApplicationDestinationPrefixes("/app");
-//        // 客户端订阅消息的请求前缀,topic一般用于广播推送,queue用于点对点推送
-//        registry.enableSimpleBroker("/topic", "/queue");
-//        // 服务端通知客户端的前缀,可以不设置,默认为user
-//        registry.setUserDestinationPrefix("/user");
-//    }
-//
-//    /**
-//     * 配置客户端入站通道拦截器
-//     * <p>
-//     * 添加 ChannelInterceptor 拦截器,用于在消息发送前,从请求头中获取 token 并解析出用户信息(username),用于点对点发送消息给指定用户
-//     *
-//     * @param registration 通道注册器
-//     */
-//    @Override
-//    public void configureClientInboundChannel(ChannelRegistration registration) {
-//        registration.interceptors(new ChannelInterceptor() {
-//            @Override
-//            public Message<?> preSend(Message<?> message, MessageChannel channel) {
-//
-//                StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
-//
-//                // (改) 如果是连接请求(CONNECT 命令),从请求头中取出 token 并设置到认证信息中
-//                if (accessor != null && StompCommand.CONNECT.equals(accessor.getCommand())) {
-//
-//                   // 从连接头中提取授权令牌
-//                   String bearerToken = accessor.getFirstNativeHeader(HttpHeaders.AUTHORIZATION);
-//
-//                   // 验证令牌格式并提取用户信息
-//                   if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith("Bearer ")) {
-//                       try {
-//                           // 移除 "Bearer " 前缀,从令牌中提取用户信息(username), 并设置到认证信息中
-//                           String tokenWithoutPrefix = bearerToken.substring(7);
-//
-//                           Claims tokenInfo = jwtUtil.extractAllClaims(tokenWithoutPrefix);
-//                           String username = (String) tokenInfo.get("username");
-//
-//                           if (StrUtil.isNotBlank(username)) {
-//                               accessor.setUser(() -> username);
-//                               return message;
-//                           }
-//
-//                       } catch (Exception e) {
-//                           throw new RuntimeException("Failed to process authentication token.");
-//                       }
-//                   }
-//
-//
-//                }
-//                // 不是连接请求,直接放行
-//
-//
-//
-//                return ChannelInterceptor.super.preSend(message, channel);
-//            }
-//        });
-//    }
-//
-//}

+ 2 - 1
src/main/java/com/backendsys/modules/ai/media/service/impl/MediaTtvServiceImpl.java

@@ -12,6 +12,7 @@ import com.backendsys.modules.ai.media.entity.MediaTtv;
 import com.backendsys.modules.ai.media.entity.MediaTtvConfig;
 import com.backendsys.modules.ai.media.entity.MediaTtvSource;
 import com.backendsys.modules.ai.media.service.MediaTtvService;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.sdk.baidu.bce.entity.BaiduBceMediaJob;
 import com.backendsys.modules.sdk.baidu.bce.entity.BaiduBceMediaJobResult;
 import com.backendsys.modules.sdk.baidu.bce.service.BaiduBceMediaService;
@@ -128,7 +129,7 @@ public class MediaTtvServiceImpl implements MediaTtvService {
                 job_status = 1;
 
                 // URL转存文件
-                SysFileResult sysFileResult = sysFileService.urlToUploadFile(response.getVideo_origin_url());
+                SysFileResult sysFileResult = sysFileService.urlToUploadFile(response.getVideo_origin_url(), SecurityUtil.getUserId());
                 String object_key = sysFileResult.getKey();
                 String video_url = sysFileResult.getDomain() + "/" + sysFileResult.getKey();
 

+ 1 - 0
src/main/java/com/backendsys/modules/common/config/security/utils/SecurityUtil.java

@@ -66,6 +66,7 @@ public class SecurityUtil {
      * 获得当前登录 Token
      */
     public static String getToken() {
+        // 如果是在 websocket 周期中,是获取不到 context 的
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         return Convert.toStr(authentication.getDetails());
     }

+ 53 - 11
src/main/java/com/backendsys/modules/common/utils/CommonUtil.java

@@ -14,6 +14,7 @@ import java.io.File;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
@@ -68,25 +69,66 @@ public class CommonUtil {
             throw new RuntimeException(e);
         }
     }
-    // 下载URL文件到本地
+
+    /**
+     * 从 URL 下载文件到本地临时目录(兼容 Windows & Linux)
+     * @param url 文件下载地址(如 "http://example.com/file.txt")
+     * @return 下载后的本地文件对象
+     * @throws RuntimeException 如果下载失败
+     */
     public static File downloadLocalFromUrl(String url) {
         try {
-            // 获取系统默认的临时目录
+            // 1. 获取系统临时目录(兼容 Win/Linux)
             Path tempDirPath = Paths.get(System.getProperty("java.io.tmpdir"));
-            // 下载文件到本地 (可能会有性能问题)
-            HttpUtil.downloadFile(url, tempDirPath.toFile());
-            // 获得远程 url
+            // 2. 解析 URL 获取文件名(处理编码和路径)
             URL urlObject = new URL(url);
-            // 获得文件名
-            String filename = Paths.get(urlObject.getPath()).getFileName().toString();
-            // 获得下载到本地的路径
-            File downloadFile = new File(tempDirPath + File.separator + filename);
-            return downloadFile;
+            String filename = extractValidFilename(urlObject);
+            // 3. 构建目标文件路径(自动处理路径分隔符)
+            Path targetPath = tempDirPath.resolve(filename);
+            // 4. 下载文件(示例使用 Hutool 的 HttpUtil)
+            HttpUtil.downloadFile(url, targetPath.toFile());
+            // 5. 检查文件是否下载成功
+            if (!targetPath.toFile().exists()) {
+                throw new RuntimeException("下载失败,文件不存在: " + targetPath);
+            }
+            return targetPath.toFile();
         } catch (MalformedURLException e) {
-            throw new RuntimeException(e);
+            throw new RuntimeException("URL 格式错误: " + url, e);
+        } catch (IOException e) {
+            throw new RuntimeException("下载文件失败: " + url, e);
         }
     }
 
+    /**
+     * 从 URL 提取有效文件名(移除非法字符)
+     */
+    private static String extractValidFilename(URL url) {
+        // 获取原始文件名(如 "/downloads/file.txt" → "file.txt")
+        String filename = Paths.get(url.getPath()).getFileName().toString();
+        // 移除 URL 编码(如 "%20" → " ")
+        filename = java.net.URLDecoder.decode(filename, StandardCharsets.UTF_8);
+        // 替换非法字符(兼容 Win/Linux)
+        return filename.replaceAll("[\\\\/:*?\"<>|]", "_");
+    }
+
+//    public static File downloadLocalFromUrl(String url) {
+//        try {
+//            // 获取系统默认的临时目录
+//            Path tempDirPath = Paths.get(System.getProperty("java.io.tmpdir"));
+//            // 下载文件到本地 (可能会有性能问题)
+//            HttpUtil.downloadFile(url, tempDirPath.toFile());
+//            // 获得远程 url
+//            URL urlObject = new URL(url);
+//            // 获得文件名
+//            String filename = Paths.get(urlObject.getPath()).getFileName().toString();
+//            // 获得下载到本地的路径
+//            File downloadFile = new File(tempDirPath + File.separator + filename);
+//            return downloadFile;
+//        } catch (MalformedURLException e) {
+//            throw new RuntimeException(e);
+//        }
+//    }
+
     // [文件名] 插入 '-thumb' 文件名后缀
     public static String insertThumbSuffix(String filePath) {
         int dotIndex = filePath.lastIndexOf('.');

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

@@ -16,6 +16,8 @@ public class CrtGenerateImage {
     @TableId(type = IdType.AUTO)
     private Long id;
     private Long user_id;                       // 用户ID
+    private Long drama_project_storyboard_id;   // 分镜ID
+    private String prompt_id;                   // 任务ID
     private String name;                        // 图片名称
     private String url_origin;                  // 原图
     private String url;                          // 转存图

+ 15 - 2
src/main/java/com/backendsys/modules/crt/service/impl/CrtGenerateServiceImpl.java

@@ -5,11 +5,13 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.backendsys.exception.CustException;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.crt.dao.CrtDramaProjectSettingsDao;
 import com.backendsys.modules.crt.dao.CrtDramaProjectStoryboardDao;
 import com.backendsys.modules.crt.dao.CrtModelDao;
 import com.backendsys.modules.crt.entity.CrtDramaProjectSettings;
 import com.backendsys.modules.crt.entity.CrtDramaProjectStoryboard;
+import com.backendsys.modules.crt.entity.CrtGenerateImage;
 import com.backendsys.modules.crt.entity.CrtModel;
 import com.backendsys.modules.crt.enums.SamplerEnums;
 import com.backendsys.modules.crt.service.CrtGenerateService;
@@ -91,6 +93,7 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
         Integer param_batch_size = storyboardDetail.getParam_batch_size();                        // 生成图片数量
         Float param_prompt_flux_guidance = storyboardDetail.getParam_prompt_flux_guidance();      // 提示词引导系数
         String param_sampler = SamplerEnums.getValueByKey(storyboardDetail.getParam_sampler());   // 采样器
+        Integer param_step = storyboardDetail.getParam_step();
 
         // 随机种子 (默认值:1,范围:(1:随机, 2:自定义))
         Integer param_seed = storyboardDetail.getParam_seed();
@@ -99,9 +102,16 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
             param_seed_custom = Convert.toStr(DateUtil.current());   // 生成一个随机值 (毫秒时间戳)
 
             // 【随机值,要保存到数据库】
+            CrtDramaProjectStoryboard entity = new CrtDramaProjectStoryboard();
+            entity.setParam_seed_custom(param_seed_custom);
+            LambdaQueryWrapper<CrtDramaProjectStoryboard> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(CrtDramaProjectStoryboard::getId, drama_project_storyboard_id);
+            crtDramaProjectStoryboardDao.update(entity, wrapper);
 
         }
 
+
+
         // [db] 获取基础模型 (从项目配置)
         Long model_id = settings_image.getModel_id();
         CrtModel modelDetail = crtModelDao.selectById(model_id);
@@ -121,7 +131,9 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
         String client_id = Convert.toStr(UUID.randomUUID());
 
         // -- [ComfyUI] 创建 WebSocket 监听连接 --------------------------
-        comfyUISocketService.connectToSse(client_id, 8000).subscribe();
+        Map<String, Object> params = new LinkedHashMap<>();
+        params.put("drama_project_storyboard_id", drama_project_storyboard_id);
+        comfyUISocketService.connectToSse(client_id, 8000, params).subscribe();
 
         // -------------------------------------------------------------
         // [ComfyUI-基础生图]
@@ -451,7 +463,7 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
                 "  \"150\": {"+
                 "    \"inputs\": {"+
                 "      \"scheduler\": \"normal\","+
-                "      \"steps\": 25,"+
+                "      \"steps\": " + param_step + ","+
                 "      \"denoise\": 1,"+
                 "      \"model\": ["+
                 "        \"152\","+
@@ -515,6 +527,7 @@ public class CrtGenerateServiceImpl implements CrtGenerateService {
         CFPromptResponse response = cfPromptResponseMono.block();
         response.setClient_id(client_id);
         System.out.println("结果: " + response);
+        // 结果: CFPromptResponse(client_id=1a8a2d01-5500-437f-bb11-7a986130da48, prompt_id=c74501ed-6755-48f8-a440-aef3474b523c, number=47, node_errors={}, error=null)
 
         Map<String, Object> resp = new LinkedHashMap<>();
         resp.put("drama_project_storyboard_id", drama_project_storyboard_id);

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

@@ -2,8 +2,11 @@ package com.backendsys.modules.sdk.comfyui.controller;
 
 import com.backendsys.exception.CustException;
 import com.backendsys.modules.common.config.security.annotations.Anonymous;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.sdk.comfyui.service.ComfyUIService;
 import com.backendsys.modules.sdk.comfyui.service.ComfyUISocketService;
+import com.backendsys.modules.sdk.tencentcloud.cos.service.TencentCosService;
+import com.backendsys.modules.upload.entity.SysFileResult;
 import com.tencentcloudapi.tione.v20211111.models.ChatCompletionResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.MediaType;
@@ -22,8 +25,10 @@ import java.util.concurrent.Executors;
 public class ComfyUIDemoController {
 
     @Autowired
-    private ComfyUIService comfyUIService;
+    private TencentCosService tencentCosService;
 
+    @Autowired
+    private ComfyUIService comfyUIService;
     @Autowired
     private ComfyUISocketService comfyUISocketService;
 
@@ -34,7 +39,7 @@ public class ComfyUIDemoController {
     @PostMapping("/api/comfyui/ws/connect")
     public String connect(String clientId) {
         comfyUISocketService.connect(clientId, 8000).subscribe();
-        return "Connection initiated for: " + clientId;
+        return "Connection initiated for client_id: " + clientId;
     }
 
     /**
@@ -47,4 +52,12 @@ public class ComfyUIDemoController {
         return "Disconnected: " + clientId;
     }
 
+    /**
+     * 转存测试
+     */
+    @GetMapping("/api/comfyui/testToCos")
+    public SysFileResult testToCos(String url) {
+        return tencentCosService.urlToCOS(url, "png");
+    }
+
 }

+ 3 - 0
src/main/java/com/backendsys/modules/sdk/comfyui/service/ComfyUISocketService.java

@@ -2,6 +2,8 @@ package com.backendsys.modules.sdk.comfyui.service;
 
 import reactor.core.publisher.Mono;
 
+import java.util.Map;
+
 public interface ComfyUISocketService {
 
     // [ComfyUI] 创建 WebSocket 监听连接
@@ -9,6 +11,7 @@ public interface ComfyUISocketService {
 
     // [ComfyUI] 创建 WebSocket 监听连接 (转发到 SSE)
     Mono<Void> connectToSse(String clientId, Integer port);
+    Mono<Void> connectToSse(String clientId, Integer port, Map<String, Object> params);
 
     // [ComfyUI] 断开 WebSocket 监听连接
     Mono<Void> disconnect(String clientId);

+ 0 - 1
src/main/java/com/backendsys/modules/sdk/comfyui/service/impl/ComfyUIServiceImpl.java

@@ -32,7 +32,6 @@ public class ComfyUIServiceImpl implements ComfyUIService {
     private WebClient webClient;
     public WebClient getWebClient() {
         if (webClient == null) {
-//            webClient = WebClient.builder().baseUrl(COMFYUI_HOST + ":8000").filter(WebClientFilter.logFilter).build();
             webClient = WebClient.builder().filter(WebClientFilter.logFilter).build();
         }
         return webClient;

+ 52 - 2
src/main/java/com/backendsys/modules/sdk/comfyui/service/impl/ComfyUISocketServiceImpl.java

@@ -6,11 +6,18 @@ 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.crt.dao.CrtGenerateImageDao;
+import com.backendsys.modules.crt.entity.CrtGenerateImage;
 import com.backendsys.modules.sdk.comfyui.enums.TypeEnums;
 import com.backendsys.modules.sdk.comfyui.service.ComfyUISocketService;
+import com.backendsys.modules.sdk.douyincloud.tos.service.DouyinTosService;
+import com.backendsys.modules.sdk.tencentcloud.cos.service.TencentCosService;
 import com.backendsys.modules.sse.entity.SseResponse;
 import com.backendsys.modules.sse.entity.SseResponseEnum;
 import com.backendsys.modules.sse.utils.SseUtil;
+import com.backendsys.modules.system.service.SysCommonService;
+import com.backendsys.modules.upload.entity.SysFileResult;
+import com.backendsys.modules.upload.service.SysFileService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -28,6 +35,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 
 @Service
@@ -35,6 +43,10 @@ public class ComfyUISocketServiceImpl implements ComfyUISocketService {
 
     @Autowired
     private SseUtil sseUtil;
+    @Autowired
+    private SysFileService sysFileService;
+    @Autowired
+    private CrtGenerateImageDao crtGenerateImageDao;
 
     @Value("${comfyui.host}")
     private String COMFYUI_HOST;
@@ -98,8 +110,16 @@ public class ComfyUISocketServiceImpl implements ComfyUISocketService {
      */
     @Override
     public Mono<Void> connectToSse(String clientId, Integer port) {
+        return connectToSse(clientId, port, null);
+    }
+    @Override
+    public Mono<Void> connectToSse(String clientId, Integer port, Map<String, Object> params) {
 
+        CrtGenerateImage entity = new CrtGenerateImage();
+
+        // Websocket 获取不到上下文信息,所以 user_id 需要从外部传递
         Long user_id = SecurityUtil.getUserId();
+        entity.setUser_id(user_id);
 
         String wsUrl =  "ws://" + COMFYUI_HOST + ":" + port + "/ws";
         return Mono.defer(() -> {
@@ -132,6 +152,9 @@ public class ComfyUISocketServiceImpl implements ComfyUISocketService {
                                 JSONObject output = JSONUtil.parseObj(dataChildren.get("output"));
 
                                 // -- [生成图片] ------------------------------------------------------
+                                // 由于图片地址不是公开的,需要加 Token 访问,因此不能公开返回原始图片地址,比如:
+                                // http://43.128.1.201:8000/api/view?filename=fenjing_1_00012_.png&token=$2b$12$.MR4qGaFetN1FPQzbfyIrehsyjnPJ12xAZhR/l7KZpLkUPQTCG4gy
+
                                 Object imagesObj = output.get("images");
                                 if (imagesObj != null) {
                                     JSONArray images = JSONUtil.parseArray(imagesObj);
@@ -143,13 +166,40 @@ public class ComfyUISocketServiceImpl implements ComfyUISocketService {
                                         for (int i = 0; i < images.size(); i++) {
                                             JSONObject image = images.getJSONObject(i);
                                             String filename = image.getStr("filename");
-                                            String filepath = "http://" + COMFYUI_HOST + ":" + port + "/api/view?filename=" + filename;
-                                            images_path.add(filepath);
+
+                                            // ComfyUI + Nginx 输出域名转发
+//                                            String filepath = "http://" + COMFYUI_HOST + ":" + port + "/api/view?filename=" + filename;
+//                                            String filepath_with_token = filepath + "&token=" + COMFYUI_TOKEN;
+
+                                            String filepath = "http://o.daogu.ai/" + port + "/" + filename;
 
                                             // -- [图片转存储存桶] -------------------------------------
+                                            SysFileResult result = sysFileService.urlToUploadFile(filepath, user_id);
+                                            System.out.println("urlToUploadFile (result) = " + result);
+
+                                            // -- [记录到生成图片记录表] --------------------------------
+                                            if (params != null) {
+
+                                                // 创建一个 CompletableFuture 来执行异步任务
+                                                CompletableFuture.runAsync(() -> {
+
+                                                    Long drama_project_storyboard_id = Convert.toLong(params.get("drama_project_storyboard_id"));
+                                                    String prompt_id = Convert.toStr(dataChildren.get("prompt_id"));
+                                                    if (drama_project_storyboard_id != null) {
+                                                        entity.setDrama_project_storyboard_id(drama_project_storyboard_id);
+                                                        entity.setPrompt_id(prompt_id);
+                                                        entity.setUrl_origin(filepath);
+                                                        entity.setUrl(result.getUrl());
+                                                        crtGenerateImageDao.insert(entity);
+                                                    }
 
+                                                });
+
+                                            }
                                             // ------------------------------------------------------
 
+                                            images_path.add(filepath);
+
                                         }
                                     }
                                     output.put("images_path", images_path);

+ 2 - 0
src/main/java/com/backendsys/modules/sdk/douyincloud/tos/service/DouyinTosService.java

@@ -37,5 +37,7 @@ public interface DouyinTosService {
 
     // [抖音云TOS] URL 转存
     SysFileResult urlToTOS(String origin_url);
+    SysFileResult urlToTOS(String origin_url, String input_suffix);
+    SysFileResult urlToTOS(String origin_url, String input_suffix, Long user_id);
 
 }

+ 19 - 5
src/main/java/com/backendsys/modules/sdk/douyincloud/tos/service/impl/DouyinTosServiceImpl.java

@@ -60,7 +60,7 @@ public class DouyinTosServiceImpl implements DouyinTosService {
 
 
     // [抖音云COS] 获取进度函数
-    private DataTransferListener getDataTransferListener(String filename) {
+    private DataTransferListener getDataTransferListener(String filename, Long input_user_id) {
 
         return new DataTransferListener() {
             // 自定义实现 DataTransferListener 的 dataTransferStatusChange 接口
@@ -97,7 +97,10 @@ public class DouyinTosServiceImpl implements DouyinTosService {
                 progress.setPercent(percentage);
                 progress.setState(state);
                 String dataStr = (new SseResponse(SseResponseEnum.UPLOAD, progress)).toJsonStr();
-                sseUtil.send(SecurityUtil.getUserId(), dataStr);
+
+                Long user_id = input_user_id == null ? SecurityUtil.getUserId() : input_user_id;
+
+                sseUtil.send(user_id, dataStr);
 
             }
         };
@@ -141,7 +144,7 @@ public class DouyinTosServiceImpl implements DouyinTosService {
             putObjectInput.setOptions(options);
 
             // 自定义实现 DataTransferListener,实现进度条功能
-            DataTransferListener listener = getDataTransferListener(filename);
+            DataTransferListener listener = getDataTransferListener(filename, null);
             putObjectInput.setDataTransferListener(listener);
 
             // 上传对象
@@ -153,6 +156,7 @@ public class DouyinTosServiceImpl implements DouyinTosService {
             result.setRequest_id(output.getRequestInfo().getRequestId());
             result.setE_tag(output.getEtag());
             result.setDomain(DOMAIN);
+            result.setUrl(DOMAIN + "/" + object_key);
             return result;
 
         } catch (TosClientException | TosServerException e) {
@@ -355,14 +359,23 @@ public class DouyinTosServiceImpl implements DouyinTosService {
     /**
      * [抖音云TOS] URL 转存
      */
+    @Override
     public SysFileResult urlToTOS(String origin_url) {
+        return urlToTOS(origin_url, null);
+    }
+    @Override
+    public SysFileResult urlToTOS(String origin_url, String input_suffix) {
+        return urlToTOS(origin_url, input_suffix);
+    }
+    @Override
+    public SysFileResult urlToTOS(String origin_url, String input_suffix, Long user_id) {
 
         if (StrUtil.isEmpty(origin_url)) throw new CustException("url 不能为空");
 
         // 下载URL文件到本地
         File downloadFile = CommonUtil.downloadLocalFromUrl(origin_url);
         // 获得URL文件后缀名
-        String suffix = CommonUtil.getFileSuffixFromUrl(origin_url);
+        String suffix = StrUtil.isEmpty(input_suffix) ? CommonUtil.getFileSuffixFromUrl(origin_url) : input_suffix;
         // 获得文件大小
         Long size = downloadFile.length();
 
@@ -390,7 +403,7 @@ public class DouyinTosServiceImpl implements DouyinTosService {
                 putObjectInput.setOptions(options);
 
                 // 自定义实现 DataTransferListener,实现进度条功能
-                DataTransferListener listener = getDataTransferListener(object_filename);
+                DataTransferListener listener = getDataTransferListener(object_filename, user_id);
                 putObjectInput.setDataTransferListener(listener);
 
                 // 上传对象
@@ -403,6 +416,7 @@ public class DouyinTosServiceImpl implements DouyinTosService {
                 result.setRequest_id(output.getRequestInfo().getRequestId());
                 result.setE_tag(output.getEtag());
                 result.setSize(size);
+                result.setUrl(DOMAIN + "/" + object_key);
                 return result;
 
             } catch (TosClientException | TosServerException e) {

+ 2 - 0
src/main/java/com/backendsys/modules/sdk/tencentcloud/cos/service/TencentCosService.java

@@ -42,4 +42,6 @@ public interface TencentCosService {
 
     // [腾讯云COS] URL 转存
     SysFileResult urlToCOS(String url);
+    SysFileResult urlToCOS(String url, String input_suffix);
+    SysFileResult urlToCOS(String url, String input_suffix, Long user_id);
 }

+ 18 - 12
src/main/java/com/backendsys/modules/sdk/tencentcloud/cos/service/impl/TencentCosServiceImpl.java

@@ -3,8 +3,6 @@ package com.backendsys.modules.sdk.tencentcloud.cos.service.impl;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.hutool.crypto.digest.DigestUtil;
-import cn.hutool.http.HttpUtil;
 import com.backendsys.exception.CustException;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.common.utils.CommonUtil;
@@ -32,18 +30,14 @@ import org.joda.time.DateTime;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
-import org.springframework.util.StringUtils;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.*;
 import java.math.BigDecimal;
 import java.net.URL;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 
 // 图片处理概述
 // https://cloud.tencent.com/document/product/436/42215
@@ -83,9 +77,10 @@ public class TencentCosServiceImpl implements TencentCosService {
     }
 
     // [腾讯云COS][高级接口] 获取进度函数
-    private void showTransferProgress(String filename, Transfer transfer) {
+    private void showTransferProgress(String filename, Transfer transfer, Long input_user_id) {
 
-        Long user_id = SecurityUtil.getUserId();
+        Long user_id = input_user_id == null ? SecurityUtil.getUserId() : input_user_id;
+        System.out.println("showTransferProgress (user_id): " + user_id);
 
         // [SSE] 进度回传
         Progress progress = new Progress();
@@ -180,7 +175,7 @@ public class TencentCosServiceImpl implements TencentCosService {
             // 高级上传
             TransferManager transferManager = TencentCosUtil.createTransferManager(cosClient);
             Upload upload = transferManager.upload(putObjectRequest);   // 返回一个异步结果Upload
-            showTransferProgress(filename, upload);                     // 查询上传进度,直到上传结束
+            showTransferProgress(filename, upload, null);    // 查询上传进度,直到上传结束
             UploadResult uploadResult = upload.waitForUploadResult();   // 捕获可能出现的异常
 
             // 自定义返回结果实体
@@ -189,6 +184,7 @@ public class TencentCosServiceImpl implements TencentCosService {
             result.setRequest_id(uploadResult.getRequestId());
             result.setE_tag(uploadResult.getETag());
             result.setDomain(ACCESSIBLE_DOMAIN);
+            result.setUrl(ACCESSIBLE_DOMAIN + "/" + object_key);
             return result;
 
         } catch (IOException e) {
@@ -359,16 +355,24 @@ public class TencentCosServiceImpl implements TencentCosService {
     }
 
 
-    // [腾讯云COS] URL 转存
+    // [腾讯云COS] URL 转存@Override
     @Override
     public SysFileResult urlToCOS(String url) {
+        return urlToCOS(url, null);
+    }
+    @Override
+    public SysFileResult urlToCOS(String url, String input_suffix) {
+        return urlToCOS(url, input_suffix);
+    }
+    @Override
+    public SysFileResult urlToCOS(String url, String input_suffix, Long user_id) {
 
         if (StrUtil.isEmpty(url)) throw new CustException("url 不能为空");
 
         // 下载URL文件到本地
         File downloadFile = CommonUtil.downloadLocalFromUrl(url);
         // 获得URL文件后缀名
-        String suffix = CommonUtil.getFileSuffixFromUrl(url);
+        String suffix = StrUtil.isEmpty(input_suffix) ? CommonUtil.getFileSuffixFromUrl(url) : input_suffix;
         // 获得文件大小
         Long size = downloadFile.length();
 
@@ -387,7 +391,7 @@ public class TencentCosServiceImpl implements TencentCosService {
             // 高级上传
             TransferManager transferManager = TencentCosUtil.createTransferManager(cosClient);
             Upload upload = transferManager.upload(putObjectRequest);   // 返回一个异步结果Upload
-            showTransferProgress(object_filename, upload);                     // 查询上传进度,直到上传结束
+            showTransferProgress(object_filename, upload, user_id);     // 查询上传进度,直到上传结束
             UploadResult uploadResult = upload.waitForUploadResult();   // 捕获可能出现的异常
 
             SysFileResult result = new SysFileResult();
@@ -396,6 +400,7 @@ public class TencentCosServiceImpl implements TencentCosService {
             result.setRequest_id(uploadResult.getRequestId());
             result.setE_tag(uploadResult.getETag());
             result.setSize(size);
+            result.setUrl(ACCESSIBLE_DOMAIN + "/" + object_key);
             return result;
 
         } catch (CosClientException e) {
@@ -406,4 +411,5 @@ public class TencentCosServiceImpl implements TencentCosService {
             if (cosClient != null) cosClient.shutdown();
         }
     }
+
 }

+ 1 - 0
src/main/java/com/backendsys/modules/upload/entity/SysFileResult.java

@@ -8,5 +8,6 @@ public class SysFileResult {
     private String key;
     private String e_tag;
     private String domain;
+    private String url;
     private Long size;
 }

+ 1 - 1
src/main/java/com/backendsys/modules/upload/service/SysFileService.java

@@ -32,7 +32,7 @@ public interface SysFileService extends IService<SysFile> {
     Map<String, Object> updateUploadFileBatch(SysFile sysFile);
 
     // URL转存
-    SysFileResult urlToUploadFile(String origin_url);
+    SysFileResult urlToUploadFile(String origin_url, Long user_id);
 
     // 根据 MD5 获取文件列表 (我的)
     List<Map<String, Object>> getUploadFileListByMd5(SysFile sysFile);

+ 7 - 5
src/main/java/com/backendsys/modules/upload/service/impl/SysFileServiceImpl.java

@@ -444,28 +444,30 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
         return Map.of("ids", ids);
     }
 
-    // URL转存
+    // URL转存 (因为会用到 高级上传 -> 进度监听,需要从外部传入 user_id
     @Override
-    public SysFileResult urlToUploadFile(String origin_url) {
+    public SysFileResult urlToUploadFile(String origin_url, Long input_user_id) {
 
         Integer UPLOAD_TARGET = Convert.toInt(sysCommonService.getCommonByTag("UPLOAD_TARGET"));
 
         SysFileResult sysFileResult = null;
 
+        Long user_id = input_user_id == null ? SecurityUtil.getUserId() : input_user_id;
+
         // target: 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
         if (UPLOAD_TARGET == 1) {
-            sysFileResult = tencentCosService.urlToCOS(origin_url);
+            sysFileResult = tencentCosService.urlToCOS(origin_url, null, user_id);
         }
         // 3: 抖音云
         if (UPLOAD_TARGET == 3) {
-            sysFileResult = douyinTosService.urlToTOS(origin_url);
+            sysFileResult = douyinTosService.urlToTOS(origin_url, null, user_id);
         }
         if (sysFileResult == null) throw new CustException("上传失败");
 
         // [DB] 创建文件
         SysFile sysFileEntity = new SysFile();
         sysFileEntity.setRequest_id(sysFileEntity.getRequest_id());
-        sysFileEntity.setUser_id(SecurityUtil.getUserId());
+        sysFileEntity.setUser_id(user_id);
 
         try {
             URL parsed_url = new URL(origin_url);

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

@@ -136,7 +136,7 @@ tencent:
     region: ap-hongkong
     bucket-name: storage-1320301544
     # accessible-domain: https://storage-1320301544.cos.ap-hongkong.myqcloud.com
-    accessible-domain: http://cos.daoguyujia.com
+    accessible-domain: http://cos.daogu.ai
   ivh:
     empty-app-key: 283ca6dc9d4147debc60bf9fc3fbbe03         # 空数据账号 (我自己的子账号)
     empty-access-token: eea44503a2f64c119fb0acd2006dacb0

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

@@ -138,7 +138,7 @@ tencent:
     # 香港
     region: ap-hongkong
     bucket-name: storage-1320301544
-    accessible-domain: http://cos.daoguyujia.com
+    accessible-domain: http://cos.daogu.ai
 #    # 广州
 #    region: ap-guangzhou
 #    bucket-name: duanju3-1320301544

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

@@ -137,7 +137,7 @@ tencent:
     region: ap-hongkong
     bucket-name: storage-1320301544
     # accessible-domain: https://storage-1320301544.cos.ap-hongkong.myqcloud.com
-    accessible-domain: http://cos.daoguyujia.com
+    accessible-domain: http://cos.daogu.ai
   ivh:
     empty-app-key: 283ca6dc9d4147debc60bf9fc3fbbe03         # 空数据账号 (我自己的子账号)
     empty-access-token: eea44503a2f64c119fb0acd2006dacb0