Parcourir la source

视频生成素材生成

wangyunpeng il y a 2 semaines
Parent
commit
964e1dd2c8

+ 96 - 0
core/src/main/java/com/tzld/supply/dao/mapper/supply/spider/ProduceVideoMaterialMapper.java

@@ -0,0 +1,96 @@
+package com.tzld.supply.dao.mapper.supply.spider;
+
+import com.tzld.supply.model.po.supply.spider.ProduceVideoMaterial;
+import com.tzld.supply.model.po.supply.spider.ProduceVideoMaterialExample;
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+
+public interface ProduceVideoMaterialMapper {
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    long countByExample(ProduceVideoMaterialExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int deleteByExample(ProduceVideoMaterialExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int deleteByPrimaryKey(String id);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int insert(ProduceVideoMaterial record);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int insertSelective(ProduceVideoMaterial record);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    List<ProduceVideoMaterial> selectByExample(ProduceVideoMaterialExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    ProduceVideoMaterial selectByPrimaryKey(String id);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int updateByExampleSelective(@Param("record") ProduceVideoMaterial record, @Param("example") ProduceVideoMaterialExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int updateByExample(@Param("record") ProduceVideoMaterial record, @Param("example") ProduceVideoMaterialExample example);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int updateByPrimaryKeySelective(ProduceVideoMaterial record);
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    int updateByPrimaryKey(ProduceVideoMaterial record);
+}

+ 2 - 22
core/src/main/java/com/tzld/supply/job/ContentMediaSearchJob.java

@@ -1,8 +1,6 @@
 package com.tzld.supply.job;
 
 import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.http.HttpUtil;
-import com.stuuudy.commons.external.filestorage.enums.EnumPublicBuckets;
 import com.tzld.supply.api.DangerFaceRecognizeService;
 import com.tzld.supply.api.SpiderApiService;
 import com.tzld.supply.common.enums.FindFaceStatusEnum;
@@ -15,7 +13,6 @@ import com.tzld.supply.model.entity.ContentSearchResponse;
 import com.tzld.supply.model.po.supply.spider.SpiderContent;
 import com.tzld.supply.model.po.supply.spider.SpiderContentMedia;
 import com.tzld.supply.util.AliOssFileTool;
-import com.tzld.supply.util.CdnUtil;
 import com.tzld.supply.util.DateUtils;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.handler.annotation.XxlJob;
@@ -24,7 +21,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -65,7 +61,7 @@ public class ContentMediaSearchJob {
             for (ContentSearchResponse.DataItem dataItem : dataItems) {
                 // 2. 调用saveInPublic转存到OSS
                 String fileName = String.format("image/%s_%d.jpg", content.getId(), System.currentTimeMillis());
-                String fileUrl = downloadAndSaveInOSS(fileName, dataItem.getUrl(), "image/jpeg");
+                String fileUrl = AliOssFileTool.downloadAndSaveInOSS(fileName, dataItem.getUrl(), "image/jpeg");
                 if (StringUtils.isBlank(fileUrl)) {
                     log.warn("图片转存OSS失败,URL: {}", dataItem.getUrl());
                     continue;
@@ -89,22 +85,6 @@ public class ContentMediaSearchJob {
         return ReturnT.SUCCESS;
     }
 
-    private String downloadAndSaveInOSS(String fileName, String url, String contentType) {
-        try {
-            byte[] fileData = HttpUtil.downloadBytes(url);
-            if (fileData == null || fileData.length == 0) {
-                log.warn("下载media失败,URL: {}", url);
-                return null;
-            }
-            InputStream inputStream = new java.io.ByteArrayInputStream(fileData);
-            return CdnUtil.getOssHttpUrl(AliOssFileTool.saveInPublic(EnumPublicBuckets.PUBBUCKET.getBucketName(),
-                    fileName, inputStream, contentType));
-        } catch (Exception e) {
-            log.error("下载转存OSS失败,URL: {}", url, e);
-            return null;
-        }
-    }
-
     @XxlJob("contentMediaImageCheckJob")
     public ReturnT<String> contentMediaImageCheckJob(String param) {
         Long startTime = DateUtils.getTodayStart();
@@ -158,7 +138,7 @@ public class ContentMediaSearchJob {
             for (ContentSearchResponse.DataItem dataItem : dataItems) {
                 // todo 时长过滤
                 String fileName = String.format("video/%s_%d.mp4", content.getId(), System.currentTimeMillis());
-                String fileUrl = downloadAndSaveInOSS(fileName, dataItem.getUrl(), "video/mp4");
+                String fileUrl = AliOssFileTool.downloadAndSaveInOSS(fileName, dataItem.getUrl(), "video/mp4");
                 if (StringUtils.isBlank(fileUrl)) {
                     log.warn("视频转存OSS失败,URL: {}", dataItem.getUrl());
                     continue;

+ 8 - 0
core/src/main/java/com/tzld/supply/job/SpiderJob.java

@@ -1,5 +1,6 @@
 package com.tzld.supply.job;
 
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.tzld.supply.api.SpiderApiService;
 import com.tzld.supply.common.enums.SpiderContentStatusEnum;
 import com.tzld.supply.dao.mapper.supply.spider.SpiderContentMapper;
@@ -37,6 +38,9 @@ public class SpiderJob {
 
     @Value("${get.rank.page.size:5}")
     private Integer getRankPageSize;
+    @ApolloJsonValue("${spider.filter.source.list:[\"知乎\",\"知乎日报\",\"果壳\",\"壹心理\",\"China Daily\",\"NASA \uD83C\uDF0D\",\"wikiHow 中文\"]}")
+    private List<String> filterSourceList;
+
 
     @XxlJob("spiderTaskJob")
     public ReturnT<String> spiderTaskJob(String param) {
@@ -74,6 +78,10 @@ public class SpiderJob {
                         || rankListItem.getTitle().matches("^[A-Za-z0-9\\s!\"#$%&'()*+,\\-./:;<=>?@\\[\\\\\\]^_`{|}~’‘“”]+$")) {
                     continue;
                 }
+                // Filter 过滤来源
+                if (CollectionUtils.isNotEmpty(filterSourceList) && filterSourceList.contains(dataItem.getSource())) {
+                    continue;
+                }
                 // Filter 过滤已存在
                 if (checkContentExist(rankListItem) || titles.contains(rankListItem.getTitle())) {
                     continue;

+ 145 - 6
core/src/main/java/com/tzld/supply/job/VideoGenerateJob.java

@@ -6,13 +6,19 @@ import com.alibaba.fastjson.JSONObject;
 import com.tzld.supply.api.AigcFFmpegApiService;
 import com.tzld.supply.api.fish.FishApiService;
 import com.tzld.supply.common.enums.ProduceVideoStatusEnum;
+import com.tzld.supply.common.enums.SpiderContentMediaStatusEnum;
 import com.tzld.supply.common.enums.SpiderContentStatusEnum;
 import com.tzld.supply.dao.mapper.supply.spider.AiModelTtsMapper;
 import com.tzld.supply.dao.mapper.supply.spider.ProduceVideoAudioMapper;
 import com.tzld.supply.dao.mapper.supply.spider.ProduceVideoMapper;
+import com.tzld.supply.dao.mapper.supply.spider.ProduceVideoMaterialMapper;
 import com.tzld.supply.dao.mapper.supply.spider.ext.SpiderMapperExt;
+import com.tzld.supply.model.param.FFmpeg.CommandParam;
 import com.tzld.supply.model.param.FFmpeg.VideoInfoParam;
+import com.tzld.supply.model.param.FFmpeg.VideoTimeCutParam;
 import com.tzld.supply.model.po.supply.spider.*;
+import com.tzld.supply.util.AliOssFileTool;
+import com.tzld.supply.util.CdnUtil;
 import com.tzld.supply.util.DateUtils;
 import com.tzld.supply.util.DistributedIdGenerator;
 import com.xxl.job.core.biz.model.ReturnT;
@@ -25,6 +31,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 
@@ -43,6 +50,8 @@ public class VideoGenerateJob {
     @Autowired
     private ProduceVideoAudioMapper produceVideoAudioMapper;
     @Autowired
+    private ProduceVideoMaterialMapper produceVideoMaterialMapper;
+    @Autowired
     private AigcFFmpegApiService ffmpegApiService;
 
     /**
@@ -75,8 +84,7 @@ public class VideoGenerateJob {
             }
 
             // 获取音频信息
-            String mediaInfo = ffmpegApiService.videoInfo(new VideoInfoParam(voiceUrl));
-            AudioInfo audioInfo = JSONObject.parseObject(mediaInfo, AudioInfo.class);
+            MediaInfo audioInfo = getMediaInfo(voiceUrl);
 
             // save video
             Long now = System.currentTimeMillis();
@@ -105,13 +113,19 @@ public class VideoGenerateJob {
     @Getter
     @Setter
     @Accessors(chain = true)
-    public static class AudioInfo {
+    public static class MediaInfo {
+        private Integer width;
+        private Integer height;
         // 毫秒
         private Integer duration;
         // Byte
         private Integer size;
         // 音频采样率
         private Integer sampleRate;
+        // 视频平均帧率
+        private Integer avgFrameRate;
+        // 时间基
+        private Integer timeBase;
     }
 
     /**
@@ -123,12 +137,137 @@ public class VideoGenerateJob {
      */
     @XxlJob("videoMaterialGenerateJob")
     public ReturnT<String> videoMaterialGenerateJob(String param) {
-        // image to short video
-        // video cut
-
+        List<ProduceVideo> produceList = getProcessingVideoList();
+        if (CollectionUtil.isEmpty(produceList)) {
+            return ReturnT.SUCCESS;
+        }
+        for (ProduceVideo produceVideo : produceList) {
+            // image to short video
+            imageToVideoMaterial(produceVideo);
+            // video cut
+            videoCutMaterial(produceVideo);
+        }
         return ReturnT.SUCCESS;
     }
 
+    private void imageToVideoMaterial(ProduceVideo produceVideo) {
+        List<SpiderContentMedia> imageList = spiderMapperExt.getMediaSearchSpiderContentMedia(SpiderContentStatusEnum.PASSED.getCode(),
+                null, null, SpiderContentMediaStatusEnum.PASSED.getCode(), "image");
+        if (CollectionUtil.isNotEmpty(imageList)) {
+            for (SpiderContentMedia image : imageList) {
+                // 现仅一种效果,后更改为多种效果随机
+                String command = "ffmpeg -loop 1 -i input.jpg -t 3 -r 25 -filter_complex \"[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,gblur=sigma=5[bg];[0:v]scale=1080:1920:force_original_aspect_ratio=decrease,pad=1080:1920:(ow-iw)/2:(oh-ih)/2:color=black@0[fg];[fg]zoompan=z='min(1.00+0.000266*on,1.02)':d=1:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1080x1920[fg_anim];[bg][fg_anim]overlay=0:0:format=auto\" -c:v libx264 -pix_fmt yuv420p -movflags +faststart \"output.mp4\"";
+                command = command.replace("input.jpg", CdnUtil.getOssHttpUrl(image.getOssKey()));
+                String outputVideoName = String.format("%s_%d.mp4", produceVideo.getContentId(), System.currentTimeMillis());
+                command = command.replace("output.mp4", outputVideoName);
+                CommandParam commandParam = CommandParam.builder()
+                        .ffmpegCommand(command)
+                        .outputFile(outputVideoName)
+                        .outputType("video")
+                        .build();
+                String imageToVideoUrl = ffmpegApiService.command(commandParam);
+                String fileName = String.format("video/%s", outputVideoName);
+                String imageToVideoOssUrl = AliOssFileTool.downloadAndSaveInOSS(fileName, imageToVideoUrl, "video/mp4");
+
+                // 获取视频信息
+                MediaInfo mediaInfo = getMediaInfo(imageToVideoOssUrl);
+                // 保存视频素材
+                saveVideoMaterial(produceVideo, imageToVideoOssUrl, mediaInfo);
+            }
+        }
+    }
+
+    private MediaInfo getMediaInfo(String videoUrl) {
+        String mediaInfo = ffmpegApiService.videoInfo(new VideoInfoParam(videoUrl));
+        return JSONObject.parseObject(mediaInfo, MediaInfo.class);
+    }
+
+    private void saveVideoMaterial(ProduceVideo produceVideo, String imageToVideoOssUrl, MediaInfo videoInfo) {
+        Long now = System.currentTimeMillis();
+        ProduceVideoMaterial material = new ProduceVideoMaterial();
+        material.setId(DistributedIdGenerator.generate());
+        material.setProduceVideoId(produceVideo.getId());
+        material.setUrl(imageToVideoOssUrl);
+        material.setDuration(videoInfo.getDuration());
+        material.setCreateTime(now);
+        material.setUpdateTime(now);
+        produceVideoMaterialMapper.insertSelective(material);
+    }
+
+    private void videoCutMaterial(ProduceVideo produceVideo) {
+        List<SpiderContentMedia> videoList = spiderMapperExt.getMediaSearchSpiderContentMedia(SpiderContentStatusEnum.PASSED.getCode(),
+                null, null, SpiderContentMediaStatusEnum.PASSED.getCode(), "video");
+        if (CollectionUtil.isNotEmpty(videoList)) {
+            for (SpiderContentMedia video : videoList) {
+                // 随机截取时间
+                List<Integer> cutTime = randomCutTime(video.getDuration());
+                // 按时长截取视频片段 起始时间为上次结束时间 结束时间为上次结束时间+时长
+                // 第一个视频片段起始时间为0
+                String lastEndTime = "00:00:00";
+                for (Integer time : cutTime) {
+                    // 本次结束时间为上次结束时间+时长
+                    int lastSeconds = parseTimeToSeconds(lastEndTime);
+                    int currentSeconds = time / 1000; // 毫秒转秒数
+                    int endSeconds = lastSeconds + currentSeconds;
+                    String endTime = formatSecondsToTime(endSeconds);
+                    // 截取视频片段
+                    VideoTimeCutParam timeCutParam = VideoTimeCutParam.builder()
+                            .videoUrl(video.getUrl())
+                            .startTime(lastEndTime)
+                            .endTime(endTime)
+                            .build();
+                    lastEndTime = endTime;
+                    String videoCutUrl = ffmpegApiService.timeCutVideo(timeCutParam);
+                    String fileName = String.format("video/%s_%d.mp4", produceVideo.getContentId(), System.currentTimeMillis());
+                    String videoCutOssUrl = AliOssFileTool.downloadAndSaveInOSS(fileName, videoCutUrl, "video/mp4");
+                    // 获取视频信息
+                    MediaInfo mediaInfo = getMediaInfo(videoCutOssUrl);
+                    // 保存视频素材
+                    saveVideoMaterial(produceVideo, videoCutOssUrl, mediaInfo);
+                }
+            }
+        }
+    }
+
+    private List<Integer> randomCutTime(Integer duration) {
+        List<Integer> cutTime = new ArrayList<>();
+        // 输入为毫秒级视频时长,随机切为5s/10s 长度视频片段,直到视频时长小于10s 返回所有切分时间点
+        while (duration > 10000) {
+            int cut = new Random().nextInt(2) == 0 ? 5000 : 10000;
+            cutTime.add(cut);
+            duration -= cut;
+        }
+        return cutTime;
+    }
+
+    /**
+     * 将"HH:MM:SS"格式的时间字符串转换为总秒数
+     */
+    private int parseTimeToSeconds(String timeStr) {
+        String[] parts = timeStr.split(":");
+        int hours = Integer.parseInt(parts[0]);
+        int minutes = Integer.parseInt(parts[1]);
+        int seconds = Integer.parseInt(parts[2]);
+        return hours * 3600 + minutes * 60 + seconds;
+    }
+
+    /**
+     * 将总秒数格式化为"HH:MM:SS"格式的时间字符串
+     */
+    private String formatSecondsToTime(int totalSeconds) {
+        int hours = totalSeconds / 3600;
+        int remaining = totalSeconds % 3600;
+        int minutes = remaining / 60;
+        int seconds = remaining % 60;
+        return String.format("%02d:%02d:%02d", hours, minutes, seconds);
+    }
+
+    private List<ProduceVideo> getProcessingVideoList() {
+        ProduceVideoExample example = new ProduceVideoExample();
+        example.createCriteria().andStatusEqualTo(ProduceVideoStatusEnum.PROCESSING.getCode());
+        return produceVideoMapper.selectByExample(example);
+    }
+
     /**
      * 视频生成
      * 1. 视频素材拼接长视频

+ 2 - 0
core/src/main/java/com/tzld/supply/model/param/FFmpeg/CommandParam.java

@@ -1,10 +1,12 @@
 package com.tzld.supply.model.param.FFmpeg;
 
+import lombok.Builder;
 import lombok.Getter;
 import lombok.Setter;
 
 @Getter
 @Setter
+@Builder
 public class CommandParam {
     private String ffmpegCommand;
     private String outputFile;

+ 2 - 0
core/src/main/java/com/tzld/supply/model/param/FFmpeg/VideoTimeCutParam.java

@@ -1,10 +1,12 @@
 package com.tzld.supply.model.param.FFmpeg;
 
+import lombok.Builder;
 import lombok.Getter;
 import lombok.Setter;
 
 @Getter
 @Setter
+@Builder
 public class VideoTimeCutParam {
     private String videoUrl;
     private String startTime;

+ 272 - 0
core/src/main/java/com/tzld/supply/model/po/supply/spider/ProduceVideoMaterial.java

@@ -0,0 +1,272 @@
+package com.tzld.supply.model.po.supply.spider;
+
+/**
+ *
+ * This class was generated by MyBatis Generator.
+ * This class corresponds to the database table produce_video_material
+ */
+public class ProduceVideoMaterial {
+    /**
+     * Database Column Remarks:
+     *   主键ID
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column produce_video_material.id
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    private String id;
+
+    /**
+     * Database Column Remarks:
+     *   生成视频ID
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column produce_video_material.produce_video_id
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    private String produceVideoId;
+
+    /**
+     * Database Column Remarks:
+     *   视频url
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column produce_video_material.url
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    private String url;
+
+    /**
+     * Database Column Remarks:
+     *   时长
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column produce_video_material.duration
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    private Integer duration;
+
+    /**
+     * Database Column Remarks:
+     *   状态 0-废弃 1-正常
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column produce_video_material.status
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    private Integer status;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column produce_video_material.create_time
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    private Long createTime;
+
+    /**
+     *
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database column produce_video_material.update_time
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    private Long updateTime;
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column produce_video_material.id
+     *
+     * @return the value of produce_video_material.id
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column produce_video_material.id
+     *
+     * @param id the value for produce_video_material.id
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column produce_video_material.produce_video_id
+     *
+     * @return the value of produce_video_material.produce_video_id
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public String getProduceVideoId() {
+        return produceVideoId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column produce_video_material.produce_video_id
+     *
+     * @param produceVideoId the value for produce_video_material.produce_video_id
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setProduceVideoId(String produceVideoId) {
+        this.produceVideoId = produceVideoId;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column produce_video_material.url
+     *
+     * @return the value of produce_video_material.url
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public String getUrl() {
+        return url;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column produce_video_material.url
+     *
+     * @param url the value for produce_video_material.url
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column produce_video_material.duration
+     *
+     * @return the value of produce_video_material.duration
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public Integer getDuration() {
+        return duration;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column produce_video_material.duration
+     *
+     * @param duration the value for produce_video_material.duration
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setDuration(Integer duration) {
+        this.duration = duration;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column produce_video_material.status
+     *
+     * @return the value of produce_video_material.status
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public Integer getStatus() {
+        return status;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column produce_video_material.status
+     *
+     * @param status the value for produce_video_material.status
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column produce_video_material.create_time
+     *
+     * @return the value of produce_video_material.create_time
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public Long getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column produce_video_material.create_time
+     *
+     * @param createTime the value for produce_video_material.create_time
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setCreateTime(Long createTime) {
+        this.createTime = createTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method returns the value of the database column produce_video_material.update_time
+     *
+     * @return the value of produce_video_material.update_time
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public Long getUpdateTime() {
+        return updateTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method sets the value of the database column produce_video_material.update_time
+     *
+     * @param updateTime the value for produce_video_material.update_time
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setUpdateTime(Long updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", produceVideoId=").append(produceVideoId);
+        sb.append(", url=").append(url);
+        sb.append(", duration=").append(duration);
+        sb.append(", status=").append(status);
+        sb.append(", createTime=").append(createTime);
+        sb.append(", updateTime=").append(updateTime);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 752 - 0
core/src/main/java/com/tzld/supply/model/po/supply/spider/ProduceVideoMaterialExample.java

@@ -0,0 +1,752 @@
+package com.tzld.supply.model.po.supply.spider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ProduceVideoMaterialExample {
+    /**
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    protected String orderByClause;
+
+    /**
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    protected boolean distinct;
+
+    /**
+     * This field was generated by MyBatis Generator.
+     * This field corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    protected List<Criteria> oredCriteria;
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public ProduceVideoMaterialExample() {
+        oredCriteria = new ArrayList<Criteria>();
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setOrderByClause(String orderByClause) {
+        this.orderByClause = orderByClause;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public String getOrderByClause() {
+        return orderByClause;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void setDistinct(boolean distinct) {
+        this.distinct = distinct;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public boolean isDistinct() {
+        return distinct;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public List<Criteria> getOredCriteria() {
+        return oredCriteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void or(Criteria criteria) {
+        oredCriteria.add(criteria);
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public Criteria or() {
+        Criteria criteria = createCriteriaInternal();
+        oredCriteria.add(criteria);
+        return criteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public Criteria createCriteria() {
+        Criteria criteria = createCriteriaInternal();
+        if (oredCriteria.size() == 0) {
+            oredCriteria.add(criteria);
+        }
+        return criteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    protected Criteria createCriteriaInternal() {
+        Criteria criteria = new Criteria();
+        return criteria;
+    }
+
+    /**
+     * This method was generated by MyBatis Generator.
+     * This method corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public void clear() {
+        oredCriteria.clear();
+        orderByClause = null;
+        distinct = false;
+    }
+
+    /**
+     * This class was generated by MyBatis Generator.
+     * This class corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    protected abstract static class GeneratedCriteria {
+        protected List<Criterion> criteria;
+
+        protected GeneratedCriteria() {
+            super();
+            criteria = new ArrayList<Criterion>();
+        }
+
+        public boolean isValid() {
+            return criteria.size() > 0;
+        }
+
+        public List<Criterion> getAllCriteria() {
+            return criteria;
+        }
+
+        public List<Criterion> getCriteria() {
+            return criteria;
+        }
+
+        protected void addCriterion(String condition) {
+            if (condition == null) {
+                throw new RuntimeException("Value for condition cannot be null");
+            }
+            criteria.add(new Criterion(condition));
+        }
+
+        protected void addCriterion(String condition, Object value, String property) {
+            if (value == null) {
+                throw new RuntimeException("Value for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value));
+        }
+
+        protected void addCriterion(String condition, Object value1, Object value2, String property) {
+            if (value1 == null || value2 == null) {
+                throw new RuntimeException("Between values for " + property + " cannot be null");
+            }
+            criteria.add(new Criterion(condition, value1, value2));
+        }
+
+        public Criteria andIdIsNull() {
+            addCriterion("id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIsNotNull() {
+            addCriterion("id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdEqualTo(String value) {
+            addCriterion("id =", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotEqualTo(String value) {
+            addCriterion("id <>", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThan(String value) {
+            addCriterion("id >", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdGreaterThanOrEqualTo(String value) {
+            addCriterion("id >=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThan(String value) {
+            addCriterion("id <", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLessThanOrEqualTo(String value) {
+            addCriterion("id <=", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdLike(String value) {
+            addCriterion("id like", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotLike(String value) {
+            addCriterion("id not like", value, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdIn(List<String> values) {
+            addCriterion("id in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotIn(List<String> values) {
+            addCriterion("id not in", values, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdBetween(String value1, String value2) {
+            addCriterion("id between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andIdNotBetween(String value1, String value2) {
+            addCriterion("id not between", value1, value2, "id");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdIsNull() {
+            addCriterion("produce_video_id is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdIsNotNull() {
+            addCriterion("produce_video_id is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdEqualTo(String value) {
+            addCriterion("produce_video_id =", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdNotEqualTo(String value) {
+            addCriterion("produce_video_id <>", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdGreaterThan(String value) {
+            addCriterion("produce_video_id >", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdGreaterThanOrEqualTo(String value) {
+            addCriterion("produce_video_id >=", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdLessThan(String value) {
+            addCriterion("produce_video_id <", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdLessThanOrEqualTo(String value) {
+            addCriterion("produce_video_id <=", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdLike(String value) {
+            addCriterion("produce_video_id like", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdNotLike(String value) {
+            addCriterion("produce_video_id not like", value, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdIn(List<String> values) {
+            addCriterion("produce_video_id in", values, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdNotIn(List<String> values) {
+            addCriterion("produce_video_id not in", values, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdBetween(String value1, String value2) {
+            addCriterion("produce_video_id between", value1, value2, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andProduceVideoIdNotBetween(String value1, String value2) {
+            addCriterion("produce_video_id not between", value1, value2, "produceVideoId");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlIsNull() {
+            addCriterion("url is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlIsNotNull() {
+            addCriterion("url is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlEqualTo(String value) {
+            addCriterion("url =", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlNotEqualTo(String value) {
+            addCriterion("url <>", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlGreaterThan(String value) {
+            addCriterion("url >", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlGreaterThanOrEqualTo(String value) {
+            addCriterion("url >=", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlLessThan(String value) {
+            addCriterion("url <", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlLessThanOrEqualTo(String value) {
+            addCriterion("url <=", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlLike(String value) {
+            addCriterion("url like", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlNotLike(String value) {
+            addCriterion("url not like", value, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlIn(List<String> values) {
+            addCriterion("url in", values, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlNotIn(List<String> values) {
+            addCriterion("url not in", values, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlBetween(String value1, String value2) {
+            addCriterion("url between", value1, value2, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andUrlNotBetween(String value1, String value2) {
+            addCriterion("url not between", value1, value2, "url");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationIsNull() {
+            addCriterion("duration is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationIsNotNull() {
+            addCriterion("duration is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationEqualTo(Integer value) {
+            addCriterion("duration =", value, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationNotEqualTo(Integer value) {
+            addCriterion("duration <>", value, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationGreaterThan(Integer value) {
+            addCriterion("duration >", value, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationGreaterThanOrEqualTo(Integer value) {
+            addCriterion("duration >=", value, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationLessThan(Integer value) {
+            addCriterion("duration <", value, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationLessThanOrEqualTo(Integer value) {
+            addCriterion("duration <=", value, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationIn(List<Integer> values) {
+            addCriterion("duration in", values, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationNotIn(List<Integer> values) {
+            addCriterion("duration not in", values, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationBetween(Integer value1, Integer value2) {
+            addCriterion("duration between", value1, value2, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andDurationNotBetween(Integer value1, Integer value2) {
+            addCriterion("duration not between", value1, value2, "duration");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusIsNull() {
+            addCriterion("`status` is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusIsNotNull() {
+            addCriterion("`status` is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusEqualTo(Integer value) {
+            addCriterion("`status` =", value, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusNotEqualTo(Integer value) {
+            addCriterion("`status` <>", value, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusGreaterThan(Integer value) {
+            addCriterion("`status` >", value, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusGreaterThanOrEqualTo(Integer value) {
+            addCriterion("`status` >=", value, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusLessThan(Integer value) {
+            addCriterion("`status` <", value, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusLessThanOrEqualTo(Integer value) {
+            addCriterion("`status` <=", value, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusIn(List<Integer> values) {
+            addCriterion("`status` in", values, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusNotIn(List<Integer> values) {
+            addCriterion("`status` not in", values, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusBetween(Integer value1, Integer value2) {
+            addCriterion("`status` between", value1, value2, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andStatusNotBetween(Integer value1, Integer value2) {
+            addCriterion("`status` not between", value1, value2, "status");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeIsNull() {
+            addCriterion("create_time is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeIsNotNull() {
+            addCriterion("create_time is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeEqualTo(Long value) {
+            addCriterion("create_time =", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeNotEqualTo(Long value) {
+            addCriterion("create_time <>", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeGreaterThan(Long value) {
+            addCriterion("create_time >", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) {
+            addCriterion("create_time >=", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeLessThan(Long value) {
+            addCriterion("create_time <", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeLessThanOrEqualTo(Long value) {
+            addCriterion("create_time <=", value, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeIn(List<Long> values) {
+            addCriterion("create_time in", values, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeNotIn(List<Long> values) {
+            addCriterion("create_time not in", values, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeBetween(Long value1, Long value2) {
+            addCriterion("create_time between", value1, value2, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andCreateTimeNotBetween(Long value1, Long value2) {
+            addCriterion("create_time not between", value1, value2, "createTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeIsNull() {
+            addCriterion("update_time is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeIsNotNull() {
+            addCriterion("update_time is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeEqualTo(Long value) {
+            addCriterion("update_time =", value, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeNotEqualTo(Long value) {
+            addCriterion("update_time <>", value, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeGreaterThan(Long value) {
+            addCriterion("update_time >", value, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) {
+            addCriterion("update_time >=", value, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeLessThan(Long value) {
+            addCriterion("update_time <", value, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeLessThanOrEqualTo(Long value) {
+            addCriterion("update_time <=", value, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeIn(List<Long> values) {
+            addCriterion("update_time in", values, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeNotIn(List<Long> values) {
+            addCriterion("update_time not in", values, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeBetween(Long value1, Long value2) {
+            addCriterion("update_time between", value1, value2, "updateTime");
+            return (Criteria) this;
+        }
+
+        public Criteria andUpdateTimeNotBetween(Long value1, Long value2) {
+            addCriterion("update_time not between", value1, value2, "updateTime");
+            return (Criteria) this;
+        }
+    }
+
+    /**
+     * This class was generated by MyBatis Generator.
+     * This class corresponds to the database table produce_video_material
+     *
+     * @mbg.generated do_not_delete_during_merge Mon Oct 27 14:49:52 CST 2025
+     */
+    public static class Criteria extends GeneratedCriteria {
+
+        protected Criteria() {
+            super();
+        }
+    }
+
+    /**
+     * This class was generated by MyBatis Generator.
+     * This class corresponds to the database table produce_video_material
+     *
+     * @mbg.generated Mon Oct 27 14:49:52 CST 2025
+     */
+    public static class Criterion {
+        private String condition;
+
+        private Object value;
+
+        private Object secondValue;
+
+        private boolean noValue;
+
+        private boolean singleValue;
+
+        private boolean betweenValue;
+
+        private boolean listValue;
+
+        private String typeHandler;
+
+        public String getCondition() {
+            return condition;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public Object getSecondValue() {
+            return secondValue;
+        }
+
+        public boolean isNoValue() {
+            return noValue;
+        }
+
+        public boolean isSingleValue() {
+            return singleValue;
+        }
+
+        public boolean isBetweenValue() {
+            return betweenValue;
+        }
+
+        public boolean isListValue() {
+            return listValue;
+        }
+
+        public String getTypeHandler() {
+            return typeHandler;
+        }
+
+        protected Criterion(String condition) {
+            super();
+            this.condition = condition;
+            this.typeHandler = null;
+            this.noValue = true;
+        }
+
+        protected Criterion(String condition, Object value, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.typeHandler = typeHandler;
+            if (value instanceof List<?>) {
+                this.listValue = true;
+            } else {
+                this.singleValue = true;
+            }
+        }
+
+        protected Criterion(String condition, Object value) {
+            this(condition, value, null);
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
+            super();
+            this.condition = condition;
+            this.value = value;
+            this.secondValue = secondValue;
+            this.typeHandler = typeHandler;
+            this.betweenValue = true;
+        }
+
+        protected Criterion(String condition, Object value, Object secondValue) {
+            this(condition, value, secondValue, null);
+        }
+    }
+}

+ 18 - 0
core/src/main/java/com/tzld/supply/util/AliOssFileTool.java

@@ -1,5 +1,6 @@
 package com.tzld.supply.util;
 
+import cn.hutool.http.HttpUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
@@ -31,6 +32,7 @@ import com.tzld.supply.common.enums.EnumUploadFileType;
 import com.tzld.supply.config.AliOssConfig;
 import com.tzld.supply.model.vo.SignatureVO;
 import com.tzld.supply.model.vo.StsTokenVO;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -61,6 +63,7 @@ import java.util.*;
  * @version 1.0.0
  */
 @Component
+@Slf4j
 public class AliOssFileTool extends AliOssConfig {
 
     private static Logger logger = Logger.getLogger(AliOssFileTool.class);
@@ -1489,4 +1492,19 @@ public class AliOssFileTool extends AliOssConfig {
         return policy;
     }
 
+    public static String downloadAndSaveInOSS(String fileName, String url, String contentType) {
+        try {
+            byte[] fileData = HttpUtil.downloadBytes(url);
+            if (fileData == null || fileData.length == 0) {
+                log.warn("下载media失败,URL: {}", url);
+                return null;
+            }
+            InputStream inputStream = new ByteArrayInputStream(fileData);
+            return CdnUtil.getOssHttpUrl(saveInPublic(EnumPublicBuckets.PUBBUCKET.getBucketName(),
+                    fileName, inputStream, contentType));
+        } catch (Exception e) {
+            log.error("下载转存OSS失败,URL: {}", url, e);
+            return null;
+        }
+    }
 }

+ 1 - 0
core/src/main/resources/generator/mybatis-spider-generator-config.xml

@@ -53,6 +53,7 @@
 <!--        <table tableName="fish_api_record" domainObjectName="" alias=""/>-->
 <!--        <table tableName="produce_video" domainObjectName="" alias=""/>-->
 <!--        <table tableName="produce_video_audio" domainObjectName="" alias=""/>-->
+        <table tableName="produce_video_material" domainObjectName="" alias=""/>
     </context>
 
 </generatorConfiguration>

+ 318 - 0
core/src/main/resources/mapper/supply/spider/ProduceVideoMaterialMapper.xml

@@ -0,0 +1,318 @@
+<?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.tzld.supply.dao.mapper.supply.spider.ProduceVideoMaterialMapper">
+  <resultMap id="BaseResultMap" type="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterial">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    <id column="id" jdbcType="VARCHAR" property="id" />
+    <result column="produce_video_id" jdbcType="VARCHAR" property="produceVideoId" />
+    <result column="url" jdbcType="VARCHAR" property="url" />
+    <result column="duration" jdbcType="INTEGER" property="duration" />
+    <result column="status" jdbcType="INTEGER" property="status" />
+    <result column="create_time" jdbcType="BIGINT" property="createTime" />
+    <result column="update_time" jdbcType="BIGINT" property="updateTime" />
+  </resultMap>
+  <sql id="Example_Where_Clause">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    <where>
+      <foreach collection="oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Update_By_Example_Where_Clause">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    <where>
+      <foreach collection="example.oredCriteria" item="criteria" separator="or">
+        <if test="criteria.valid">
+          <trim prefix="(" prefixOverrides="and" suffix=")">
+            <foreach collection="criteria.criteria" item="criterion">
+              <choose>
+                <when test="criterion.noValue">
+                  and ${criterion.condition}
+                </when>
+                <when test="criterion.singleValue">
+                  and ${criterion.condition} #{criterion.value}
+                </when>
+                <when test="criterion.betweenValue">
+                  and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
+                </when>
+                <when test="criterion.listValue">
+                  and ${criterion.condition}
+                  <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
+                    #{listItem}
+                  </foreach>
+                </when>
+              </choose>
+            </foreach>
+          </trim>
+        </if>
+      </foreach>
+    </where>
+  </sql>
+  <sql id="Base_Column_List">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    id, produce_video_id, url, duration, `status`, create_time, update_time
+  </sql>
+  <select id="selectByExample" parameterType="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterialExample" resultMap="BaseResultMap">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    select
+    <if test="distinct">
+      distinct
+    </if>
+    <include refid="Base_Column_List" />
+    from produce_video_material
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+    <if test="orderByClause != null">
+      order by ${orderByClause}
+    </if>
+  </select>
+  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    select 
+    <include refid="Base_Column_List" />
+    from produce_video_material
+    where id = #{id,jdbcType=VARCHAR}
+  </select>
+  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    delete from produce_video_material
+    where id = #{id,jdbcType=VARCHAR}
+  </delete>
+  <delete id="deleteByExample" parameterType="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterialExample">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    delete from produce_video_material
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </delete>
+  <insert id="insert" parameterType="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterial">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    insert into produce_video_material (id, produce_video_id, url, 
+      duration, `status`, create_time, 
+      update_time)
+    values (#{id,jdbcType=VARCHAR}, #{produceVideoId,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR}, 
+      #{duration,jdbcType=INTEGER}, #{status,jdbcType=INTEGER}, #{createTime,jdbcType=BIGINT}, 
+      #{updateTime,jdbcType=BIGINT})
+  </insert>
+  <insert id="insertSelective" parameterType="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterial">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    insert into produce_video_material
+    <trim prefix="(" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        id,
+      </if>
+      <if test="produceVideoId != null">
+        produce_video_id,
+      </if>
+      <if test="url != null">
+        url,
+      </if>
+      <if test="duration != null">
+        duration,
+      </if>
+      <if test="status != null">
+        `status`,
+      </if>
+      <if test="createTime != null">
+        create_time,
+      </if>
+      <if test="updateTime != null">
+        update_time,
+      </if>
+    </trim>
+    <trim prefix="values (" suffix=")" suffixOverrides=",">
+      <if test="id != null">
+        #{id,jdbcType=VARCHAR},
+      </if>
+      <if test="produceVideoId != null">
+        #{produceVideoId,jdbcType=VARCHAR},
+      </if>
+      <if test="url != null">
+        #{url,jdbcType=VARCHAR},
+      </if>
+      <if test="duration != null">
+        #{duration,jdbcType=INTEGER},
+      </if>
+      <if test="status != null">
+        #{status,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        #{createTime,jdbcType=BIGINT},
+      </if>
+      <if test="updateTime != null">
+        #{updateTime,jdbcType=BIGINT},
+      </if>
+    </trim>
+  </insert>
+  <select id="countByExample" parameterType="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterialExample" resultType="java.lang.Long">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    select count(*) from produce_video_material
+    <if test="_parameter != null">
+      <include refid="Example_Where_Clause" />
+    </if>
+  </select>
+  <update id="updateByExampleSelective" parameterType="map">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    update produce_video_material
+    <set>
+      <if test="record.id != null">
+        id = #{record.id,jdbcType=VARCHAR},
+      </if>
+      <if test="record.produceVideoId != null">
+        produce_video_id = #{record.produceVideoId,jdbcType=VARCHAR},
+      </if>
+      <if test="record.url != null">
+        url = #{record.url,jdbcType=VARCHAR},
+      </if>
+      <if test="record.duration != null">
+        duration = #{record.duration,jdbcType=INTEGER},
+      </if>
+      <if test="record.status != null">
+        `status` = #{record.status,jdbcType=INTEGER},
+      </if>
+      <if test="record.createTime != null">
+        create_time = #{record.createTime,jdbcType=BIGINT},
+      </if>
+      <if test="record.updateTime != null">
+        update_time = #{record.updateTime,jdbcType=BIGINT},
+      </if>
+    </set>
+    <if test="_parameter != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByExample" parameterType="map">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    update produce_video_material
+    set id = #{record.id,jdbcType=VARCHAR},
+      produce_video_id = #{record.produceVideoId,jdbcType=VARCHAR},
+      url = #{record.url,jdbcType=VARCHAR},
+      duration = #{record.duration,jdbcType=INTEGER},
+      `status` = #{record.status,jdbcType=INTEGER},
+      create_time = #{record.createTime,jdbcType=BIGINT},
+      update_time = #{record.updateTime,jdbcType=BIGINT}
+    <if test="_parameter != null">
+      <include refid="Update_By_Example_Where_Clause" />
+    </if>
+  </update>
+  <update id="updateByPrimaryKeySelective" parameterType="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterial">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    update produce_video_material
+    <set>
+      <if test="produceVideoId != null">
+        produce_video_id = #{produceVideoId,jdbcType=VARCHAR},
+      </if>
+      <if test="url != null">
+        url = #{url,jdbcType=VARCHAR},
+      </if>
+      <if test="duration != null">
+        duration = #{duration,jdbcType=INTEGER},
+      </if>
+      <if test="status != null">
+        `status` = #{status,jdbcType=INTEGER},
+      </if>
+      <if test="createTime != null">
+        create_time = #{createTime,jdbcType=BIGINT},
+      </if>
+      <if test="updateTime != null">
+        update_time = #{updateTime,jdbcType=BIGINT},
+      </if>
+    </set>
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+  <update id="updateByPrimaryKey" parameterType="com.tzld.supply.model.po.supply.spider.ProduceVideoMaterial">
+    <!--
+      WARNING - @mbg.generated
+      This element is automatically generated by MyBatis Generator, do not modify.
+      This element was generated on Mon Oct 27 14:49:52 CST 2025.
+    -->
+    update produce_video_material
+    set produce_video_id = #{produceVideoId,jdbcType=VARCHAR},
+      url = #{url,jdbcType=VARCHAR},
+      duration = #{duration,jdbcType=INTEGER},
+      `status` = #{status,jdbcType=INTEGER},
+      create_time = #{createTime,jdbcType=BIGINT},
+      update_time = #{updateTime,jdbcType=BIGINT}
+    where id = #{id,jdbcType=VARCHAR}
+  </update>
+</mapper>

+ 12 - 2
core/src/main/resources/mapper/supply/spider/ext/SpiderMapperExt.xml

@@ -60,7 +60,12 @@
         from spider_content content
         join spider_content_media media on content.id = media.content_id and media.media_type = #{mediaType}
         where content.`status` = #{status}
-        and content.create_time between #{startTime} and #{endTime}
+          <if test="startTime != null">
+            and content.create_time >= #{startTime}
+        </if>
+        <if test="endTime != null">
+            and content.create_time &lt;= #{endTime}
+        </if>
         and media.status = #{mediaStatus}
     </select>
 
@@ -68,7 +73,12 @@
         select *
         from spider_content
         where `status` = #{status}
-          and create_time between #{startTime} and #{endTime}
+          <if test="startTime != null">
+            and create_time >= #{startTime}
+        </if>
+        <if test="endTime != null">
+            and create_time &lt;= #{endTime}
+        </if>
           and pq_improve_narration_script is not null
           and id not in (select content_id from produce_video)
     </select>

+ 6 - 0
server/src/main/java/com/tzld/supply/controller/XxlJobController.java

@@ -88,4 +88,10 @@ public class XxlJobController {
         return CommonResponse.success();
     }
 
+    @GetMapping("/videoMaterialGenerateJob")
+    public CommonResponse<Void> videoMaterialGenerateJob(String dateStr) {
+        videoGenerateJob.videoMaterialGenerateJob(dateStr);
+        return CommonResponse.success();
+    }
+
 }