Quellcode durchsuchen

Merge branch 'dev-volcengine'

tsurumure vor 3 Wochen
Ursprung
Commit
f2ca45160d
30 geänderte Dateien mit 1353 neuen und 4 gelöschten Zeilen
  1. 13 0
      db/ai_volcengine_video_collect.sql
  2. 16 0
      db/ai_volcengine_video_task.sql
  3. 19 0
      db/ai_volcengine_video_task_detail.sql
  4. 3 0
      db/sys_user_role_menu.sql
  5. 11 0
      db/sys_user_role_permission.sql
  6. 7 0
      db/sys_user_role_permission_relation.sql
  7. 5 0
      pom.xml
  8. 75 0
      src/main/java/com/backendsys/modules/ai/volcengine/controller/VolcengineVideoController.java
  9. 9 0
      src/main/java/com/backendsys/modules/ai/volcengine/dao/VolcengineVideoCollectDao.java
  10. 19 0
      src/main/java/com/backendsys/modules/ai/volcengine/dao/VolcengineVideoTaskDao.java
  11. 9 0
      src/main/java/com/backendsys/modules/ai/volcengine/dao/VolcengineVideoTaskDetailDao.java
  12. 39 0
      src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoCollect.java
  13. 97 0
      src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTask.java
  14. 33 0
      src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTaskDTO.java
  15. 81 0
      src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTaskDetail.java
  16. 71 0
      src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTaskNotify.java
  17. 10 0
      src/main/java/com/backendsys/modules/ai/volcengine/service/VolcengineVideoCollectService.java
  18. 8 0
      src/main/java/com/backendsys/modules/ai/volcengine/service/VolcengineVideoTaskDetailService.java
  19. 21 0
      src/main/java/com/backendsys/modules/ai/volcengine/service/VolcengineVideoTaskService.java
  20. 56 0
      src/main/java/com/backendsys/modules/ai/volcengine/service/impl/VolcengineVideoCollectServiceImpl.java
  21. 12 0
      src/main/java/com/backendsys/modules/ai/volcengine/service/impl/VolcengineVideoTaskDetailServiceImpl.java
  22. 357 0
      src/main/java/com/backendsys/modules/ai/volcengine/service/impl/VolcengineVideoTaskServiceImpl.java
  23. 26 1
      src/main/java/com/backendsys/modules/sdk/volcengine/controller/VolcengineDemoController.java
  24. 41 0
      src/main/java/com/backendsys/modules/sdk/volcengine/entity/VolcengineImage2VideoParams.java
  25. 11 0
      src/main/java/com/backendsys/modules/sdk/volcengine/service/VolcengineImage2VideoService.java
  26. 103 0
      src/main/java/com/backendsys/modules/sdk/volcengine/service/impl/VolcengineImage2VideoServiceImpl.java
  27. 8 1
      src/main/resources/application-dev.yml
  28. 8 1
      src/main/resources/application-local.yml
  29. 8 1
      src/main/resources/application-prod.yml
  30. 177 0
      src/main/resources/mapper/ai/volcengine/VolcengineVideoTaskDao.xml

+ 13 - 0
db/ai_volcengine_video_collect.sql

@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS `ai_volcengine_video_collect`;
+CREATE TABLE `ai_volcengine_video_collect`  (
+    `id` BIGINT NOT NULL AUTO_INCREMENT,
+    `user_id` BIGINT COMMENT '用户id',
+    `task_id` BIGINT COMMENT '任务id',
+    `task_detail_id` BIGINT COMMENT '任务明细id',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
+    PRIMARY KEY (`id`) USING BTREE,
+    INDEX `user_id`(`user_id` ASC) USING BTREE,
+    INDEX `task_detail_id`(`task_detail_id` ASC) USING BTREE,
+    INDEX `idx_user_task_detail`(`user_id` ASC, `task_id` ASC, `task_detail_id` ASC) USING BTREE,
+    INDEX `idx_user_task`(`user_id` ASC, `task_id` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '火山大模型生成视频收藏表' ROW_FORMAT = DYNAMIC;

+ 16 - 0
db/ai_volcengine_video_task.sql

@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS `ai_volcengine_video_task`;
+CREATE TABLE `ai_volcengine_video_task`  (
+    `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '任务id',
+    `user_id` BIGINT COMMENT '用户id',
+    `model` VARCHAR(255) COMMENT '模型',
+    `img_url` VARCHAR(500) COMMENT '图片地址',
+    `resolution` VARCHAR(32) COMMENT '分辨率',
+    `duration` TINYINT COMMENT '视频时长(秒)',
+    `text` VARCHAR(500) COMMENT '文本描述',
+    `camerafixed` TINYINT COMMENT '是否固定镜头 1-是 -1-否',
+    `quantity` TINYINT COMMENT '生成数量',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY (`id`) USING BTREE,
+    INDEX `user_id`(`user_id` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '火山大模型视频生成任务表' ROW_FORMAT = DYNAMIC;

+ 19 - 0
db/ai_volcengine_video_task_detail.sql

@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS `ai_volcengine_video_task_detail`;
+CREATE TABLE `ai_volcengine_video_task_detail`  (
+    `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '任务明细id',
+    `user_id` BIGINT COMMENT '用户id',
+    `task_id` BIGINT COMMENT '主表id',
+    `volcengine_task_id` VARCHAR(100) COMMENT '火山大模型任务id',
+    `cover_img_url` VARCHAR(500) COMMENT '封面图地址(首帧)',
+    `seed` INT COMMENT '种子',
+    `status` VARCHAR(64) COMMENT '状态\r\nqueued:排队中。\r\nrunning:任务运行中。\r\ncancelled:取消任务,取消状态24h自动删除(只支持排队中状态的任务被取消)。\r\nsucceeded: 任务成功。\r\nfailed:任务失败。',
+    `video_origin_url` TEXT COMMENT '视频原始地址',
+    `video_url` TEXT COMMENT '转存视频地址',
+    `object_key` TEXT COMMENT '对象key',
+    `error_msg` TEXT COMMENT '失败信息',
+    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY (`id`) USING BTREE,
+    INDEX `user_id`(`user_id` ASC) USING BTREE,
+    INDEX `task_id`(`task_id` ASC) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '火山大模型视频生成任务明细表' ROW_FORMAT = DYNAMIC;

+ 3 - 0
db/sys_user_role_menu.sql

@@ -78,6 +78,9 @@ INSERT INTO sys_user_role_menu(id, parent_id, menu_name, menu_name_en, type, pat
     (57, 50, '商品单位', 'Goods Units', 1, '/b2c/good/b2cGoodUnit', '/src/views/b2c/good/b2cGoodUnit.vue', '{}', null, '11.4', 14),
     (58, 50, '商品单位详情', 'Goods Units Detail', 2, '/b2c/good/b2cGoodUnitDetail', '/src/views/b2c/good/b2cGoodUnitDetail.vue', '{"isBack":true,"isHide":true}', null, '11.4.1', 13),
 
+    (70, -1, 'AI工具', 'AI Tool', 1, '/aiTool', '', '{}', 'MagicStick', '37', 905),
+    (71, 70, '图生视频', 'ImgToVideo', 1, '/aiTool/imgToVideo', '', '{}', null, '37.1', 905),
+
 
     (1000, -1, '系统管理', 'Systems', 1, '/system', '', '{}', 'Setting', '3', 12),
     (1001, 1000, '用户管理', 'Users', 1, '/system/sysUserList', '/src/views/system/sysUser/sysUserList.vue', '{}', null, '3.2', 12),

+ 11 - 0
db/sys_user_role_permission.sql

@@ -61,6 +61,17 @@ INSERT INTO sys_user_role_permission(id, parent_id, permission_name, sort) VALUE
         ('36.3', '36', 'AI短剧创作-生成图片', null),
         ('36.4', '36', 'AI短剧创作-生成视频', null),
 
+    ('37', -1, 'AI工具', 9),
+        ('37.1', '37', 'AI工具-图生视频', null),
+            ('37.1.1', '37.1', 'AI工具-创建图生视频任务', null),
+            ('37.1.2', '37.1', 'AI工具-查询图生视频任务状态', null),
+            ('37.1.3', '37.1', 'AI工具-图生视频任务列表', null),
+            ('37.1.4', '37.1', 'AI工具-图生视频任务收藏/取消收藏', null),
+            ('37.1.5', '37.1', 'AI工具-重新生成视频', null),
+
+
+
+
 
 
      ('3', -1, '系统用户管理', 900),

+ 7 - 0
db/sys_user_role_permission_relation.sql

@@ -119,6 +119,13 @@ INSERT INTO sys_user_role_permission_relation(role_id, permission_id) VALUES
             (1, '36.2.6'), (1, '36.2.6.1'),
         (1, '36.3'),
         (1, '36.4'),
+    (1, '37'),
+        (1, '37.1'),
+            (1, '37.1.1'),
+            (1, '37.1.2'),
+            (1, '37.1.3'),
+            (1, '37.1.4'),
+            (1, '37.1.5'),
 
     (1, '100'),
         (1, '101'),

+ 5 - 0
pom.xml

@@ -315,6 +315,11 @@
             <artifactId>volc-sdk-java</artifactId>
             <version>1.0.225</version>
         </dependency>
+        <dependency>
+            <groupId>com.volcengine</groupId>
+            <artifactId>volcengine-java-sdk-ark-runtime</artifactId>
+            <version>LATEST</version>
+        </dependency>
         <dependency>
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>

+ 75 - 0
src/main/java/com/backendsys/modules/ai/volcengine/controller/VolcengineVideoController.java

@@ -0,0 +1,75 @@
+package com.backendsys.modules.ai.volcengine.controller;
+
+import com.backendsys.modules.ai.volcengine.entity.*;
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoCollectService;
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoTaskService;
+import com.backendsys.modules.common.config.security.annotations.Anonymous;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
+import com.backendsys.modules.common.utils.Result;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+@AllArgsConstructor
+@Validated
+@RestController
+@Tag(name = "火山大模型 视频生成")
+public class VolcengineVideoController {
+
+    private final VolcengineVideoTaskService volcengineVideoTaskService;
+
+    private final VolcengineVideoCollectService volcengineVideoCollectService;
+
+
+    @Operation(summary = "创建图生视频任务")
+    @PreAuthorize("@sr.hasPermission('37.1.1')")
+    @PostMapping("/api/ai/volcengine/generateVideo")
+    public Result generateVideo(@Validated(VolcengineVideoTask.Generate.class) @RequestBody VolcengineVideoTask volcengineVideoTask) {
+        volcengineVideoTask.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", volcengineVideoTaskService.generateVideo(volcengineVideoTask));
+    }
+
+    @Operation(summary = "查询图生视频任务状态")
+    @PreAuthorize("@sr.hasPermission('37.1.2')")
+    @GetMapping("/api/ai/volcengine/video/getTaskStatus/{volcengineTaskId}")
+    public Result getTaskStatus(@PathVariable String volcengineTaskId) {
+        return Result.success().put("data", volcengineVideoTaskService.selectTaskStatus(volcengineTaskId));
+    }
+
+
+    @Operation(summary = "分页获取图生视频任务记录")
+    @PreAuthorize("@sr.hasPermission('37.1.3')")
+    @GetMapping("/api/ai/volcengine/video/getTaskList")
+    public Result getTaskList(VolcengineVideoTaskDTO dto) {
+        dto.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", volcengineVideoTaskService.selectTaskList(dto));
+    }
+
+    @Operation(summary = "视频收藏/取消收藏")
+    @PreAuthorize("@sr.hasPermission('37.1.4')")
+    @PutMapping("/api/ai/volcengine/video/setCollect")
+    public Result setCollect(@Validated(VolcengineVideoCollect.Collect.class) @RequestBody VolcengineVideoCollect volcengineVideoCollect) {
+        volcengineVideoCollect.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", volcengineVideoCollectService.setCollect(volcengineVideoCollect));
+    }
+
+    @Operation(summary = "重新生成视频")
+    @PreAuthorize("@sr.hasPermission('37.1.5')")
+    @PostMapping("/api/ai/volcengine/video/regenerate")
+    public Result regenerate(@Validated(VolcengineVideoTaskDetail.Regenerate.class) @RequestBody VolcengineVideoTaskDetail volcengineVideoTaskDetail) {
+        volcengineVideoTaskDetail.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", volcengineVideoTaskService.regenerate(volcengineVideoTaskDetail));
+    }
+
+    @Anonymous
+    @Operation(summary = "图生视频状态通知")
+    @PostMapping("/api/ai/volcengine/video/notify")
+    public Result notify(@Validated @RequestBody VolcengineVideoTaskNotify volcengineVideoTaskNotify) {
+        volcengineVideoTaskService.notify(volcengineVideoTaskNotify);
+        return Result.success();
+    }
+
+}

+ 9 - 0
src/main/java/com/backendsys/modules/ai/volcengine/dao/VolcengineVideoCollectDao.java

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

+ 19 - 0
src/main/java/com/backendsys/modules/ai/volcengine/dao/VolcengineVideoTaskDao.java

@@ -0,0 +1,19 @@
+package com.backendsys.modules.ai.volcengine.dao;
+
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTask;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDTO;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface VolcengineVideoTaskDao extends BaseMapper<VolcengineVideoTask> {
+
+    List<VolcengineVideoTask> selectTaskCollectList(@Param("dto") VolcengineVideoTaskDTO dto);
+
+    List<VolcengineVideoTask> selectTaskList(@Param("dto") VolcengineVideoTaskDTO dto);
+
+}

+ 9 - 0
src/main/java/com/backendsys/modules/ai/volcengine/dao/VolcengineVideoTaskDetailDao.java

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

+ 39 - 0
src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoCollect.java

@@ -0,0 +1,39 @@
+package com.backendsys.modules.ai.volcengine.entity;
+
+import com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeAdapter;
+import com.backendsys.entity.validator.RangeArray;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.google.gson.annotations.JsonAdapter;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@TableName("ai_volcengine_video_collect")
+@Data
+public class VolcengineVideoCollect {
+
+    public static interface Collect{}
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long user_id;
+
+    private Long task_id;
+
+    @NotNull(message = "任务明细id不能为空", groups = { Collect.class })
+    private Long task_detail_id;
+
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime create_time;
+
+    @TableField(exist = false)
+    @NotNull(message = "收藏状态不能为空", groups = { Collect.class })
+    @RangeArray(message="收藏状态取值有误,范围应是(-1取消收藏, 1加入收藏)", value = {"-1", "1"}, groups = { Collect.class })
+    private Integer is_collect;
+
+}

+ 97 - 0
src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTask.java

@@ -0,0 +1,97 @@
+package com.backendsys.modules.ai.volcengine.entity;
+
+import com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeAdapter;
+import com.backendsys.entity.validator.RangeArray;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.gson.annotations.JsonAdapter;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@TableName("ai_volcengine_video_task")
+@Data
+public class VolcengineVideoTask {
+
+    public static interface Query{}
+    public static interface Generate{}
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long user_id;
+
+    /**
+     * 模型
+     */
+    @NotBlank(message = "请选择模型", groups = { Generate.class })
+    private String model;
+    /**
+     * 图片地址
+     */
+    @NotBlank(message = "请上传图片", groups = { Generate.class })
+    private String img_url;
+
+    /**
+     * 分辨率 480p 720p 1080p
+     */
+    @NotBlank(message = "请选择分辨率", groups = { Generate.class })
+    private String resolution;
+
+    /**
+     * 时长 秒
+     */
+    @NotNull(message = "请选择时长", groups = { Generate.class })
+    @RangeArray(message="时长取值有误,范围应是(5, 10)", value = { "5", "10" })
+    private Integer duration;
+
+    /**
+     * 文本说明
+     */
+    @Size(max = 500, message = "提示词长度不能超过 {max} 字符", groups = { Generate.class })
+    private String text;
+
+    /**
+     * 是否固定镜头 1-是 -1-否
+     */
+    private Integer camerafixed;
+
+    /**
+     * 生成数量
+     */
+    @NotNull(message="生成数量不能为空")
+    @RangeArray(message="生成数量取值有误,范围应是(1, 2, 3, 4)", value = { "1", "2", "3", "4" }, groups = { Generate.class })
+    private Integer quantity;
+
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime create_time;
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime update_time;
+
+    @TableField(exist = false)
+    private List<VolcengineVideoTaskDetail> detail_list;
+
+    /**
+     * 是否收藏 1-是 -1-否
+     */
+    @TableField(exist = false)
+    private Integer is_collect;
+
+    /**
+     * 用户id参数,用于分页查询
+     */
+    @TableField(exist = false)
+    @JsonIgnore
+    private Long param_user_id;
+
+}

+ 33 - 0
src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTaskDTO.java

@@ -0,0 +1,33 @@
+package com.backendsys.modules.ai.volcengine.entity;
+
+import lombok.Data;
+
+@Data
+public class VolcengineVideoTaskDTO {
+
+    /**
+     * 模型
+     */
+    private String model;
+
+    /**
+     * 日期范围 0-今天 1-昨天 2-近三天 3-近5天 4-近7天 5-近14天 6-自定义
+     */
+    private Integer date_range;
+    /**
+     * 日期时间范围
+     */
+    private String create_begin_date;
+    private String create_end_date;
+
+    /**
+     * 是否只看收藏 1-是 -1-否
+     */
+    private Integer only_collections;
+
+    /**
+     * 用户ID
+     */
+    private Long user_id;
+
+}

+ 81 - 0
src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTaskDetail.java

@@ -0,0 +1,81 @@
+package com.backendsys.modules.ai.volcengine.entity;
+
+import com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeAdapter;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.google.gson.annotations.JsonAdapter;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@TableName("ai_volcengine_video_task_detail")
+@Data
+public class VolcengineVideoTaskDetail {
+
+    public interface Regenerate{}
+
+    @NotNull(message = "id不能为空", groups = { VolcengineVideoTaskDetail.Regenerate.class })
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户ID
+     */
+    private Long user_id;
+    /**
+     * 任务ID
+     */
+    private Long task_id;
+    /**
+     * 火山大模型任务ID
+     */
+    private String volcengine_task_id;
+    /**
+     * 封面图片URL
+     */
+    private String cover_img_url;
+    /**
+     * 种子
+     */
+    private Integer seed;
+    /**
+     * 任务状态
+     * queued:排队中。
+     * running:任务运行中。
+     * cancelled:取消任务,取消状态24h自动删除(只支持排队中状态的任务被取消)。
+     * succeeded: 任务成功。
+     * failed:任务失败。
+     */
+    private String status;
+    /**
+     * 视频原始URL
+     */
+    private String video_origin_url;
+    /**
+     * 视频转存URL
+     */
+    private String video_url;
+    /**
+     * 存储对象key
+     */
+    private String object_key;
+    /**
+     * 失败信息
+     */
+    private String error_msg;
+
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime create_time;
+    @JsonAdapter(LocalDateTimeAdapter.class)
+    private LocalDateTime update_time;
+
+    /**
+     * 是否收藏 1-是 -1-否
+     */
+    @TableField(exist = false)
+    private Integer is_collect;
+}

+ 71 - 0
src/main/java/com/backendsys/modules/ai/volcengine/entity/VolcengineVideoTaskNotify.java

@@ -0,0 +1,71 @@
+package com.backendsys.modules.ai.volcengine.entity;
+
+import com.volcengine.ark.runtime.model.content.generation.GetContentGenerationTaskResponse;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 火山大模型图生视频状态通知
+ */
+@Data
+public class VolcengineVideoTaskNotify {
+    /**
+     * 任务id
+     */
+    @NotBlank(message = "任务id不能为空")
+    private String id;
+    /**
+     * 模型名称
+     */
+    private String model;
+
+    /**
+     * 任务状态
+     * queued:排队中。
+     * running:任务运行中。
+     * cancelled:取消任务,取消状态24h自动删除(只支持排队中状态的任务被取消)。
+     * succeeded: 任务成功。
+     * failed:任务失败。
+     */
+    private String status;
+    /**
+     * 错误提示信息,任务成功返回null,任务失败时返回错误数据
+     */
+    private GetContentGenerationTaskResponse.ContentGenerationError error;
+
+    /**
+     * 任务创建时间的 Unix 时间戳(秒)
+     */
+    private Long created_at;
+    /**
+     * 任务当前状态更新时间的 Unix 时间戳(秒)
+     */
+    private Long updated_at;
+
+    /**
+     * 当视频生成任务完成,会输出该字段,包含生成视频下载的 URL
+     * content.video_url
+     */
+    private GetContentGenerationTaskResponse.Content content;
+    /**
+     * 种子
+     */
+    private Integer seed;
+    /**
+     * 分辨率
+     */
+    private String resolution;
+    /**
+     * 生成视频的宽高比
+     */
+    private String ratio;
+    /**
+     * 视频时长 秒
+     */
+    private Integer duration;
+    /**
+     * 生成视频的帧率
+     */
+    private Integer framespersecond;
+}

+ 10 - 0
src/main/java/com/backendsys/modules/ai/volcengine/service/VolcengineVideoCollectService.java

@@ -0,0 +1,10 @@
+package com.backendsys.modules.ai.volcengine.service;
+
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoCollect;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+public interface VolcengineVideoCollectService extends IService<VolcengineVideoCollect> {
+
+    boolean setCollect(VolcengineVideoCollect volcengineVideoCollect);
+
+}

+ 8 - 0
src/main/java/com/backendsys/modules/ai/volcengine/service/VolcengineVideoTaskDetailService.java

@@ -0,0 +1,8 @@
+package com.backendsys.modules.ai.volcengine.service;
+
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+public interface VolcengineVideoTaskDetailService extends IService<VolcengineVideoTaskDetail> {
+
+}

+ 21 - 0
src/main/java/com/backendsys/modules/ai/volcengine/service/VolcengineVideoTaskService.java

@@ -0,0 +1,21 @@
+package com.backendsys.modules.ai.volcengine.service;
+
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTask;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDTO;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskNotify;
+import com.backendsys.utils.response.PageEntity;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+public interface VolcengineVideoTaskService extends IService<VolcengineVideoTask> {
+
+    VolcengineVideoTask generateVideo(VolcengineVideoTask volcengineVideoTask);
+
+    VolcengineVideoTaskDetail selectTaskStatus(String volcengineTaskId);
+
+    PageEntity selectTaskList(VolcengineVideoTaskDTO dto);
+
+    VolcengineVideoTaskDetail regenerate(VolcengineVideoTaskDetail volcengineVideoTaskDetail);
+
+    void notify(VolcengineVideoTaskNotify volcengineVideoTaskNotify);
+}

+ 56 - 0
src/main/java/com/backendsys/modules/ai/volcengine/service/impl/VolcengineVideoCollectServiceImpl.java

@@ -0,0 +1,56 @@
+package com.backendsys.modules.ai.volcengine.service.impl;
+
+import com.backendsys.exception.CustException;
+import com.backendsys.modules.ai.volcengine.dao.VolcengineVideoCollectDao;
+import com.backendsys.modules.ai.volcengine.dao.VolcengineVideoTaskDetailDao;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoCollect;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail;
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoCollectService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class VolcengineVideoCollectServiceImpl extends ServiceImpl<VolcengineVideoCollectDao, VolcengineVideoCollect> implements VolcengineVideoCollectService {
+
+    @Autowired
+    private VolcengineVideoTaskDetailDao volcengineVideoTaskDetailDao;
+
+    @Override
+    public boolean setCollect(VolcengineVideoCollect volcengineVideoCollect) {
+
+        // 是否收藏 (-1取消收藏, 1加入收藏)
+        Integer isCollect = volcengineVideoCollect.getIs_collect();
+        Long taskDetailId = volcengineVideoCollect.getTask_detail_id();
+
+        // 查询当前用户,是否已经收藏过该风格
+        LambdaQueryWrapper<VolcengineVideoCollect> wrapperCollect = new LambdaQueryWrapper<>();
+        wrapperCollect.eq(VolcengineVideoCollect::getUser_id, volcengineVideoCollect.getUser_id());
+        wrapperCollect.eq(VolcengineVideoCollect::getTask_detail_id, taskDetailId);
+
+        if (isCollect == 1) {
+            // 如果已经收藏过,则不变
+            VolcengineVideoCollect detail = baseMapper.selectOne(wrapperCollect);
+            if (detail == null) {
+
+                // [DB] 判断任务是否存在
+                VolcengineVideoTaskDetail taskDetail = volcengineVideoTaskDetailDao.selectOne(Wrappers.<VolcengineVideoTaskDetail>lambdaQuery().eq(VolcengineVideoTaskDetail::getId, taskDetailId));
+                if (taskDetail == null) {
+                    throw new CustException("视频不存在");
+                }
+                volcengineVideoCollect.setTask_id(taskDetail.getTask_id());
+
+                // [DB] 加入收藏
+                baseMapper.insert(volcengineVideoCollect);
+            }
+        } else {
+            // [DB] 取消收藏
+            baseMapper.delete(wrapperCollect);
+        }
+
+        return true;
+    }
+
+}

+ 12 - 0
src/main/java/com/backendsys/modules/ai/volcengine/service/impl/VolcengineVideoTaskDetailServiceImpl.java

@@ -0,0 +1,12 @@
+package com.backendsys.modules.ai.volcengine.service.impl;
+
+import com.backendsys.modules.ai.volcengine.dao.VolcengineVideoTaskDetailDao;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail;
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoTaskDetailService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+@Service
+public class VolcengineVideoTaskDetailServiceImpl extends ServiceImpl<VolcengineVideoTaskDetailDao, VolcengineVideoTaskDetail> implements VolcengineVideoTaskDetailService {
+
+}

+ 357 - 0
src/main/java/com/backendsys/modules/ai/volcengine/service/impl/VolcengineVideoTaskServiceImpl.java

@@ -0,0 +1,357 @@
+package com.backendsys.modules.ai.volcengine.service.impl;
+
+import com.backendsys.exception.CustException;
+import com.backendsys.modules.ai.volcengine.dao.VolcengineVideoTaskDao;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTask;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDTO;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail;
+import com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskNotify;
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoTaskDetailService;
+import com.backendsys.modules.ai.volcengine.service.VolcengineVideoTaskService;
+import com.backendsys.modules.common.config.security.utils.SecurityUtil;
+import com.backendsys.modules.upload.entity.SysFileResult;
+import com.backendsys.modules.upload.service.SysFileService;
+import com.backendsys.utils.response.PageEntity;
+import com.backendsys.utils.response.PageInfoResult;
+import com.backendsys.utils.v2.PageUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.volcengine.ark.runtime.model.content.generation.CreateContentGenerationTaskRequest;
+import com.volcengine.ark.runtime.model.content.generation.CreateContentGenerationTaskResult;
+import com.volcengine.ark.runtime.model.content.generation.GetContentGenerationTaskRequest;
+import com.volcengine.ark.runtime.model.content.generation.GetContentGenerationTaskResponse;
+import com.volcengine.ark.runtime.service.ArkService;
+import okhttp3.ConnectionPool;
+import okhttp3.Dispatcher;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class VolcengineVideoTaskServiceImpl extends ServiceImpl<VolcengineVideoTaskDao, VolcengineVideoTask> implements VolcengineVideoTaskService {
+
+    @Autowired
+    private VolcengineVideoTaskDetailService volcengineVideoTaskDetailService;
+
+    @Autowired
+    private SysFileService sysFileService;
+
+    @Value("${volcengine.api-key}")
+    private String apiKey;
+
+    @Value("${domain}")
+    private String domain;
+
+    private final ConnectionPool connectionPool = new ConnectionPool(20, 5, TimeUnit.MINUTES);
+    private final Dispatcher dispatcher = new Dispatcher();
+    private ArkService service;
+
+    @PostConstruct
+    public void init() {
+        this.service = ArkService.builder()
+                .dispatcher(dispatcher)
+                .connectionPool(connectionPool)
+                .apiKey(apiKey)
+                .build();
+    }
+
+    @Transactional
+    @Override
+    public VolcengineVideoTask generateVideo(VolcengineVideoTask volcengineVideoTask) {
+        // 生成任务主表记录
+        this.baseMapper.insert(volcengineVideoTask);
+
+        List<CreateContentGenerationTaskRequest.Content> contents = new ArrayList<>();
+
+        // 图生视频功能 构建请求参数
+        // 文本提示词与参数组合
+        if (StringUtils.isBlank(volcengineVideoTask.getText())) {
+            volcengineVideoTask.setText("");
+        }
+        StringBuilder text = new StringBuilder(volcengineVideoTask.getText());
+        if(StringUtils.isNotBlank(volcengineVideoTask.getResolution())) {
+            text.append(" --rs ").append(volcengineVideoTask.getResolution());
+        }
+        if(volcengineVideoTask.getDuration() != null) {
+            text.append(" --dur ").append(volcengineVideoTask.getDuration());
+        }
+        if(volcengineVideoTask.getCamerafixed() != null) {
+            if (volcengineVideoTask.getCamerafixed() == 1) {
+                text.append(" --cf true");
+            } else {
+                text.append(" --cf false");
+            }
+        }
+
+        contents.add(CreateContentGenerationTaskRequest.Content.builder()
+                .type("text")
+                .text(text.toString())
+                .build());
+
+        // 首帧图片 (若仅需使用文本生成视频功能,可将此部分内容进行注释处理。)
+        contents.add(CreateContentGenerationTaskRequest.Content.builder()
+                .type("image_url")
+                .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+                        .url(volcengineVideoTask.getImg_url())
+                        .build())
+                .build());
+        String notifyUrl = domain+ "/api/ai/volcengine/video/notify";
+        // 创建视频生成任务
+        CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+                .model(volcengineVideoTask.getModel())
+                .content(contents)
+                // 回调地址
+                .callbackUrl(notifyUrl)
+                .build();
+
+        //根据数量生成任务,记录明细
+        List<VolcengineVideoTaskDetail> detailList = new ArrayList<>();
+        int num = 0;
+        while (volcengineVideoTask.getQuantity() > num) {
+
+            VolcengineVideoTaskDetail volcengineVideoTaskDetail = new VolcengineVideoTaskDetail();
+            volcengineVideoTaskDetail.setTask_id(volcengineVideoTask.getId());
+            volcengineVideoTaskDetail.setUser_id(volcengineVideoTask.getUser_id());
+            volcengineVideoTaskDetail.setCover_img_url(volcengineVideoTask.getImg_url());
+            volcengineVideoTaskDetail.setStatus("queued");
+            // 创建图生视频任务
+            CreateContentGenerationTaskResult taskResult = service.createContentGenerationTask(createRequest);
+
+            volcengineVideoTaskDetail.setVolcengine_task_id(taskResult.getId());
+            detailList.add(volcengineVideoTaskDetail);
+            num++;
+        }
+        if(!detailList.isEmpty()){
+            volcengineVideoTaskDetailService.saveBatch(detailList);
+            volcengineVideoTask.setDetail_list(detailList);
+        }
+
+        return volcengineVideoTask;
+    }
+
+    @Override
+    public VolcengineVideoTaskDetail selectTaskStatus(String volcengineTaskId) {
+
+        VolcengineVideoTaskDetail volcengineVideoTaskDetail = volcengineVideoTaskDetailService.getOne(Wrappers.<VolcengineVideoTaskDetail>lambdaQuery()
+                .eq(VolcengineVideoTaskDetail::getVolcengine_task_id, volcengineTaskId));
+        if(volcengineVideoTaskDetail == null) {
+            throw new CustException("找不到该图生视频信息,请检查参数是否有误");
+        }
+        // 成功或失败的直接返回
+        if("succeeded".equals(volcengineVideoTaskDetail.getStatus()) || "failed".equals(volcengineVideoTaskDetail.getStatus())) {
+            return volcengineVideoTaskDetail;
+        }
+
+        GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+                .taskId(volcengineTaskId)
+                .build();
+
+        GetContentGenerationTaskResponse response = service.getContentGenerationTask(getRequest);
+        // 更新任务明细
+        volcengineVideoTaskDetail.setStatus(response.getStatus());
+        if(response.getSeed() != null) {
+            volcengineVideoTaskDetail.setSeed(response.getSeed().intValue());
+        }
+        if("succeeded".equals(response.getStatus())) {
+            // 成功,保存视频url
+            String videoOriginUrl = response.getContent().getVideoUrl();
+            volcengineVideoTaskDetail.setVideo_origin_url(videoOriginUrl);
+
+            // URL转存文件
+            SysFileResult sysFileResult = sysFileService.urlToUploadFile(videoOriginUrl, SecurityUtil.getUserId());
+            String objectKey = sysFileResult.getKey();
+            String videoUrl = sysFileResult.getDomain() + "/" + sysFileResult.getKey();
+            volcengineVideoTaskDetail.setVideo_url(videoUrl);
+            volcengineVideoTaskDetail.setObject_key(objectKey);
+        } else {
+            if(response.getError() !=null) {
+                volcengineVideoTaskDetail.setError_msg(response.getError().toString());
+            }
+        }
+
+        volcengineVideoTaskDetailService.updateById(volcengineVideoTaskDetail);
+
+        return volcengineVideoTaskDetail;
+
+    }
+
+    @Override
+    public PageEntity selectTaskList(VolcengineVideoTaskDTO dto) {
+        PageUtils.startPage();
+        List<VolcengineVideoTask> list = new ArrayList<>();
+        // 时间范围
+        if(dto.getDate_range() != null) {
+            String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+            switch (dto.getDate_range()) {
+                case 0:
+                    // 今天
+                    dto.setCreate_begin_date(today);
+                    dto.setCreate_end_date(today);
+                    break;
+                case 1:
+                    // 昨天
+                    String yesterday = LocalDate.now().minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+                    dto.setCreate_begin_date(yesterday);
+                    dto.setCreate_end_date(yesterday);
+                    break;
+                case 2:
+                    // 近3天
+                    dto.setCreate_begin_date(LocalDate.now().minusDays(2).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    dto.setCreate_end_date(today);
+                    break;
+                case 3:
+                    // 近5天
+                    dto.setCreate_begin_date(LocalDate.now().minusDays(4).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    dto.setCreate_end_date(today);
+                    break;
+                case 4:
+                    // 近7天
+                    dto.setCreate_begin_date(LocalDate.now().minusDays(6).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    dto.setCreate_end_date(today);
+                    break;
+                case 5:
+                    // 近14天
+                    dto.setCreate_begin_date(LocalDate.now().minusDays(13).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    dto.setCreate_end_date(today);
+                    break;
+            }
+        }
+        if(dto.getOnly_collections() != null && dto.getOnly_collections() == 1){
+            // 只看收藏
+            list = baseMapper.selectTaskCollectList(dto);
+        } else {
+            list = baseMapper.selectTaskList(dto);
+        }
+        return new PageInfoResult(list).toEntity();
+    }
+
+    @Override
+    public VolcengineVideoTaskDetail regenerate(VolcengineVideoTaskDetail volcengineVideoTaskDetail) {
+
+        VolcengineVideoTaskDetail dataBean = volcengineVideoTaskDetailService.getById(volcengineVideoTaskDetail.getId());
+        if(dataBean == null) {
+            throw new CustException("任务不存在");
+        }
+        VolcengineVideoTask volcengineVideoTask = this.getById(dataBean.getTask_id());
+        if (volcengineVideoTask == null) {
+            throw new CustException("任务不存在");
+        }
+        if(!"failed".equals(dataBean.getStatus())) {
+            throw new CustException("只有失败状态可进行重试");
+        }
+
+        List<CreateContentGenerationTaskRequest.Content> contents = new ArrayList<>();
+
+        // 图生视频功能 构建请求参数
+        // 文本提示词与参数组合
+        if (StringUtils.isBlank(volcengineVideoTask.getText())) {
+            volcengineVideoTask.setText("");
+        }
+        StringBuilder text = new StringBuilder(volcengineVideoTask.getText());
+        if(StringUtils.isNotBlank(volcengineVideoTask.getResolution())) {
+            text.append(" --rs ").append(volcengineVideoTask.getResolution());
+        }
+        if(volcengineVideoTask.getDuration() != null) {
+            text.append(" --dur ").append(volcengineVideoTask.getDuration());
+        }
+        if(volcengineVideoTask.getCamerafixed() != null) {
+            if (volcengineVideoTask.getCamerafixed() == 1) {
+                text.append(" --cf true");
+            } else {
+                text.append(" --cf false");
+            }
+        }
+
+        contents.add(CreateContentGenerationTaskRequest.Content.builder()
+                .type("text")
+                .text(text.toString())
+                .build());
+
+
+        // 首帧图片 (若仅需使用文本生成视频功能,可将此部分内容进行注释处理。)
+        contents.add(CreateContentGenerationTaskRequest.Content.builder()
+                .type("image_url")
+                .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+                        .url(volcengineVideoTask.getImg_url())
+                        .build())
+                .build());
+
+        String notifyUrl = domain+ "/api/ai/volcengine/video/notify";
+        // 创建视频生成任务
+        CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+                .model(volcengineVideoTask.getModel())
+                .content(contents)
+                // 回调地址
+                .callbackUrl(notifyUrl)
+                .build();
+
+        dataBean.setUser_id(volcengineVideoTaskDetail.getUser_id());
+        dataBean.setStatus("queued");
+        // 创建图生视频任务
+        CreateContentGenerationTaskResult taskResult = service.createContentGenerationTask(createRequest);
+
+        dataBean.setVolcengine_task_id(taskResult.getId());
+
+        volcengineVideoTaskDetailService.updateById(dataBean);
+
+        return dataBean;
+
+    }
+
+    @Override
+    public void notify(VolcengineVideoTaskNotify notify) {
+
+        VolcengineVideoTaskDetail dataBean = volcengineVideoTaskDetailService.getOne(Wrappers.<VolcengineVideoTaskDetail>lambdaQuery()
+                .eq(VolcengineVideoTaskDetail::getVolcengine_task_id, notify.getId()));
+        if (dataBean == null) {
+            // 任务状态排队/运行中,可能速度快于平台数据库保存,可忽略,其他状态视为异常
+            if(!Arrays.asList("queued","running").contains(notify.getStatus())){
+                log.error("图生视频通知异常,找不到任务明细,通知内容:" + notify);
+                throw new CustException("图生视频通知异常,找不到任务明细");
+            } else {
+                return;
+            }
+        }
+
+        if("succeeded".equals(dataBean.getStatus()) || "failed".equals(dataBean.getStatus())) {
+            // 任务已经完成,不再处理
+            return;
+        }
+
+        // 任务状态
+        dataBean.setStatus(notify.getStatus());
+
+        if(notify.getSeed() != null) {
+            dataBean.setSeed(notify.getSeed());
+        }
+
+        if("succeeded".equals(notify.getStatus())) {
+            // 成功
+            String videoOriginUrl = notify.getContent().getVideoUrl();
+            dataBean.setVideo_origin_url(videoOriginUrl);
+
+            // URL转存文件
+            SysFileResult sysFileResult = sysFileService.urlToUploadFile(videoOriginUrl, dataBean.getUser_id());
+            String objectKey = sysFileResult.getKey();
+            String videoUrl = sysFileResult.getDomain() + "/" + sysFileResult.getKey();
+            dataBean.setVideo_url(videoUrl);
+            dataBean.setObject_key(objectKey);
+        } else {
+            if(notify.getError() !=null) {
+                dataBean.setError_msg(notify.getError().toString());
+            }
+        }
+        // 更新任务信息
+        volcengineVideoTaskDetailService.updateById(dataBean);
+    }
+}

+ 26 - 1
src/main/java/com/backendsys/modules/sdk/volcengine/controller/VolcengineDemoController.java

@@ -4,6 +4,8 @@ import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.common.utils.Result;
 import com.backendsys.modules.sdk.volcengine.entity.VisualFaceFusionMovie;
 import com.backendsys.modules.sdk.volcengine.entity.VisualFaceSwapV2;
+import com.backendsys.modules.sdk.volcengine.entity.VolcengineImage2VideoParams;
+import com.backendsys.modules.sdk.volcengine.service.VolcengineImage2VideoService;
 import com.backendsys.modules.sdk.volcengine.service.VolcengineService;
 import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -18,6 +20,9 @@ public class VolcengineDemoController {
     @Autowired
     private VolcengineService volcengineService;
 
+    @Autowired
+    private VolcengineImage2VideoService volcengineImage2VideoService;
+
     @Anonymous
     @Operation(summary = "人像融合3.6(最新版)(图像)")
     @PostMapping("/api/volcegine/faceSwapV2")
@@ -40,6 +45,26 @@ public class VolcengineDemoController {
     public Result faceFusionMovieGetResult(String task_id) {
         return Result.success().put("data", volcengineService.faceFusionMovieGetResult(task_id));
     }
-    //
+
+    @Anonymous
+    @Operation(summary = "【图生视频】创建任务")
+    @PostMapping("/api/volcegine/image2Video")
+    public Result image2Video(@RequestBody VolcengineImage2VideoParams params) {
+        return Result.success().put("data", volcengineImage2VideoService.image2Video(params));
+    }
+
+    @Anonymous
+    @Operation(summary = "【图生视频】查询任务(单个)")
+    @GetMapping("/api/volcegine/queryImage2VideoTask")
+    public Result queryImage2VideoTask(String taskId) {
+        return Result.success().put("data", volcengineImage2VideoService.queryImage2VideoTask(taskId));
+    }
+
+    @Anonymous
+    @Operation(summary = "【图生视频】查询任务列表")
+    @GetMapping("/api/volcegine/queryImage2VideoTaskList")
+    public Result queryImage2VideoTaskList(String taskIds) {
+        return Result.success().put("data", volcengineImage2VideoService.queryImage2VideoTaskList(taskIds));
+    }
 
 }

+ 41 - 0
src/main/java/com/backendsys/modules/sdk/volcengine/entity/VolcengineImage2VideoParams.java

@@ -0,0 +1,41 @@
+package com.backendsys.modules.sdk.volcengine.entity;
+
+import lombok.Data;
+
+/**
+ * 图生视频 创建任务
+ */
+@Data
+public class VolcengineImage2VideoParams {
+
+    private String model;
+    /**
+     * 图片地址
+     */
+    private String imageUrl;
+    /**
+     * 文本说明
+     */
+    private String text;
+
+    /**
+     * 分辨率 480p 720p 1080p
+     */
+    private String resolution;
+
+    /**
+     * 视频时长 单位秒
+     */
+    private Integer duration;
+
+    /**
+     * 摄像头固定 1-是 0-否
+     */
+    private Integer camerafixed;
+
+    /**
+     * 种子
+     */
+//    private Integer seed;
+
+}

+ 11 - 0
src/main/java/com/backendsys/modules/sdk/volcengine/service/VolcengineImage2VideoService.java

@@ -1,4 +1,15 @@
 package com.backendsys.modules.sdk.volcengine.service;
 
+import com.backendsys.modules.sdk.volcengine.entity.VolcengineImage2VideoParams;
+import com.volcengine.ark.runtime.model.content.generation.CreateContentGenerationTaskResult;
+import com.volcengine.ark.runtime.model.content.generation.GetContentGenerationTaskResponse;
+import com.volcengine.ark.runtime.model.content.generation.ListContentGenerationTasksResponse;
+
 public interface VolcengineImage2VideoService {
+
+    CreateContentGenerationTaskResult image2Video(VolcengineImage2VideoParams params);
+
+    GetContentGenerationTaskResponse queryImage2VideoTask(String taskId);
+
+    ListContentGenerationTasksResponse queryImage2VideoTaskList(String taskIds);
 }

+ 103 - 0
src/main/java/com/backendsys/modules/sdk/volcengine/service/impl/VolcengineImage2VideoServiceImpl.java

@@ -0,0 +1,103 @@
+package com.backendsys.modules.sdk.volcengine.service.impl;
+
+import com.backendsys.modules.sdk.volcengine.entity.VolcengineImage2VideoParams;
+import com.backendsys.modules.sdk.volcengine.service.VolcengineImage2VideoService;
+import com.volcengine.ark.runtime.model.content.generation.*;
+import com.volcengine.ark.runtime.service.ArkService;
+import okhttp3.ConnectionPool;
+import okhttp3.Dispatcher;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class VolcengineImage2VideoServiceImpl implements VolcengineImage2VideoService {
+
+
+    static String API_KEY = "ea19ba93-41fa-4bd9-90ec-6b203be84e33";
+    static ConnectionPool connectionPool = new ConnectionPool(5, 1, TimeUnit.SECONDS);
+    static Dispatcher dispatcher = new Dispatcher();
+    static ArkService service = ArkService.builder()
+            .dispatcher(dispatcher)
+            .connectionPool(connectionPool)
+            .apiKey(API_KEY)
+            .build();
+
+    @Override
+    public CreateContentGenerationTaskResult image2Video(VolcengineImage2VideoParams params) {
+//        String model = "doubao-seedance-1-0-pro-250528";
+
+        List<CreateContentGenerationTaskRequest.Content> contents = new ArrayList<>();
+
+        // 图生视频功能
+        // 文本提示词与参数组合
+        if (StringUtils.isBlank(params.getText())) {
+            params.setText("");
+        }
+
+        StringBuilder text = new StringBuilder(params.getText());
+        if(StringUtils.isNotBlank(params.getResolution())) {
+            text.append(" --rs ").append(params.getResolution());
+        }
+        if(params.getDuration() != null) {
+            text.append(" --dur ").append(params.getDuration());
+        }
+        if(params.getCamerafixed() != null) {
+            if (params.getCamerafixed() == 1) {
+                text.append(" --cf true");
+            } else {
+                text.append(" --cf false");
+            }
+        }
+
+        contents.add(CreateContentGenerationTaskRequest.Content.builder()
+                .type("text")
+                .text(text.toString())
+                .build());
+
+
+        // 首帧图片 (若仅需使用文本生成视频功能,可将此部分内容进行注释处理。)
+        contents.add(CreateContentGenerationTaskRequest.Content.builder()
+                .type("image_url")
+                .imageUrl(CreateContentGenerationTaskRequest.ImageUrl.builder()
+                        .url(params.getImageUrl())
+                        .build())
+                .build());
+        // 创建视频生成任务
+        CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
+                .model(params.getModel())
+                .content(contents)
+                .build();
+
+        return service.createContentGenerationTask(createRequest);
+
+    }
+
+    @Override
+    public GetContentGenerationTaskResponse queryImage2VideoTask(String taskId) {
+
+        GetContentGenerationTaskRequest getRequest = GetContentGenerationTaskRequest.builder()
+                .taskId(taskId)
+                .build();
+
+        return service.getContentGenerationTask(getRequest);
+
+    }
+
+    @Override
+    public ListContentGenerationTasksResponse queryImage2VideoTaskList(String taskIds) {
+        List<String> taskIdList = Arrays.asList(taskIds.split( ","));
+        ListContentGenerationTasksRequest req = ListContentGenerationTasksRequest.builder()
+                .taskIds(taskIdList)
+                .pageNum(1)
+                .pageSize(taskIdList.size())
+                .build();
+
+        return service.listContentGenerationTasks(req);
+    }
+
+}

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

@@ -216,4 +216,11 @@ comfyui:
   ports: 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007
   token: $2b$12$.MR4qGaFetN1FPQzbfyIrehsyjnPJ12xAZhR/l7KZpLkUPQTCG4gy
   is-save: true
-  # queue-key: comfyui:queue
+  # queue-key: comfyui:queue
+
+# 火山大模型
+volcengine:
+  api-key: ea19ba93-41fa-4bd9-90ec-6b203be84e33
+
+# 接口域名(用于通知)
+domain: https://dev.manage.daogu.ai

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

@@ -226,4 +226,11 @@ comfyui:
   ports: 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007
   token: $2b$12$.MR4qGaFetN1FPQzbfyIrehsyjnPJ12xAZhR/l7KZpLkUPQTCG4gy
   is-save: false
-  # queue-key: comfyui:queue
+  # queue-key: comfyui:queue
+
+# 火山大模型
+volcengine:
+  api-key: ea19ba93-41fa-4bd9-90ec-6b203be84e33
+
+# 接口域名(用于通知)
+domain: https://dev.manage.daogu.ai

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

@@ -217,4 +217,11 @@ comfyui:
   ports: 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007
   token: $2b$12$.MR4qGaFetN1FPQzbfyIrehsyjnPJ12xAZhR/l7KZpLkUPQTCG4gy
   is-save: true
-  # queue-key: comfyui:queue
+  # queue-key: comfyui:queue
+
+# 火山大模型
+volcengine:
+  api-key: ea19ba93-41fa-4bd9-90ec-6b203be84e33
+
+# 接口域名(用于通知)
+domain: https://manage.daogu.ai

+ 177 - 0
src/main/resources/mapper/ai/volcengine/VolcengineVideoTaskDao.xml

@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.backendsys.modules.ai.volcengine.dao.VolcengineVideoTaskDao">
+
+    <sql id="includeVolcengineVideoTask">
+        avvt.id,
+        avvt.user_id,
+        avvt.model,
+        avvt.img_url,
+        avvt.resolution,
+        avvt.duration,
+        avvt.`text`,
+        avvt.camerafixed,
+        avvt.quantity,
+        avvt.create_time,
+        avvt.update_time
+    </sql>
+
+    <sql id="includeVolcengineVideoTaskDetail">
+        avvtd.id,
+        avvtd.user_id,
+        avvtd.task_id,
+        avvtd.volcengine_task_id,
+        avvtd.cover_img_url,
+        avvtd.seed,
+        avvtd.status,
+        avvtd.video_origin_url,
+        avvtd.video_url,
+        avvtd.object_key,
+        avvtd.error_msg,
+        avvtd.create_time,
+        avvtd.update_time
+    </sql>
+
+    <!-- 基础映射 -->
+    <resultMap id="baseTaskMap" type="com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTask">
+        <id property="id" column="id"/>
+        <result property="user_id" column="user_id"/>
+        <result property="model" column="model"  />
+        <result property="img_url" column="img_url"  />
+        <result property="resolution" column="resolution" />
+        <result property="duration" column="duration" />
+        <result property="text" column="text" />
+        <result property="camerafixed" column="camerafixed" />
+        <result property="quantity" column="quantity" />
+        <result property="is_collect" column="is_collect" />
+        <result property="create_time" column="create_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+        <result property="update_time" column="update_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+        <result property="param_user_id" column="param_user_id"/>
+    </resultMap>
+
+    <!-- 任务映射 -->
+    <resultMap id="resultMapTask" type="com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTask" extends="baseTaskMap">
+        <collection select="queryDetailById" property="detail_list" javaType="java.util.List"
+                    ofType="com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail" column="{id=id, param_user_id=param_user_id}">
+            <id property="id" column="id" />
+            <result property="user_id" column="user_id" />
+            <result property="task_id" column="task_id" />
+            <result property="volcengine_task_id" column="volcengine_task_id" />
+            <result property="cover_img_url" column="cover_img_url" />
+            <result property="seed" column="seed" />
+            <result property="status" column="status" />
+            <result property="video_origin_url" column="video_origin_url" />
+            <result property="video_url" column="video_url"/>
+            <result property="object_key" column="object_key"/>
+            <result property="error_msg" column="error_msg" />
+            <result property="create_time" column="create_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+            <result property="update_time" column="update_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+            <result property="is_collect" column="is_collect" />
+        </collection>
+    </resultMap>
+
+    <!-- 收藏任务映射 -->
+    <resultMap id="resultMapTaskCollect" type="com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTask" extends="baseTaskMap">
+        <collection select="queryDetailCollectById" property="detail_list" javaType="java.util.List"
+                    ofType="com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail" column="{id=id, param_user_id=param_user_id}">
+            <id property="id" column="id" />
+            <result property="user_id" column="user_id" />
+            <result property="task_id" column="task_id" />
+            <result property="volcengine_task_id" column="volcengine_task_id" />
+            <result property="cover_img_url" column="cover_img_url" />
+            <result property="seed" column="seed" />
+            <result property="status" column="status" />
+            <result property="video_origin_url" column="video_origin_url" />
+            <result property="video_url" column="video_url"/>
+            <result property="object_key" column="object_key"/>
+            <result property="error_msg" column="error_msg" />
+            <result property="create_time" column="create_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+            <result property="update_time" column="update_time" typeHandler="com.backendsys.config.Mybatis.handler.timezone.LocalDateTimeHandler" />
+            <result property="is_collect" column="is_collect" />
+        </collection>
+    </resultMap>
+
+    <!-- 根据用户id 获取任务列表,标记用户是否收藏 -->
+    <select id="selectTaskList" resultMap="resultMapTask">
+        SELECT
+            <include refid="includeVolcengineVideoTask" />
+            , IF(avc.task_id IS NOT NULL, 1, -1) AS is_collect,
+            #{dto.user_id} as param_user_id
+        FROM ai_volcengine_video_task avvt
+        LEFT JOIN (
+            SELECT
+                DISTINCT task_id
+            FROM ai_volcengine_video_collect
+            WHERE user_id = #{dto.user_id}
+        ) avc ON avc.task_id = avvt.id
+        <where>
+            <if test="dto.model != null and dto.model != ''">
+                AND avvt.model = #{dto.model}
+            </if>
+            <if test="dto.create_begin_date != null and dto.create_begin_date != ''">
+                AND avvt.create_time >= STR_TO_DATE(#{dto.create_begin_date}, '%Y-%m-%d')
+            </if>
+            <if test="dto.create_end_date != null and dto.create_end_date != ''">
+                AND avvt.create_time &lt; DATE_ADD(STR_TO_DATE(#{dto.create_end_date}, '%Y-%m-%d'), INTERVAL 1 DAY)
+            </if>
+        </where>
+        ORDER BY create_time DESC
+    </select>
+
+    <!-- 根据任务id和用户id 查询任务明细视频,标记用户是否收藏 -->
+    <select id="queryDetailById" resultType="com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail">
+        SELECT
+            <include refid="includeVolcengineVideoTaskDetail" />
+            , IF(avc.task_id IS NOT NULL, 1, -1) AS is_collect
+        FROM ai_volcengine_video_task_detail avvtd
+        LEFT JOIN ai_volcengine_video_collect avc
+            ON avc.task_id = avvtd.task_id
+            AND avc.task_detail_id = avvtd.id
+            AND avc.user_id = #{param_user_id}
+        WHERE avvtd.task_id = #{id}
+        ORDER BY update_time DESC
+    </select>
+
+    <!-- 根据用户id 获取用户已收藏的任务列表 -->
+    <select id="selectTaskCollectList"
+            resultMap="resultMapTaskCollect">
+        SELECT
+               <include refid="includeVolcengineVideoTask" />
+               , 1 AS is_collect,
+                #{dto.user_id} as param_user_id
+        FROM ai_volcengine_video_task avvt
+        INNER JOIN (
+            SELECT
+                DISTINCT task_id
+            FROM ai_volcengine_video_collect
+            WHERE user_id = #{dto.user_id}
+        ) avc ON avc.task_id = avvt.id
+        <where>
+            <if test="dto.model != null and dto.model != ''">
+                AND avvt.model = #{dto.model}
+            </if>
+            <if test="dto.create_begin_date != null and dto.create_begin_date != ''">
+                AND avvt.create_time >= STR_TO_DATE(#{dto.create_begin_date}, '%Y-%m-%d')
+            </if>
+            <if test="dto.create_end_date != null and dto.create_end_date != ''">
+                AND avvt.create_time &lt; DATE_ADD(STR_TO_DATE(#{dto.create_end_date}, '%Y-%m-%d'), INTERVAL 1 DAY)
+            </if>
+        </where>
+        ORDER BY create_time DESC
+    </select>
+
+    <!-- 根据任务id和用户id 查询收藏的任务明细视频 -->
+    <select id="queryDetailCollectById" resultType="com.backendsys.modules.ai.volcengine.entity.VolcengineVideoTaskDetail">
+        SELECT
+               <include refid="includeVolcengineVideoTaskDetail" />
+               , 1 AS is_collect
+        FROM ai_volcengine_video_task_detail avvtd
+        INNER JOIN ai_volcengine_video_collect avc
+            ON avc.task_id = avvtd.task_id
+            AND avc.task_detail_id = avvtd.id
+            AND avc.user_id = #{param_user_id}
+        WHERE avvtd.task_id = #{id}
+        ORDER BY update_time DESC
+    </select>
+
+</mapper>