Explorar o código

fix: 相册视频功能修改

penghao hai 2 meses
pai
achega
9304da5162

+ 4 - 0
alien-entity/src/main/java/shop/alien/entity/store/StoreOfficialAlbum.java

@@ -32,6 +32,10 @@ public class StoreOfficialAlbum extends Model<StoreOfficialAlbum> {
     @ApiModelProperty(value = "相册名称")
     @TableField("album_name")
     private String albumName;
+    
+    @ApiModelProperty(value = "是否固定 (0-否, 1-是)")
+    @TableField("is_fixed")
+    private Integer isFixed;
 
     @ApiModelProperty(value = "删除标记, 0:未删除, 1:已删除")
     @TableField("delete_flag")

+ 33 - 0
alien-entity/src/main/java/shop/alien/entity/store/dto/StoreVideoSaveDto.java

@@ -0,0 +1,33 @@
+package shop.alien.entity.store.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 视频保存DTO
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@ApiModel(value = "StoreVideoSaveDto对象", description = "视频保存DTO")
+public class StoreVideoSaveDto implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "门店ID", required = true)
+    private Integer storeId;
+
+    @ApiModelProperty(value = "相册ID(业务ID)")
+    private Integer businessId;
+
+    @ApiModelProperty(value = "视频URL列表", required = true)
+    private List<String> videoUrls;
+
+    @ApiModelProperty(value = "视频ID列表")
+    private List<Integer> videoIds;
+
+}

+ 15 - 1
alien-store/src/main/java/shop/alien/store/controller/StoreImgController.java

@@ -16,6 +16,7 @@ import shop.alien.store.service.StoreOfficialAlbumService;
 import shop.alien.store.util.GroupConstant;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 二期-门店图片Controller
@@ -238,7 +239,20 @@ public class StoreImgController {
     @PostMapping("/delete")
     public R<String> delete(@RequestBody List<Integer> ids) {
         log.info("StoreImgController.delete?ids={}", ids);
-        if (storeImgService.removeByIds(ids)) {
+        // 参数校验:ID列表不能为空
+        if (ids == null || ids.isEmpty()) {
+            log.warn("删除图片失败,ID列表为空");
+            return R.fail("删除失败:ID列表不能为空");
+        }
+        // 过滤掉null值和无效ID
+        List<Integer> validIds = ids.stream()
+                .filter(id -> id != null && id > 0)
+                .collect(Collectors.toList());
+        if (validIds.isEmpty()) {
+            log.warn("删除图片失败,没有有效的ID");
+            return R.fail("删除失败:没有有效的ID");
+        }
+        if (storeImgService.removeByIds(validIds)) {
             return R.success("删除成功");
         }
         return R.fail("删除失败");

+ 13 - 2
alien-store/src/main/java/shop/alien/store/controller/StoreOfficialAlbumController.java

@@ -34,8 +34,19 @@ public class StoreOfficialAlbumController {
     @PostMapping("/createOrUpdateOfficialAlbum")
     public R<StoreOfficialAlbum> createOrUpdateOfficialAlbum(@RequestBody StoreOfficialAlbum storeOfficialAlbum) {
         log.info("StoreOfficialAlbumController.createOfficialAlbum?storeOfficialAlbum={}", storeOfficialAlbum);
-        StoreOfficialAlbum storeOfficial = storeOfficialAlbumService.createOrUpdateOfficialAlbum(storeOfficialAlbum);
-        return R.data(storeOfficial);
+        try {
+            StoreOfficialAlbum storeOfficial = storeOfficialAlbumService.createOrUpdateOfficialAlbum(storeOfficialAlbum);
+            return R.data(storeOfficial);
+        } catch (IllegalArgumentException e) {
+            log.warn("创建/更新官方相册失败,参数错误:{}", e.getMessage());
+            return R.fail(e.getMessage());
+        } catch (RuntimeException e) {
+            log.error("创建/更新官方相册失败,业务错误:{}", e.getMessage(), e);
+            return R.fail(e.getMessage());
+        } catch (Exception e) {
+            log.error("创建/更新官方相册失败,系统异常:{}", e.getMessage(), e);
+            return R.fail("操作失败:" + e.getMessage());
+        }
     }
 
     @ApiOperation("获取官方相册列表")

+ 12 - 0
alien-store/src/main/java/shop/alien/store/controller/StoreVideoController.java

@@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.StoreVideo;
+import shop.alien.entity.store.dto.StoreVideoSaveDto;
 import shop.alien.store.service.StoreVideoService;
 
 import java.util.List;
@@ -29,6 +30,17 @@ import java.util.List;
 public class StoreVideoController {
     private final StoreVideoService storeVideoService;
 
+    @ApiOperation("保存视频(单个或批量)")
+    @ApiOperationSupport(order = 1)
+    @PostMapping("/saveOrSaveBatch")
+    public R<String> saveOrSaveBatch(@RequestBody StoreVideoSaveDto dto) {
+        log.info("StoreVideoController.saveOrSaveBatch?dto={}", dto);
+        if (storeVideoService.saveOrSaveBatch(dto)) {
+            return R.success("保存成功");
+        }
+        return R.fail("保存失败");
+    }
+
     @ApiOperation("新增视频")
     @ApiOperationSupport(order = 1)
     @PostMapping("/save")

+ 9 - 0
alien-store/src/main/java/shop/alien/store/service/StoreVideoService.java

@@ -2,6 +2,7 @@ package shop.alien.store.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import shop.alien.entity.store.StoreVideo;
+import shop.alien.entity.store.dto.StoreVideoSaveDto;
 
 import java.util.List;
 
@@ -29,5 +30,13 @@ public interface StoreVideoService extends IService<StoreVideo> {
      * @return 视频列表
      */
     List<StoreVideo> getByStoreIdAndBusinessId(Integer storeId, Integer businessId);
+
+    /**
+     * 保存视频(单个或批量)
+     *
+     * @param dto 视频保存DTO,包含门店ID、相册ID和视频URL列表
+     * @return 是否保存成功
+     */
+    boolean saveOrSaveBatch(StoreVideoSaveDto dto);
 }
 

+ 21 - 17
alien-store/src/main/java/shop/alien/store/service/impl/StoreImgServiceImpl.java

@@ -185,21 +185,21 @@ public class StoreImgServiceImpl extends ServiceImpl<StoreImgMapper, StoreImg> i
         updateWrapper.eq(StoreOfficialAlbum::getId, businessId).set(StoreOfficialAlbum::getImgCount, imgCount);
         storeOfficialAlbumMapper.update(null, updateWrapper);
         List<StoreImg> resList = this.list(lambdaQueryWrapper);
-        // 查询视频列表,只获取第一个视频的封面
-        List<StoreVideo> byStoreId = storeVideoService.getByStoreId(storeId);
-        if (!CollectionUtils.isEmpty(byStoreId)) {
-            StoreVideo storeVideo = byStoreId.get(0);
-            String imgUrl = storeVideo.getImgUrl();
-            if (imgUrl != null && !imgUrl.trim().isEmpty()) {
-                try {
-                    // 解析JSON数组格式的imgUrl
-                    com.alibaba.fastjson.JSONArray jsonArray = com.alibaba.fastjson.JSONArray.parseArray(imgUrl);
-                    if (jsonArray != null && !jsonArray.isEmpty()) {
-                        com.alibaba.fastjson.JSONObject firstItem = jsonArray.getJSONObject(0);
-                        if (firstItem != null && firstItem.containsKey("cover")) {
-                            String coverUrl = firstItem.getString("cover");
+        // 查询对应相册的视频列表,获取第一个视频的封面
+        if (businessId != null && businessId > 0) {
+            List<StoreVideo> videoList = storeVideoService.getByStoreIdAndBusinessId(storeId, businessId);
+            if (!CollectionUtils.isEmpty(videoList)) {
+                StoreVideo storeVideo = videoList.get(0);
+                String imgUrl = storeVideo.getImgUrl();
+                if (imgUrl != null && !imgUrl.trim().isEmpty()) {
+                    try {
+                        // 解析JSON对象格式的imgUrl: {"video": "...", "cover": "..."}
+                        com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSON.parseObject(imgUrl);
+                        if (jsonObject != null && jsonObject.containsKey("cover")) {
+                            String coverUrl = jsonObject.getString("cover");
                             if (coverUrl != null && !coverUrl.trim().isEmpty()) {
-                                // 创建StoreImg对象,设置封面URL
+                                // 创建StoreImg对象,设置封面URL(注意:这是临时对象,id为null是正常的)
+                                // 如果需要避免id为null,可以考虑不添加此对象,或者从视频表查询对应的图片记录
                                 StoreImg storeImg = new StoreImg();
                                 storeImg.setImgUrl(coverUrl);
                                 storeImg.setStoreId(storeId);
@@ -207,13 +207,17 @@ public class StoreImgServiceImpl extends ServiceImpl<StoreImgMapper, StoreImg> i
                                 storeImg.setBusinessId(businessId);
                                 storeImg.setImgDescription("视频");
                                 storeImg.setImgSort(resList.size() + 1);
+                                // 设置一个临时ID标识,避免前端处理null值的问题
+                                // 使用负数作为临时标识,表示这是从视频中提取的封面
+                                int tempId = storeVideo.getId() != null ? -storeVideo.getId() : -(int)(System.currentTimeMillis() % Integer.MAX_VALUE);
+                                storeImg.setId(tempId);
                                 resList.add(storeImg);
-                                log.info("从第一个视频中提取封面成功,coverUrl: {}", coverUrl);
+                                log.info("从视频中提取封面成功,视频ID:{},coverUrl: {}", storeVideo.getId(), coverUrl);
                             }
                         }
+                    } catch (Exception e) {
+                        log.warn("解析视频imgUrl失败,imgUrl: {}, error: {}", imgUrl, e.getMessage());
                     }
-                } catch (Exception e) {
-                    log.warn("解析视频imgUrl失败,imgUrl: {}, error: {}", imgUrl, e.getMessage());
                 }
             }
         }

+ 15 - 6
alien-store/src/main/java/shop/alien/store/service/impl/StoreInfoServiceImpl.java

@@ -1208,12 +1208,21 @@ public class StoreInfoServiceImpl extends ServiceImpl<StoreInfoMapper, StoreInfo
 
         storeInfoMapper.insert(storeInfo);
         result.setId(storeInfo.getId());
-        // 新建默认  环境 相册
-        StoreOfficialAlbum officialAlbum = new StoreOfficialAlbum();
-        officialAlbum.setStoreId(storeInfo.getId());
-        officialAlbum.setAlbumName("环境");
-        officialAlbum.setImgCount(0);
-        storeOfficialAlbumMapper.insert(officialAlbum);
+        // 新建默认固定相册:环境相册和视频相册
+        // 创建环境相册
+        StoreOfficialAlbum environmentAlbum = new StoreOfficialAlbum();
+        environmentAlbum.setStoreId(storeInfo.getId());
+        environmentAlbum.setAlbumName("环境");
+        environmentAlbum.setImgCount(0);
+        environmentAlbum.setIsFixed(1); // 1-固定相册
+        storeOfficialAlbumMapper.insert(environmentAlbum);
+        // 创建视频相册
+        StoreOfficialAlbum videoAlbum = new StoreOfficialAlbum();
+        videoAlbum.setStoreId(storeInfo.getId());
+        videoAlbum.setAlbumName("视频");
+        videoAlbum.setImgCount(0);
+        videoAlbum.setIsFixed(1); // 1-固定相册
+        storeOfficialAlbumMapper.insert(videoAlbum);
         if (StringUtils.isNotEmpty(storeInfoDto.getStorePositionLongitude()) && StringUtils.isNotEmpty(storeInfoDto.getStorePositionLatitude())) {
             nearMeService.inGeolocation(new Point(Double.parseDouble(storeInfoDto.getStorePositionLongitude()), Double.parseDouble(storeInfoDto.getStorePositionLatitude())), storeInfo.getId().toString(), Boolean.TRUE);
         }

+ 143 - 7
alien-store/src/main/java/shop/alien/store/service/impl/StoreOfficialAlbumServiceImpl.java

@@ -1,5 +1,8 @@
 package shop.alien.store.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@@ -55,8 +58,35 @@ public class StoreOfficialAlbumServiceImpl extends ServiceImpl<StoreOfficialAlbu
     @Override
     @Transactional(rollbackFor = Exception.class)
     public StoreOfficialAlbum createOrUpdateOfficialAlbum(StoreOfficialAlbum storeOfficialAlbum) {
-        Integer id= storeOfficialAlbum.getId();
-        if (id!=null && id > 0) {
+        // 参数校验
+        if (storeOfficialAlbum == null) {
+            throw new IllegalArgumentException("相册信息不能为空");
+        }
+        if (storeOfficialAlbum.getStoreId() == null || storeOfficialAlbum.getStoreId() <= 0) {
+            throw new IllegalArgumentException("门店ID不能为空且必须大于0");
+        }
+        if (StringUtils.isBlank(storeOfficialAlbum.getAlbumName())) {
+            throw new IllegalArgumentException("相册名称不能为空");
+        }
+        
+        // 检查同一门店下相册名称是否重复
+        LambdaQueryWrapper<StoreOfficialAlbum> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(StoreOfficialAlbum::getStoreId, storeOfficialAlbum.getStoreId())
+                .eq(StoreOfficialAlbum::getAlbumName, storeOfficialAlbum.getAlbumName())
+                .eq(StoreOfficialAlbum::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+        
+        // 如果是更新操作,排除当前记录
+        Integer id = storeOfficialAlbum.getId();
+        if (id != null && id > 0) {
+            queryWrapper.ne(StoreOfficialAlbum::getId, id);
+        }
+        
+        long count = storeOfficialAlbumMapper.selectCount(queryWrapper);
+        if (count > 0) {
+            throw new RuntimeException("同一门店下相册名称不能重复");
+        }
+        
+        if (id != null && id > 0) {
             boolean updateSuccess = storeOfficialAlbumMapper.updateById(storeOfficialAlbum) > 0;
             if (!updateSuccess) {
                 throw new RuntimeException("更新官方相册失败");
@@ -84,10 +114,91 @@ public class StoreOfficialAlbumServiceImpl extends ServiceImpl<StoreOfficialAlbu
             this.updateBatchById(storeOfficialAlbumList);
 
             // 查询官方相册列表
-            return storeOfficialAlbumMapper.getStoreOfficialAlbumList(Integer.parseInt(storeId));
+            List<StoreOfficialAlbumVo> albumVoList = storeOfficialAlbumMapper.getStoreOfficialAlbumList(Integer.parseInt(storeId));
+            
+            // 为每个相册设置第一张图片,如果是视频相册则返回视频封面
+            for (StoreOfficialAlbumVo albumVo : albumVoList) {
+                // 如果是视频相册,查询视频封面
+                if ("视频".equals(albumVo.getAlbumName())) {
+                    String coverUrl = getVideoAlbumCover(Integer.parseInt(storeId), albumVo.getId());
+                    if (StringUtils.isNotBlank(coverUrl)) {
+                        albumVo.setImgUrl(coverUrl);
+                    }
+                }
+                // 其他相册的imgUrl已经在SQL查询中设置了第一张图片
+            }
+            
+            return albumVoList;
         }
         return Collections.emptyList();
     }
+    
+    /**
+     * 获取视频相册的封面(第一个视频的封面)
+     *
+     * @param storeId 门店ID
+     * @param albumId 相册ID
+     * @return 封面URL
+     */
+    private String getVideoAlbumCover(Integer storeId, Integer albumId) {
+        try {
+            // 查询该相册下的第一个视频
+            LambdaQueryWrapper<StoreVideo> videoQueryWrapper = new LambdaQueryWrapper<>();
+            videoQueryWrapper.eq(StoreVideo::getStoreId, storeId)
+                    .eq(StoreVideo::getBusinessId, albumId)
+                    .eq(StoreVideo::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE)
+                    .orderByAsc(StoreVideo::getImgSort)
+                    .orderByAsc(StoreVideo::getCreatedTime)
+                    .last("LIMIT 1");
+            
+            List<StoreVideo> videoList = storeVideoMapper.selectList(videoQueryWrapper);
+            if (!CollectionUtils.isEmpty(videoList)) {
+                StoreVideo firstVideo = videoList.get(0);
+                String imgUrl = firstVideo.getImgUrl();
+                if (StringUtils.isNotBlank(imgUrl)) {
+                    // 解析JSON格式,提取cover字段
+                    return extractCoverFromVideoUrl(imgUrl);
+                }
+            }
+        } catch (Exception e) {
+            log.error("获取视频相册封面失败,门店ID:{},相册ID:{},错误:{}", storeId, albumId, e.getMessage(), e);
+        }
+        return null;
+    }
+    
+    /**
+     * 从视频URL(JSON格式)中提取封面URL
+     *
+     * @param imgUrl 视频URL,格式:{"video": "...", "cover": "..."} 或 [{"video": "...", "cover": "..."}]
+     * @return 封面URL
+     */
+    private String extractCoverFromVideoUrl(String imgUrl) {
+        if (StringUtils.isBlank(imgUrl)) {
+            return null;
+        }
+        
+        try {
+            // 尝试解析为JSON对象
+            JSONObject jsonObject = JSON.parseObject(imgUrl);
+            if (jsonObject != null && jsonObject.containsKey("cover")) {
+                return jsonObject.getString("cover");
+            }
+        } catch (Exception e1) {
+            // 如果不是对象格式,尝试解析为数组格式
+            try {
+                JSONArray jsonArray = JSONArray.parseArray(imgUrl);
+                if (jsonArray != null && !jsonArray.isEmpty()) {
+                    JSONObject firstItem = jsonArray.getJSONObject(0);
+                    if (firstItem != null && firstItem.containsKey("cover")) {
+                        return firstItem.getString("cover");
+                    }
+                }
+            } catch (Exception e2) {
+                log.debug("解析视频URL失败,imgUrl:{}", imgUrl);
+            }
+        }
+        return null;
+    }
 
     /**
      * 根据storeId查询官方相册列表
@@ -149,11 +260,36 @@ public class StoreOfficialAlbumServiceImpl extends ServiceImpl<StoreOfficialAlbu
         }
         //视频
         if (type == 1) {
-            // 查询视频
-            LambdaQueryWrapper<StoreVideo> albumQueryWrapper = new LambdaQueryWrapper<>();
-            albumQueryWrapper.eq(StoreVideo::getStoreId, storeId)
+            // 查询视频相册(只查询视频表)
+            LambdaQueryWrapper<StoreVideo> videoQueryWrapper = new LambdaQueryWrapper<>();
+            videoQueryWrapper.eq(StoreVideo::getStoreId, storeId)
                     .eq(StoreVideo::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
-            List<StoreVideo> videoList = storeVideoMapper.selectList(albumQueryWrapper);
+            
+            // 如果指定了相册名称,根据相册名称找到对应的相册ID,然后查询该相册下的视频
+            if (StringUtils.isNotBlank(albumName)) {
+                LambdaQueryWrapper<StoreOfficialAlbum> albumQueryWrapper = new LambdaQueryWrapper<>();
+                albumQueryWrapper.eq(StoreOfficialAlbum::getStoreId, storeId)
+                        .eq(StoreOfficialAlbum::getAlbumName, albumName)
+                        .eq(StoreOfficialAlbum::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+                List<StoreOfficialAlbum> albumList = storeOfficialAlbumMapper.selectList(albumQueryWrapper);
+                
+                if (!CollectionUtils.isEmpty(albumList)) {
+                    // 提取相册ID列表
+                    List<Integer> albumIds = albumList.stream()
+                            .map(StoreOfficialAlbum::getId)
+                            .collect(Collectors.toList());
+                    // 根据相册ID筛选视频
+                    videoQueryWrapper.in(StoreVideo::getBusinessId, albumIds);
+                } else {
+                    // 如果未找到对应的相册,返回空结果
+                    log.info("获取视频列表,门店ID:{},相册名称:{},未找到对应的相册", storeId, albumName);
+                    StoreOfficialAlbumImgVo result = new StoreOfficialAlbumImgVo();
+                    result.setVideoList(Collections.emptyList());
+                    return result;
+                }
+            }
+            
+            List<StoreVideo> videoList = storeVideoMapper.selectList(videoQueryWrapper);
             StoreOfficialAlbumImgVo result = new StoreOfficialAlbumImgVo();
             result.setVideoList(videoList);
             return result;

+ 250 - 40
alien-store/src/main/java/shop/alien/store/service/impl/StoreVideoServiceImpl.java

@@ -1,7 +1,6 @@
 package shop.alien.store.service.impl;
 
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -11,9 +10,14 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import shop.alien.entity.store.StoreOfficialAlbum;
 import shop.alien.entity.store.StoreVideo;
+import shop.alien.entity.store.dto.StoreVideoSaveDto;
+import shop.alien.mapper.StoreOfficialAlbumMapper;
 import shop.alien.mapper.StoreVideoMapper;
 import shop.alien.store.service.StoreVideoService;
+import shop.alien.store.util.CommonConstant;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import shop.alien.util.ali.AliOSSUtil;
 import shop.alien.util.common.RandomCreateUtil;
 import shop.alien.util.common.VideoUtils;
@@ -42,6 +46,8 @@ public class StoreVideoServiceImpl extends ServiceImpl<StoreVideoMapper, StoreVi
 
     private final AliOSSUtil aliOSSUtil;
 
+    private final StoreOfficialAlbumMapper storeOfficialAlbumMapper;
+
     /**
      * 视频文件类型列表
      */
@@ -51,42 +57,214 @@ public class StoreVideoServiceImpl extends ServiceImpl<StoreVideoMapper, StoreVi
     private String uploadDir;
 
     /**
-     * 保存视频,自动截取第一帧作为封面
+     * 保存视频(单个或批量)
      *
-     * @param entity 视频实体
+     * @param dto 视频保存DTO,包含门店ID、相册ID和视频URL列表
      * @return 是否保存成功
      */
     @Override
-    public boolean save(StoreVideo entity) {
+    public boolean saveOrSaveBatch(StoreVideoSaveDto dto) {
         // 参数验证
-        if (entity == null) {
-            log.error("StoreVideoServiceImpl.save ERROR: entity is null");
+        if (dto == null) {
+            log.error("StoreVideoServiceImpl.saveOrSaveBatch ERROR: dto is null");
+            return false;
+        }
+        if (dto.getStoreId() == null || dto.getStoreId() <= 0) {
+            log.error("StoreVideoServiceImpl.saveOrSaveBatch ERROR: storeId is invalid");
+            return false;
+        }
+        if (dto.getVideoUrls() == null || dto.getVideoUrls().isEmpty()) {
+            log.error("StoreVideoServiceImpl.saveOrSaveBatch ERROR: videoUrls is empty");
             return false;
         }
 
-        // 如果imgUrl不为空,尝试处理视频和封面
-        if (StringUtils.isNotBlank(entity.getImgUrl())) {
-            try {
-                // 处理视频URL,截取封面并更新imgUrl字段
-                String processedImgUrl = processVideoAndCover(entity.getImgUrl());
-                if (StringUtils.isNotBlank(processedImgUrl)) {
-                    entity.setImgUrl(processedImgUrl);
+        // 解析视频URL列表,前端只传入视频URL,后端自动匹配或生成封面
+        // 排序号将根据传入数组的顺序设置(从1开始),用于支持编辑排序功能
+        List<StoreVideo> videoList = new java.util.ArrayList<>();
+        List<String> videoUrls = dto.getVideoUrls();
+        List<Integer> videoIds = dto.getVideoIds(); // 可选的视频ID列表,用于编辑
+        
+        // 遍历URL列表,每个URL都是视频URL
+        int videoIndex = 0;
+        for (int i = 0; i < videoUrls.size(); i++) {
+            String videoUrl = videoUrls.get(i);
+            if (StringUtils.isBlank(videoUrl)) {
+                log.warn("视频URL为空,跳过该视频");
+                continue;
+            }
+            
+            // 获取对应的视频ID(如果存在)
+            Integer videoId = null;
+            if (videoIds != null && i < videoIds.size()) {
+                videoId = videoIds.get(i);
+            }
+            
+            // 如果ID存在且有效,尝试查询现有视频进行更新
+            StoreVideo storeVideo = null;
+            if (videoId != null && videoId > 0) {
+                try {
+                    storeVideo = this.getById(videoId);
+                    if (storeVideo != null && storeVideo.getDeleteFlag() == CommonConstant.DELETE_FLAG_UNDELETE) {
+                        log.info("找到现有视频,ID:{},将进行更新,排序号将根据数组顺序设置为:{}", videoId, videoIndex + 1);
+                    } else {
+                        log.warn("视频ID:{} 不存在或已删除,将作为新增处理", videoId);
+                        storeVideo = null;
+                    }
+                } catch (Exception e) {
+                    log.error("查询视频失败,ID:{},错误:{},将作为新增处理", videoId, e.getMessage());
+                    storeVideo = null;
+                }
+            }
+            
+            // 如果不存在现有视频,创建新对象
+            if (storeVideo == null) {
+                storeVideo = new StoreVideo();
+                storeVideo.setId(null); // 确保ID为null,表示新增
+                // 新增时,根据数组顺序设置排序号(从1开始)
+                storeVideo.setImgSort(videoIndex + 1);
+                storeVideo.setImgDescription("相册视频");
+            } else {
+                // 更新时,根据数组顺序更新排序号(从1开始)
+                // 这样可以根据传入的数组顺序来调整视频的排序
+                storeVideo.setImgSort(videoIndex + 1);
+                // 更新时,如果描述未设置,使用默认值
+                if (StringUtils.isBlank(storeVideo.getImgDescription())) {
+                    storeVideo.setImgDescription("相册视频");
+                }
+            }
+            
+            // 设置基本信息
+            storeVideo.setStoreId(dto.getStoreId());
+            storeVideo.setBusinessId(dto.getBusinessId());
+            
+            // 尝试根据视频URL自动匹配封面URL(从/file/uploadMore接口获取的封面URL格式)
+            // 封面URL格式:视频URL的扩展名从.mp4等替换为.jpg
+            String coverUrl = tryMatchCoverUrl(videoUrl);
+            
+            // 如果匹配不到封面URL,尝试处理视频生成封面
+            if (StringUtils.isBlank(coverUrl)) {
+                log.info("未找到匹配的封面URL,视频URL:{},将尝试处理生成封面", videoUrl);
+                try {
+                    // 调用processVideoAndCover处理,生成封面
+                    String processedImgUrl = processVideoAndCover(videoUrl);
+                    if (StringUtils.isNotBlank(processedImgUrl)) {
+                        // 如果处理成功,processedImgUrl已经是JSON格式,直接使用
+                        storeVideo.setImgUrl(processedImgUrl);
+                        videoList.add(storeVideo);
+                        videoIndex++;
+                        continue;
+                    }
+                } catch (Exception e) {
+                    log.error("处理视频封面失败,视频URL:{},错误:{},将保存视频但不包含封面", videoUrl, e.getMessage(), e);
+                    // 处理失败时,继续执行下面的逻辑,保存视频但不包含封面
                 }
+            }
+            
+            // 组合成JSON对象格式(即使没有封面也保存)
+            JSONObject videoJson = new JSONObject();
+            videoJson.put("video", videoUrl);
+            if (StringUtils.isNotBlank(coverUrl)) {
+                videoJson.put("cover", coverUrl);
+            }
+            
+            storeVideo.setImgUrl(JSON.toJSONString(videoJson));
+            
+            videoList.add(storeVideo);
+            videoIndex++;
+        }
+
+        // 批量保存或更新
+        if (videoList.isEmpty()) {
+            log.warn("没有有效的视频数据需要保存");
+            return false;
+        }
+
+        boolean saveResult;
+        // 分离新增和更新的视频
+        List<StoreVideo> insertList = new java.util.ArrayList<>();
+        List<StoreVideo> updateList = new java.util.ArrayList<>();
+        
+        for (StoreVideo video : videoList) {
+            if (video.getId() != null && video.getId() > 0) {
+                updateList.add(video);
+            } else {
+                insertList.add(video);
+            }
+        }
+        
+        // 执行新增和更新
+        try {
+            if (!insertList.isEmpty()) {
+                if (insertList.size() == 1) {
+                    saveResult = super.save(insertList.get(0));
+                } else {
+                    saveResult = super.saveBatch(insertList);
+                }
+                log.info("新增视频数量:{}", insertList.size());
+            } else {
+                saveResult = true; // 如果没有新增,默认成功
+            }
+            
+            if (!updateList.isEmpty()) {
+                boolean updateResult;
+                if (updateList.size() == 1) {
+                    updateResult = super.updateById(updateList.get(0));
+                } else {
+                    updateResult = super.updateBatchById(updateList);
+                }
+                saveResult = saveResult && updateResult;
+                log.info("更新视频数量:{}", updateList.size());
+            }
+        } catch (Exception e) {
+            log.error("保存或更新视频失败,错误:{}", e.getMessage(), e);
+            return false;
+        }
+
+        // 保存成功后,更新相册的imgCount
+        if (saveResult && dto.getBusinessId() != null && dto.getBusinessId() > 0) {
+            try {
+                updateAlbumImgCount(dto.getBusinessId());
             } catch (Exception e) {
-                log.error("StoreVideoServiceImpl.save 处理视频封面失败, imgUrl: {}", entity.getImgUrl(), e);
-                // 如果处理失败,记录错误日志但不影响保存操作
+                log.error("更新相册imgCount失败,相册ID:{},错误:{}", dto.getBusinessId(), e.getMessage(), e);
+                // 不影响保存结果,只记录错误日志
             }
         }
 
-        // 调用父类的save方法保存
-        return super.save(entity);
+        return saveResult;
+    }
+
+    /**
+     * 更新相册的imgCount(统计该相册下的视频数量)
+     *
+     * @param albumId 相册ID
+     */
+    private void updateAlbumImgCount(Integer albumId) {
+        try {
+            // 统计该相册下的视频数量
+            LambdaQueryWrapper<StoreVideo> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.eq(StoreVideo::getBusinessId, albumId)
+                    .eq(StoreVideo::getDeleteFlag, CommonConstant.DELETE_FLAG_UNDELETE);
+            long videoCount = this.count(queryWrapper);
+
+            // 更新相册的imgCount
+            LambdaUpdateWrapper<StoreOfficialAlbum> updateWrapper = new LambdaUpdateWrapper<>();
+            updateWrapper.eq(StoreOfficialAlbum::getId, albumId)
+                    .set(StoreOfficialAlbum::getImgCount, (int) videoCount);
+            storeOfficialAlbumMapper.update(null, updateWrapper);
+
+            log.info("更新相册imgCount成功,相册ID:{},视频数量:{}", albumId, videoCount);
+        } catch (Exception e) {
+            log.error("更新相册imgCount失败,相册ID:{},错误:{}", albumId, e.getMessage(), e);
+            throw e;
+        }
     }
 
     /**
-     * 处理视频URL,截取第一帧作为封面并生成JSON数组
+     * 处理视频URL,截取第一帧作为封面并生成JSON对象
+     * 只支持JSON对象格式:{"video": "...", "cover": "..."}
      *
-     * @param imgUrl 视频URL或JSON字符串
-     * @return 处理后的JSON数组字符串
+     * @param imgUrl 视频URL或JSON对象字符串
+     * @return 处理后的JSON对象字符串,格式:{"video": "...", "cover": "..."}
      */
     private String processVideoAndCover(String imgUrl) {
         log.info("StoreVideoServiceImpl.processVideoAndCover imgUrl={}", imgUrl);
@@ -98,20 +276,18 @@ public class StoreVideoServiceImpl extends ServiceImpl<StoreVideoMapper, StoreVi
         }
 
         String videoUrl = null;
+        String coverUrl = null;
 
-        // 判断imgUrl是否为JSON格式
+        // 判断imgUrl是否为JSON对象格式
         try {
-            JSONArray jsonArray = JSONArray.parseArray(imgUrl);
-            // 如果已经是JSON数组格式,检查是否包含video和cover
-            if (jsonArray != null && !jsonArray.isEmpty()) {
-                JSONObject firstItem = jsonArray.getJSONObject(0);
-                if (firstItem != null && firstItem.containsKey("video")) {
-                    videoUrl = firstItem.getString("video");
-                    // 如果已经有cover,则直接返回
-                    if (firstItem.containsKey("cover") && StringUtils.isNotBlank(firstItem.getString("cover"))) {
-                        log.info("StoreVideoServiceImpl.processVideoAndCover 已有封面,无需重新生成");
-                        return imgUrl;
-                    }
+            JSONObject jsonObject = JSON.parseObject(imgUrl);
+            if (jsonObject != null && jsonObject.containsKey("video")) {
+                videoUrl = jsonObject.getString("video");
+                coverUrl = jsonObject.getString("cover");
+                // 如果已经有cover,则直接返回
+                if (StringUtils.isNotBlank(coverUrl)) {
+                    log.info("StoreVideoServiceImpl.processVideoAndCover 已有封面,无需重新生成");
+                    return imgUrl;
                 }
             }
         } catch (Exception e) {
@@ -159,21 +335,19 @@ public class StoreVideoServiceImpl extends ServiceImpl<StoreVideoMapper, StoreVi
             // 上传封面到OSS
             String coverFileName = generateCoverFileName(videoUrl);
             String coverOssPath = "video/" + coverFileName + ".jpg";
-            String coverUrl = aliOSSUtil.uploadFile(coverFile, coverOssPath);
+            coverUrl = aliOSSUtil.uploadFile(coverFile, coverOssPath);
             if (StringUtils.isBlank(coverUrl)) {
                 log.error("StoreVideoServiceImpl.processVideoAndCover 上传封面失败: {}", coverOssPath);
                 return imgUrl;
             }
 
-            // 构建JSON数组
-            JSONArray resultArray = new JSONArray();
-            JSONObject videoObject = new JSONObject();
-            videoObject.put("video", videoUrl);
-            videoObject.put("cover", coverUrl);
-            resultArray.add(videoObject);
+            // 构建JSON对象(格式:{"video": "...", "cover": "..."})
+            JSONObject resultObject = new JSONObject();
+            resultObject.put("video", videoUrl);
+            resultObject.put("cover", coverUrl);
 
             log.info("StoreVideoServiceImpl.processVideoAndCover 处理成功, videoUrl: {}, coverUrl: {}", videoUrl, coverUrl);
-            return JSON.toJSONString(resultArray);
+            return JSON.toJSONString(resultObject);
 
         } catch (Exception e) {
             log.error("StoreVideoServiceImpl.processVideoAndCover 处理异常", e);
@@ -387,5 +561,41 @@ public class StoreVideoServiceImpl extends ServiceImpl<StoreVideoMapper, StoreVi
                 .orderByAsc(StoreVideo::getImgSort);
         return this.list(lambdaQueryWrapper);
     }
+
+    /**
+     * 尝试根据视频URL匹配封面URL
+     * 从/file/uploadMore接口获取的封面URL格式:视频URL的扩展名从.mp4等替换为.jpg
+     *
+     * @param videoUrl 视频URL
+     * @return 封面URL,如果匹配不到则返回null
+     */
+    private String tryMatchCoverUrl(String videoUrl) {
+        if (StringUtils.isBlank(videoUrl)) {
+            return null;
+        }
+        
+        try {
+            // 检查是否为视频URL
+            if (!isVideoUrl(videoUrl)) {
+                return null;
+            }
+            
+            // 提取视频文件名(不含扩展名)
+            int lastDotIndex = videoUrl.lastIndexOf('.');
+            int lastSlashIndex = Math.max(videoUrl.lastIndexOf('/'), videoUrl.lastIndexOf('\\'));
+            
+            if (lastDotIndex > lastSlashIndex && lastDotIndex > 0) {
+                // 构建封面URL:将扩展名替换为.jpg
+                String coverUrl = videoUrl.substring(0, lastDotIndex) + ".jpg";
+                log.debug("尝试匹配封面URL,视频URL:{},封面URL:{}", videoUrl, coverUrl);
+                return coverUrl;
+            }
+        } catch (Exception e) {
+            log.debug("匹配封面URL失败,视频URL:{},错误:{}", videoUrl, e.getMessage());
+        }
+        
+        return null;
+    }
+
 }