Преглед на файлове

新增合并文件接口(Dev)

tsurumure преди 3 месеца
родител
ревизия
83bf5f7130

+ 23 - 2
src/main/java/com/backendsys/modules/upload/controller/SysFileController.java

@@ -9,6 +9,7 @@ import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.common.utils.Result;
 import com.backendsys.modules.upload.entity.SysFile;
+import com.backendsys.modules.upload.entity.SysFileMergeByMd5;
 import com.backendsys.modules.upload.service.SysFileService;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import io.swagger.v3.oas.annotations.Operation;
@@ -123,12 +124,32 @@ public class SysFileController {
         // 权限:
         // - 删除自己 (需权限) (1.1.6)
         // - 删除全部 (需要子权限或超级管理员) (1.1.5)
-        List<Long> userIdList = querySysFileList.stream().map(SysFile::getUser_id).collect(Collectors.toList());
-        if (userIdList.contains(SecurityUtil.getUserId()) && !securityUtil.hasPermission("1.1.5")) {
+        List<Long> sysFileListByUserId = querySysFileList.stream().map(SysFile::getUser_id).collect(Collectors.toList());
+        if (sysFileListByUserId.contains(SecurityUtil.getUserId()) && !securityUtil.hasPermission("1.1.5")) {
             throw new CustException(SecurityEnum.NOAUTH);
         }
 
         return Result.success().put("data", sysFileService.removeUploadFileBatch(sysFile, querySysFileList));
     }
 
+
+
+
+    @Operation(summary = "根据 MD5 获取文件列表 (我的)")
+    @PreAuthorize("@sr.hasPermission('1.1.2')")
+    @GetMapping("/api/upload/getUploadFileListByMd5")
+    public Result getUploadFileListByMd5(@Validated(SysFile.SelectByMd5.class) SysFile sysFile) {
+        sysFile.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", sysFileService.getUploadFileListByMd5(sysFile));
+    }
+
+    @Operation(summary = "合并重复 MD5 文件")
+    @PreAuthorize("@sr.hasPermission('1.1.4')")
+    @PutMapping("/api/upload/mergeFileByMd5")
+    public Result mergeFileByMd5(@Validated(SysFileMergeByMd5.MergeByMd5.class) @RequestBody SysFileMergeByMd5 sysFileMergeByMd5) {
+        sysFileMergeByMd5.setUser_id(SecurityUtil.getUserId());
+        return Result.success().put("data", sysFileService.mergeFileByMd5(sysFileMergeByMd5));
+    }
+
+
 }

+ 4 - 0
src/main/java/com/backendsys/modules/upload/dao/SysFileDao.java

@@ -5,10 +5,14 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
+import java.util.Map;
 
 @Mapper
 public interface SysFileDao extends BaseMapper<SysFile> {
 
+    // 获取文件列表 (简单查询)
+    List<Map<String, Object>> selectUploadFileSimple(SysFile sysFile);
+
     // 获取文件列表
     List<SysFile> selectUploadFileList(SysFile sysFile);
 

+ 3 - 0
src/main/java/com/backendsys/modules/upload/entity/SysFile.java

@@ -16,6 +16,7 @@ public class SysFile {
     public static interface UpdateBatch{}
     public static interface Delete{}
     public static interface DeleteBatch{}
+    public static interface SelectByMd5{}
 
     @TableId(type = IdType.AUTO)
     @NotNull(message = "id 不能为空", groups = { Update.class })
@@ -56,6 +57,8 @@ public class SysFile {
     private List<String> object_keys;
 
     private Long size;
+
+    @NotEmpty(message = "md5 不能为空", groups = { SelectByMd5.class })
     private String md5;
     private Integer target;         // 上传目标 (-1:本地, 1:腾讯云, 2:阿里云, 3.抖音云)
     @TableField(exist = false)

+ 27 - 0
src/main/java/com/backendsys/modules/upload/entity/SysFileMergeByMd5.java

@@ -0,0 +1,27 @@
+package com.backendsys.modules.upload.entity;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class SysFileMergeByMd5 {
+
+    public static interface MergeByMd5{}
+
+    private Long user_id;
+    @NotEmpty(message = "md5 不能为空", groups = { MergeByMd5.class })
+    private String md5;
+    @NotNull(message = "object_keys 不能为空", groups = { MergeByMd5.class })
+    @Size(min = 1, message = "object_keys 的数量必须大于 0", groups = { MergeByMd5.class })
+    private List<String> object_keys;
+    @NotEmpty(message = "合并后的文件名不能为空", groups = { MergeByMd5.class })
+    @Size(max = 50, message = "文件名长度不超过 {max} 字符", groups = { MergeByMd5.class })
+    private String target_file_name;
+    @NotEmpty(message = "合并后的文件 object_key 不能为空", groups = { MergeByMd5.class })
+    private String target_object_key;
+
+}

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

@@ -1,6 +1,7 @@
 package com.backendsys.modules.upload.service;
 
 import com.backendsys.modules.upload.entity.SysFile;
+import com.backendsys.modules.upload.entity.SysFileMergeByMd5;
 import com.backendsys.utils.response.PageEntity;
 import com.baomidou.mybatisplus.extension.service.IService;
 import org.springframework.web.multipart.MultipartFile;
@@ -28,4 +29,12 @@ public interface SysFileService extends IService<SysFile> {
 
     // 编辑文件 (批量)
     Map<String, Object> updateUploadFileBatch(SysFile sysFile);
+
+
+    // 根据 MD5 获取文件列表 (我的)
+    List<Map<String, Object>> getUploadFileListByMd5(SysFile sysFile);
+    // 合并重复 MD5 文件
+    Map<String, Object> mergeFileByMd5(SysFileMergeByMd5 sysFileMergeByMd5);
+
+
 }

+ 2 - 1
src/main/java/com/backendsys/modules/upload/service/impl/SysFileMultipartServiceImpl.java

@@ -22,6 +22,7 @@ import com.backendsys.modules.upload.entity.MultipartUploadParams;
 import com.backendsys.modules.upload.entity.SysFile;
 import com.backendsys.modules.upload.enums.StyleEnums;
 import com.backendsys.modules.upload.service.SysFileMultipartService;
+import com.backendsys.utils.response.ResultEnum;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.qcloud.cos.model.*;
 import com.volcengine.tos.model.object.CompleteMultipartUploadV2Output;
@@ -188,7 +189,7 @@ public class SysFileMultipartServiceImpl implements SysFileMultipartService {
             // - 异常情况:如果有两个相同的文件,一个上传50%,一个上传成功,就会出现以下这种情况,需要手动解决
             List<SysFile> sysFileEntityList = sysFileDao.selectList(wrapperFile);
             if (sysFileEntityList != null && sysFileEntityList.size() > 1) {
-                throw new CustException("存在 " + sysFileEntityList.size() + " 个相同MD5 (" + multipartUploadParams.getMd5() + ") 的文件,请删除后再重新上传");
+                throw new CustException("存在 " + sysFileEntityList.size() + " 个相同MD5 (" + multipartUploadParams.getMd5() + ") 的文件", ResultEnum.STATUS_CONFLICT.getCode());
             }
 
             if (sysFileEntityList != null && sysFileEntityList.size() > 0) {

+ 82 - 2
src/main/java/com/backendsys/modules/upload/service/impl/SysFileServiceImpl.java

@@ -1,12 +1,14 @@
 package com.backendsys.modules.upload.service.impl;
 
 import cn.hutool.core.codec.Base64;
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.digest.DigestUtil;
 import cn.hutool.json.JSONArray;
 import com.backendsys.exception.CustException;
+import com.backendsys.modules.common.config.security.enums.SecurityEnum;
 import com.backendsys.modules.common.config.security.utils.HttpRequestUtil;
 import com.backendsys.modules.common.config.security.utils.SecurityUtil;
 import com.backendsys.modules.sdk.douyincloud.tos.service.DouyinTosService;
@@ -17,12 +19,14 @@ import com.backendsys.modules.upload.dao.SysFileCategoryDao;
 import com.backendsys.modules.upload.dao.SysFileDao;
 import com.backendsys.modules.upload.entity.SysFile;
 import com.backendsys.modules.upload.entity.SysFileCategory;
+import com.backendsys.modules.upload.entity.SysFileMergeByMd5;
 import com.backendsys.modules.upload.entity.SysFileResult;
 import com.backendsys.modules.upload.enums.StyleEnums;
 import com.backendsys.modules.upload.service.SysFileService;
 import com.backendsys.modules.upload.utils.UploadUtil;
 import com.backendsys.utils.response.PageEntity;
 import com.backendsys.utils.response.PageInfoResult;
+import com.backendsys.utils.response.ResultEnum;
 import com.backendsys.utils.v2.PageUtils;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -30,6 +34,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -44,7 +49,8 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
 
     @Autowired
     private HttpRequestUtil httpRequestUtil;
-
+    @Autowired
+    private SecurityUtil securityUtil;
     @Autowired
     private TencentCosService tencentCosService;
     @Autowired
@@ -270,9 +276,10 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
                 LambdaQueryWrapper<SysFile> wrapperFile = new LambdaQueryWrapper<>();
                 wrapperFile.eq(SysFile::getMd5, md5);
                 wrapperFile.eq(SysFile::getUser_id, SecurityUtil.getUserId());
+                wrapperFile.isNull(SysFile::getUpload_id);
                 List<SysFile> sysFileEntityList = sysFileDao.selectList(wrapperFile);
                 if (sysFileEntityList != null && sysFileEntityList.size() > 1) {
-                    throw new CustException("存在 " + sysFileEntityList.size() + " 个相同Md5 (" + md5 + ") 的文件,请删除后再重新上传");
+                    throw new CustException("存在 " + sysFileEntityList.size() + " 个相同MD5 (" + md5 + ") 的文件", ResultEnum.STATUS_CONFLICT.getCode());
                 }
 
                 if (sysFileEntityList != null && sysFileEntityList.size() > 0) {
@@ -427,4 +434,77 @@ public class SysFileServiceImpl extends ServiceImpl<SysFileDao, SysFile> impleme
         return Map.of("ids", ids);
     }
 
+    /**
+     * 根据 MD5 获取文件列表 (我的)
+     */
+    @Override
+    public List<Map<String, Object>> getUploadFileListByMd5(SysFile sysFile) {
+        SysFile entity = new SysFile();
+        entity.setUser_id(sysFile.getUser_id());
+        entity.setUpload_id("");
+        entity.setMd5(sysFile.getMd5());
+        return sysFileDao.selectUploadFileSimple(entity);
+    }
+
+    /**
+     * 合并重复 MD5 文件 { md5, ids }
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> mergeFileByMd5(SysFileMergeByMd5 sysFileMergeByMd5) {
+
+        Long user_id = sysFileMergeByMd5.getUser_id();
+        List<String> object_keys = sysFileMergeByMd5.getObject_keys();
+        String target_object_key = sysFileMergeByMd5.getTarget_object_key();
+        String target_file_name = sysFileMergeByMd5.getTarget_file_name();
+
+        // 判断 target_object 是否存在于 object_keys 中
+        boolean exists = CollectionUtil.contains(object_keys, target_object_key);
+        if (!exists) throw new CustException("合并目标文件ID不存在于列表中");
+
+        // 判断权限 (需要子权限或超级管理员)
+        if (!SecurityUtil.isSuper() && !securityUtil.hasPermission("1.1.5")) {
+            throw new CustException(SecurityEnum.NOAUTH);
+        }
+
+
+
+        // --------------------------------------------------------------------------------
+        // 更新文件命名
+
+
+
+
+        // --------------------------------------------------------------------------------
+        // 过滤一个没有 target_object_key 的 object_keys 集合
+        List<String> file_object_keys_without_target = object_keys.stream().filter(object_key -> !object_key.equals(target_object_key)).collect(Collectors.toList());
+
+        // 查询文件列表 (不包含 target_object_key、当前用户ID、upload_id 为空)
+        LambdaQueryWrapper<SysFile> wrapperFile = new LambdaQueryWrapper<>();
+        wrapperFile.in(SysFile::getUser_id, user_id);
+        wrapperFile.in(SysFile::getObject_key, file_object_keys_without_target);
+        wrapperFile.isNull(SysFile::getUpload_id);
+        List<SysFile> querySysFileList = sysFileDao.selectList(wrapperFile);
+
+        // 筛选出待删除的文件 object_key
+        List<String> delete_object_keys = querySysFileList.stream().map(entity -> entity.getObject_key()).collect(Collectors.toList());
+
+        // [Delete] 批量删除
+        sysFileDao.delete(new LambdaQueryWrapper<SysFile>().in(SysFile::getObject_key, delete_object_keys));
+
+        // [异步任务] 创建一个 CompletableFuture 来执行异步任务
+        CompletableFuture.runAsync(() -> {
+            querySysFileList.stream().forEach(entity -> {
+                deleteObject(entity.getObject_key(), entity.getTarget());
+            });
+        });
+        // --------------------------------------------------------------------------------
+
+        Map<String, Object> resp = new LinkedHashMap<>();
+        resp.put("delete_object_keys", delete_object_keys);
+        resp.put("target_object_key", target_object_key);
+
+        return resp;
+    }
+
 }

+ 37 - 1
src/main/resources/mapper/upload/SysFileDao.xml

@@ -2,6 +2,16 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 <mapper namespace="com.backendsys.modules.upload.dao.SysFileDao">
 
+    <sql id="includeFileSimple">
+        id,
+        upload_id,
+        name,
+        object_key,
+        md5,
+        upload_time,
+        create_time,
+        update_time
+    </sql>
     <sql id="includeFile">
         f.id,
         COALESCE(f.category_id, '') category_id,
@@ -25,7 +35,16 @@
         f.update_time
     </sql>
 
-    <!-- type="java.util.LinkedHashMap" -->
+    <resultMap id="resultMapFileSimple" type="java.util.LinkedHashMap">
+        <id property="id" column="id" jdbcType="BIGINT" />
+        <result property="upload_id" column="upload_id" />
+        <result property="name" column="name" />
+        <result property="object_key" column="object_key" />
+        <result property="md5" column="md5" />
+        <result property="upload_time" column="upload_time" />
+        <result property="create_time" column="create_time" />
+        <result property="update_time" column="update_time" />
+    </resultMap>
     <resultMap id="resultMapFileList" type="com.backendsys.modules.upload.entity.SysFile">
         <id property="id" column="id" jdbcType="BIGINT" />
         <result property="category_id" column="category_id" javaType="java.lang.Long" />
@@ -49,6 +68,23 @@
         <result property="update_time" column="update_time" />
     </resultMap>
 
+    <select id="selectUploadFileSimple" resultMap="resultMapFileSimple">
+        SELECT <include refid="includeFileSimple" />
+        FROM sys_file
+        <where>
+            <if test="user_id != null and user_id != ''">
+                AND user_id = #{user_id}
+            </if>
+            <if test="upload_id != ''">
+                AND upload_id = #{upload_id}
+            </if>
+            <if test="object_key != null and object_key != ''">
+                AND object_key = #{object_key}
+            </if>
+        </where>
+        ORDER BY upload_time DESC
+    </select>
+
     <select id="selectUploadFileList" resultMap="resultMapFileList">
         SELECT <include refid="includeFile" />
         FROM sys_file f