package com.vctgo.file.service.impl;

import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.nacos.shaded.com.google.common.base.Joiner;
import com.alibaba.nacos.shaded.com.google.common.base.Splitter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vctgo.common.core.constant.StatusConstants;
import com.vctgo.common.core.exception.ServiceException;
import com.vctgo.common.core.text.Convert;
import com.vctgo.common.core.utils.DateUtils;
import com.vctgo.common.core.utils.ServletUtils;
import com.vctgo.common.core.utils.StrUtils;
import com.vctgo.common.core.web.domain.AjaxResult;
import com.vctgo.common.mybatisplus.constant.MybatisPageConstants;
import com.vctgo.common.security.utils.SecurityUtils;
import com.vctgo.file.domain.FileReShareInfo;
import com.vctgo.file.domain.FileShare;
import com.vctgo.file.domain.MinioInfo;
import com.vctgo.file.mapper.FileInfoMapper;
import com.vctgo.file.service.ICloudStorageFileService;
import com.vctgo.file.service.IDistributedFileSystemService;
import com.vctgo.file.service.IFileReShareInfoService;
import com.vctgo.file.service.IFileShareService;
import com.vctgo.file.utils.ConstantUtils;
import com.vctgo.file.utils.FileUtils;
import com.vctgo.file.utils.SnowflakeIdGenerator;
import com.vctgo.system.api.domain.FileInfo;
import com.vctgo.system.api.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CloudStorageFileServiceImpl extends ServiceImpl<FileInfoMapper, FileInfo> implements ICloudStorageFileService {
    private static final ConcurrentHashMap<String, Integer> MAP = new ConcurrentHashMap<>();
    @Autowired
    private MinioInfo minioInfo;
    @Autowired
    private IDistributedFileSystemService iDistributedFileSystemService;
    @Autowired
    private IFileReShareInfoService iFileReShareInfoService;
    @Autowired
    private IFileShareService iFileShareService;
    @Autowired
    private FileInfoMapper fileInfoMapper;

    /**
     * 云盘文件上传请求
     */
    @Override
    public AjaxResult upload(MultipartFile file) {
        try {
            // 上传并返回访问地址
            String url = iDistributedFileSystemService.uploadFile(file);
            FileInfo fileInfo = getFileInfo(SecurityUtils.getTenantId(), 0L, null, file, url);
            fileInfoMapper.insert(fileInfo);
            return AjaxResult.success(fileInfo);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            throw new ServiceException("上传文件失败");
        }
    }

    /**
     * 通过租户Id和文件夹Id在云盘文件上传文件
     *
     * @param tenantId    租户id
     * @param directoryId 文件夹id
     * @param file        文件
     * @return
     */
    @Override
    public AjaxResult uploadByTenantIdAndDirectoryId(Long tenantId, Long directoryId, MultipartFile file) {
        try {
            FileInfo fileInfoParent = Objects.nonNull(directoryId) ? fileInfoMapper.selectById(directoryId) : null;

            // 上传并返回访问地址
            String url = iDistributedFileSystemService.uploadFile(file);
            FileInfo fileInfo = getFileInfo(SecurityUtils.getTenantId(), directoryId, null, file, url);
            if (Objects.isNull(fileInfoParent)) {
                fileInfo.setAncestors(directoryId + "");
            } else {
                fileInfo.setAncestors(String.join(",", fileInfoParent.getAncestors(), directoryId + ""));
            }
            fileInfoMapper.insert(fileInfo);
            return AjaxResult.success(fileInfo);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            throw new ServiceException("上传文件失败");
        }
    }

    /**
     * 通过文件夹Id在云盘文件上传文件
     *
     * @param directoryId 文件夹id
     * @param file        文件
     * @return
     */
    @Override
    public AjaxResult uploadByDirectoryId(Long directoryId, String serviceName, MultipartFile file) {
        try {
            //父文件夹
            FileInfo fileInfoParent = fileInfoMapper.selectById(directoryId);
            //文件路径
            String url = iDistributedFileSystemService.uploadFile(file);
            //文件信息
            FileInfo fileInfo = getFileInfo(SecurityUtils.getTenantId(), directoryId, serviceName, file, url);
            //文件名重复命名规则
            List<FileInfo> fileInfoList = fileInfoMapper.selectList(new QueryWrapper<FileInfo>().lambda()
                    .eq(FileInfo::getTenantId, SecurityUtils.getTenantId())
                    .eq(FileInfo::getServiceName, serviceName)
                    .eq(FileInfo::getFileParentId, directoryId)
                    .eq(FileInfo::getFileName, fileInfo.getFileName())
                    .eq(FileInfo::getFileExtension, fileInfo.getFileExtension())
                    .eq(FileInfo::getFileIsDirectory, ConstantUtils.IS_DIRECTORY_FILE_0)
                    .eq(FileInfo::getFileIsHide, ConstantUtils.IS_DELETED_0)
                    .eq(FileInfo::getIsDeleted, ConstantUtils.IS_DELETED_0));
            if (!CollectionUtils.isEmpty(fileInfoList)) {
                fileInfo.setFileFullName(fileInfo.getFileName() + "_" + System.currentTimeMillis() + "." + fileInfo.getFileExtension());
                fileInfo.setFileName(fileInfo.getFileFullName().replace("." + fileInfo.getFileExtension(), ""));
            }
            fileInfo.setAncestors(String.join(",", fileInfoParent.getAncestors(), directoryId + ""));
            fileInfoMapper.insert(fileInfo);

            //父文件夹修改时间变更
            fileInfoParent.setUpdateById(SecurityUtils.getUserId());
            fileInfoParent.setUpdateTime(new Date());
            fileInfoMapper.updateById(fileInfoParent);
            return AjaxResult.success(fileInfo);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            throw new ServiceException("上传文件失败");
        }
    }

    /**
     * 创建文件夹
     */
    @Override
    public FileInfo mkdir(FileInfo reqFileInfo) {
        FileInfo fileInfoParent = StrUtils.isNotEmpty(reqFileInfo.getFileParentId().toString()) ? fileInfoMapper.selectById(reqFileInfo.getFileParentId()) : null;
        LoginUser loginUser = SecurityUtils.getLoginUser();

        //创建文件夹
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileName(reqFileInfo.getFileName());
        fileInfo.setFileFullName(reqFileInfo.getFileName());
        fileInfo.setServiceName(reqFileInfo.getServiceName());
        fileInfo.setFileParentId(StrUtils.isEmpty(reqFileInfo.getFileParentId()) ? 0L : reqFileInfo.getFileParentId());
        fileInfo.setFileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_1);
        fileInfo.setFileIsHide(ConstantUtils.IS_DELETED_0);
        fileInfo.setFileStatus(ConstantUtils.SHOW_STATUS_FIX_10);
        fileInfo.setIsDeleted(ConstantUtils.IS_DELETED_0);
        fileInfo.setTenantId(SecurityUtils.getTenantId());
        if (Objects.isNull(fileInfoParent)) {
            fileInfo.setAncestors(reqFileInfo.getFileParentId().toString());
        } else {
            fileInfo.setAncestors(String.join(",", fileInfoParent.getAncestors(), reqFileInfo.getFileParentId().toString()));
        }
        doSetFileInfo(fileInfo, loginUser);
        fileInfoMapper.insert(fileInfo);

        //父文件夹修改时间变更
        if (Objects.nonNull(fileInfoParent)) {
            fileInfoParent.setUpdateById(SecurityUtils.getUserId());
            fileInfoParent.setUpdateTime(new Date());
            fileInfoMapper.updateById(fileInfoParent);
        }
        return fileInfo;
    }

    /**
     * 云盘通过文件对象创建文件夹
     *
     * @param parentId
     * @param fileInfo
     * @return
     */
    @Override
    public AjaxResult mkdirByFileInfo(String parentId, FileInfo fileInfo) {
        FileInfo fileInfoParent = StrUtils.isNotEmpty(parentId) ? fileInfoMapper.selectById(parentId) : null;
        LoginUser loginUser = SecurityUtils.getLoginUser();

        fileInfo.setFileParentId(StrUtils.isEmpty(parentId) ? 0L : Long.parseLong(parentId));
        fileInfo.setFileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_1);
        fileInfo.setFileIsHide(ConstantUtils.IS_DELETED_0);
        fileInfo.setFileStatus(ConstantUtils.SHOW_STATUS_FIX_10);
        fileInfo.setIsDeleted(ConstantUtils.IS_DELETED_0);
        fileInfo.setTenantId(SecurityUtils.getTenantId());
        if (Objects.isNull(fileInfoParent)) {
            fileInfo.setAncestors(parentId);
        } else {
            fileInfo.setAncestors(String.join(",", fileInfoParent.getAncestors(), parentId));
        }
        doSetFileInfo(fileInfo, loginUser);
        fileInfoMapper.insert(fileInfo);

        if (!CollectionUtils.isEmpty(fileInfo.getSysFileList())) {
            List<FileInfo> fileInfoList = new ArrayList<>();
            for (FileInfo info : fileInfo.getSysFileList()) {
                info.setFileParentId(fileInfo.getId());
                fileInfo.setAncestors(String.join(",", fileInfo.getAncestors(), fileInfo.getId() + ""));
                fileInfo.setFileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_1);
                fileInfo.setFileIsHide(ConstantUtils.IS_DELETED_0);
                fileInfo.setFileStatus(ConstantUtils.SHOW_STATUS_FIX_10);
                fileInfo.setIsDeleted(ConstantUtils.IS_DELETED_0);
                doSetFileInfo(info, loginUser);
                info.setTenantId(SecurityUtils.getTenantId());

                fileInfoList.add(info);
            }
            fileInfoMapper.insertBatch(fileInfoList);
            fileInfo.setSysFileList(fileInfoList);
        }
        return AjaxResult.success(fileInfo);
    }

    /**
     * 云盘创建文件夹
     */
    @Override
    public AjaxResult mkdirByTenantId(Long tenantId, String parentId, String dirName) {
        FileInfo fileInfoParent = StrUtils.isNotEmpty(parentId) ? fileInfoMapper.selectById(parentId) : null;

        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileName(dirName);
        fileInfo.setFileFullName(dirName);
        fileInfo.setFileParentId(StrUtils.isEmpty(parentId) ? 0L : Long.parseLong(parentId));
        fileInfo.setFileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_1);
        fileInfo.setFileIsHide(ConstantUtils.IS_DELETED_0);
        fileInfo.setFileStatus(ConstantUtils.SHOW_STATUS_FIX_10);
        fileInfo.setIsDeleted(ConstantUtils.IS_DELETED_0);
        LoginUser loginUser = SecurityUtils.getLoginUser();
        fileInfo.setCreateById(loginUser.getUserid());
        fileInfo.setCreateTime(new Date());
        fileInfo.setTenantId(tenantId);
        if (Objects.isNull(fileInfoParent)) {
            fileInfo.setAncestors(parentId);
        } else {
            fileInfo.setAncestors(String.join(",", fileInfoParent.getAncestors(), parentId));
        }
        return fileInfoMapper.insert(fileInfo) > 0 ? AjaxResult.success(fileInfo) : AjaxResult.error();
    }

    /**
     * 通过租户和文件对象创建文件夹
     *
     * @param tenantId 租户Id
     * @param parentId 文件夹父Id
     * @param fileInfo 文件夹名称
     * @return
     */
    @Override
    public AjaxResult mkdirByTenantAndFile(Long tenantId, String parentId, FileInfo fileInfo) {

        FileInfo fileInfoParent = StrUtils.isNotEmpty(parentId) ? fileInfoMapper.selectById(parentId) : null;
        fileInfo.setFileParentId(StrUtils.isEmpty(parentId) ? 0L : Long.parseLong(parentId));
        fileInfo.setFileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_1);
        fileInfo.setFileIsHide(ConstantUtils.IS_DELETED_0);
        fileInfo.setFileStatus(ConstantUtils.SHOW_STATUS_FIX_10);
        LoginUser loginUser = SecurityUtils.getLoginUser();
        fileInfo.setCreateById(loginUser.getUserid());
        fileInfo.setCreateTime(new Date());
        fileInfo.setTenantId(tenantId);
        if (Objects.isNull(fileInfoParent)) {
            fileInfo.setAncestors(parentId);
        } else {
            fileInfo.setAncestors(String.join(",", fileInfoParent.getAncestors(), parentId));
        }
        fileInfoMapper.insert(fileInfo);

        if (Objects.nonNull(fileInfo.getSysFileList()) && !fileInfo.getSysFileList().isEmpty()) {
            List<FileInfo> fileInfoList = new ArrayList<>();
            fileInfo.getSysFileList().forEach(fileInfoTmp -> {
                fileInfoTmp.setAncestors(String.join(",", fileInfo.getAncestors(), fileInfo.getId().toString()));
                fileInfoTmp.setFileParentId(fileInfo.getId());
                fileInfo.setFileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_1);
                fileInfo.setFileIsHide(ConstantUtils.IS_DELETED_0);
                fileInfo.setFileStatus(ConstantUtils.SHOW_STATUS_FIX_10);
                fileInfoTmp.setCreateById(loginUser.getUserid());
                fileInfoTmp.setCreateTime(new Date());
                fileInfoTmp.setTenantId(tenantId);
                fileInfoList.add(fileInfoTmp);
            });
            fileInfoMapper.insertBatch(fileInfoList);
            fileInfo.setSysFileList(fileInfoList);
        }
        return AjaxResult.success(fileInfo);
    }

    /**
     * 云盘通过文件id批量隐藏文件/文件夹---》回收站
     */
    @Override
    public AjaxResult hideFileByIds(List<FileInfo> hideFileList) {
        return fileInfoMapper.hideFileByIds(Joiner.on(",")
                .join(hideFileList.stream()
                        .map(FileInfo::getId)
                        .map(String::valueOf)
                        .collect(Collectors.toList())), SecurityUtils.getUserId()) > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /**
     * 云盘通过文件id批量恢复文件/文件夹
     */
    @Override
    public AjaxResult recoverFileByIds(List<FileInfo> recoverFileList) {

        // 获取该租户下所有的文件夹/文件
        List<String> ancestorsIdList = recoverFileList.stream().map(FileInfo::getAncestors).collect(Collectors.toList());
        List<String> fileIdList = recoverFileList.stream().map(FileInfo::getId).map(String::valueOf).collect(Collectors.toList());
        fileIdList.addAll(ancestorsIdList);

        // 将list中的值去重
        HashSet<String> idSet = new HashSet<>();
        Splitter.on(",").split(String.join(",", fileIdList)).forEach(idSet::add);
        fileInfoMapper.recoverFileByIds(Joiner.on(",").join(idSet), SecurityUtils.getUserId());
        return AjaxResult.success();
    }

    /**
     * 云盘通过文件id批量移动文件/文件夹
     */
    @Override
    public AjaxResult moveFileByIds(String ids, String toParentId) {
        FileInfo fileInfo = fileInfoMapper.selectById(toParentId);
        return AjaxResult.success(fileInfoMapper.moveFileByIds(ids, toParentId, StrUtils.join(fileInfo.getAncestors(), ",", toParentId), SecurityUtils.getUserId()));
    }

    /**
     * 云盘通过文件id分享文件/文件夹
     */
    @Override
    public AjaxResult shareFileFileShare(FileShare fileShare) {

        // 创建分享信息
        fileShare.setCreateById(SecurityUtils.getUserId());
        fileShare.setCreateTime(new Date());
        iFileShareService.insertFileShare(fileShare);

        // 创建文件信息与分享信息关联
        List<FileReShareInfo> fileReShareInfoList = new ArrayList<>();
        fileShare.getFileIdList().forEach(id -> fileReShareInfoList.add(FileReShareInfo.builder().fileInfoId(id).fileShareId(fileShare.getId()).build()));

        return iFileReShareInfoService.saveBatchFileReShareInfo(fileReShareInfoList) > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    @Override
    public AjaxResult cancelShareFile(Long id) {
        FileShare fileShare = iFileShareService.selectFileShareById(id);
        fileShare.setUpdateById(SecurityUtils.getUserId());
        fileShare.setUpdateTime(new Date());
        fileShare.setIsDeleted("1");
        return iFileShareService.updateFileShare(fileShare) > 0 ? AjaxResult.success() : AjaxResult.error();
    }

    /**
     * 云盘通过文件id彻底删除文件/文件夹
     */
    @Override
    public AjaxResult deleteFileByIds(List<FileInfo> deleteFileList) {

        // 获取该租户下所有的文件夹/文件
        LambdaQueryWrapper<FileInfo> fileInfoWrapper = new LambdaQueryWrapper<>();
        fileInfoWrapper.eq(FileInfo::getIsDeleted, "0");
        fileInfoWrapper.eq(FileInfo::getTenantId, SecurityUtils.getTenantId());
        fileInfoWrapper.select(FileInfo::getId, FileInfo::getFileParentId);
        List<FileInfo> fileInfoList = fileInfoMapper.selectList(fileInfoWrapper);

        ArrayList<FileInfo> fileInfoIdListTmp = new ArrayList<>();
        deleteFileList.forEach(fileInfo -> {
            // 通过Id查询出该目录或者该文件关联的所有文件
            if ("1".equals(fileInfo.getFileIsDirectory())) {
                fileInfoIdListTmp.addAll(getChildFileInfoByFileParentId(fileInfoList, fileInfo.getId()));
            } else {
                fileInfoIdListTmp.add(fileInfo);
            }
        });
        fileInfoIdListTmp.addAll(deleteFileList);
        fileInfoMapper.deleteFileByIds(Joiner.on(",").join(fileInfoIdListTmp.stream().map(FileInfo::getId).map(String::valueOf).collect(Collectors.toList())), SecurityUtils.getUserId());

        // 删除minio中的文件
        iDistributedFileSystemService.deleteMinioFile(fileInfoIdListTmp);
        return AjaxResult.success();
    }

    @Override
    public void deleteFileByFileStorageName(List<FileInfo> fileInfoIdListTmp) {
        // 删除minio中的文件
        iDistributedFileSystemService.deleteMinioFile(fileInfoIdListTmp);
    }

    @Override
    public AjaxResult innerDeleteFileByIds(List<FileInfo> deleteFileList) {

        // 获取该租户下所有的文件夹/文件
        LambdaQueryWrapper<FileInfo> fileInfoWrapper = new LambdaQueryWrapper<>();
        fileInfoWrapper.eq(FileInfo::getIsDeleted, "0");
        fileInfoWrapper.select(FileInfo::getId, FileInfo::getFileParentId);
        List<FileInfo> fileInfoList = fileInfoMapper.selectList(fileInfoWrapper);

        ArrayList<FileInfo> fileInfoIdListTmp = new ArrayList<>();
        deleteFileList.forEach(fileInfo -> {
            // 通过Id查询出该目录或者该文件关联的所有文件
            if ("1".equals(fileInfo.getFileIsDirectory())) {
                fileInfoIdListTmp.addAll(getChildFileInfoByFileParentId(fileInfoList, fileInfo.getId()));
            } else {
                fileInfoIdListTmp.add(fileInfo);
            }
        });
        fileInfoIdListTmp.addAll(deleteFileList);
        fileInfoMapper.deleteFileByIds(Joiner.on(",").join(fileInfoIdListTmp.stream().map(FileInfo::getId).map(String::valueOf).collect(Collectors.toList())), SecurityUtils.getUserId());
        return AjaxResult.success();
    }

    /**
     * 云盘获取列表，文件和文件夹融合不分开
     */
    @Override
    public AjaxResult listFileInfo(FileInfo fileInfo) {
        LambdaQueryWrapper<FileInfo> fileInfoLambdaWrapper = new LambdaQueryWrapper<>();
        fileInfoLambdaWrapper.eq(FileInfo::getIsDeleted, "0");
        fileInfoLambdaWrapper.orderByDesc(FileInfo::getCreateTime);
        return AjaxResult.success(fileInfoMapper.selectList(fileInfoLambdaWrapper));
    }

    /**
     * 分页--云盘获取列表，文件和文件夹融合不分开
     */
    @Override
    public IPage<FileInfo> pageFileInfo(FileInfo fileInfo) {

        if (Objects.isNull(fileInfo.getFileParentId())) {
            fileInfo.setFileParentId(0L);
        }
        Page mpPage = new Page(Convert.toLong(ServletUtils.getParameterToInt(MybatisPageConstants.PAGE_NUM), 1L), Convert.toLong(ServletUtils.getParameterToInt(MybatisPageConstants.PAGE_SIZE), 10L));
        IPage<FileInfo> fileInfoIPage = fileInfoMapper.selectFileInfoList(mpPage, fileInfo);
        if (!fileInfoIPage.getRecords().isEmpty()) {
            fileInfoIPage.getRecords().forEach(fileInfoTmp -> fileInfoTmp
                    .setFileSizeBytes(Objects.nonNull(fileInfoTmp.getFileSize()) ? FileUtils.convertBytes(fileInfoTmp.getFileSize()) : ""));
        }
        return fileInfoIPage;
    }

    @Override
    public IPage<FileInfo> innerListFile(FileInfo fileInfo) {
        Page mpPage = new Page(Convert.toLong(ServletUtils.getParameterToInt(MybatisPageConstants.PAGE_NUM), 1L), Convert.toLong(ServletUtils.getParameterToInt(MybatisPageConstants.PAGE_SIZE), 10L));
        IPage<FileInfo> fileInfoIPage = fileInfoMapper.selectFileInfoList(mpPage, fileInfo);
        if (!fileInfoIPage.getRecords().isEmpty()) {
            fileInfoIPage.getRecords().forEach(fileInfoTmp -> fileInfoTmp
                    .setFileSizeBytes(Objects.nonNull(fileInfoTmp.getFileSize()) ? FileUtils.convertBytes(fileInfoTmp.getFileSize()) : ""));
        }
        return fileInfoIPage;
    }

    /**
     * 批量下载
     *
     * @param: filenames: 多个文件名称
     * @Param: zip: 压缩包名称
     * @Param: res: 响应对象
     * @return: void
     **/
    @Override
    public AjaxResult batchDownload(List<FileInfo> batchDownloadFileList, HttpServletResponse response, HttpServletRequest request) {

        // 通过文件Id筛选出文件
        FileInfo fileInfoFiler = new FileInfo();
        fileInfoFiler.setFileIdList(batchDownloadFileList.stream().map(FileInfo::getId).collect(Collectors.toList()));
        List<FileInfo> fileInfoList = fileInfoMapper.selectFileInfoList(fileInfoFiler);
        List<FileInfo> fileList = treeToList(fileInfoList);

        // 判断总的大小是否大于100MB
        long sum = fileList.stream().filter(fileInfo -> Objects.nonNull(fileInfo.getFileSize())).mapToLong(FileInfo::getFileSize).sum();
        if (Math.round(sum / (1024.0 * 1024.0)) >= 100) {
            throw new ServiceException("批量下载最大支持100M以内");
        }

        // 判断大小
        if (fileList.stream().filter(fileInfo -> "0".equals(fileInfo.getFileIsDirectory())).count() > 50) {
            throw new ServiceException("批量下载最多支持50个文件");
        }

        // 将列表转换为树状
        List<FileInfo> fileInfoListTmp = new ArrayList<>();
        batchDownloadFileList.forEach(batchDownloadFileInfo -> {
            Optional<FileInfo> fileInfoOptional = fileList.stream().filter(fileInfo -> fileInfo.getId().longValue() == batchDownloadFileInfo.getId().longValue()).findFirst();
            if (fileInfoOptional.isPresent()) {
                FileInfo fileInfo = fileInfoOptional.get();
                fileInfoListTmp.add("1".equals(fileInfo.getFileIsDirectory()) ? fileInfo.setSysFileList(convertFileListToFileTree(fileList, fileInfo.getId())) : fileInfo);
            }
        });
        String fileName = URLDecoder.decode(request.getHeader("Filename")).replace(".zip", "") + "_" + SnowflakeIdGenerator.instance.nextId() + ".zip";
        MAP.put(fileName, StatusConstants.START);
        iDistributedFileSystemService.batchDownload(fileInfoListTmp, fileName, response, request, MAP);
        return AjaxResult.success("", fileName);
    }

    @Override
    public void downloadByFileName(String fileName, HttpServletResponse response, HttpServletRequest request) throws IOException {
        boolean isContains = MAP.containsKey(fileName);
        int status = MAP.get(fileName);
        if (isContains && StatusConstants.COMPLETED == status) {
            String fileTmpName = (SystemUtils.IS_OS_WINDOWS ? "D:\\example\\" : "/home/vctgo/uploadPath/") + fileName;
            File file = new File(fileTmpName);
            if (file.exists()) {
                try (OutputStream outputStream = response.getOutputStream(); InputStream inputStream = Files.newInputStream(file.toPath())) {
                    response.setHeader("Content-Length", "" + inputStream.available());
                    response.setCharacterEncoding("utf-8");
                    //文件在服务器的路径
                    response.setHeader("Content-disposition", "attachment;fileName=" + fileName);
                    byte[] buffer = new byte[1024];
                    int length = 0;
                    while ((length = inputStream.read(buffer)) > 0) {
                        outputStream.write(buffer, 0, length);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    JSONObject result = new JSONObject();
                    result.put("code", HttpStatus.HTTP_INTERNAL_ERROR);
                    result.put("message", "文件下载失败");
                    IOUtils.write(result.toString(), response.getOutputStream(), StandardCharsets.UTF_8);
                }
                file.deleteOnExit();
            }
        } else if (isContains && Objects.nonNull(status)) {
            JSONObject result = new JSONObject();
            result.put("code", status);
            result.put("message", status == StatusConstants.START ? "文件开始压缩" : status == StatusConstants.UNDER_WAY ? "文件正在压缩" : "文件压缩失败");
            IOUtils.write(result.toString(), response.getOutputStream(), StandardCharsets.UTF_8);
        } else {
            JSONObject result = new JSONObject();
            result.put("code", HttpStatus.HTTP_INTERNAL_ERROR);
            result.put("message", "文件下载失败");
            IOUtils.write(result.toString(), response.getOutputStream(), StandardCharsets.UTF_8);
        }
    }

    private List<FileInfo> treeToList(List<FileInfo> messageList) {
        List<FileInfo> result = new ArrayList<>();
        for (FileInfo entity : messageList) {
            result.add(entity);
            List<FileInfo> childMsg = entity.getSysFileList();
            if (childMsg != null && childMsg.size() > 0) {
                List<FileInfo> entityList = this.treeToList(childMsg);
                result.addAll(entityList);
            }
        }
        if (result.size() > 0) {
            for (FileInfo entity : result) {
                entity.setSysFileList(null);
            }
        }
        return result;
    }

    /**
     * 递归把传过来的tree转为list
     *
     * @param result 结果集
     */
    public void recursionTreeToList(List<FileInfo> result, FileInfo fileInfo) {
        if (CollectionUtils.isEmpty(fileInfo.getSysFileList())) {
            return;
        }
        result.add(fileInfo);
        for (FileInfo fileInfoTmp : fileInfo.getSysFileList()) {
            result.add(fileInfoTmp);
            this.recursionTreeToList(result, fileInfoTmp);
        }
    }

    /**
     * 通过文件id下载文件
     *
     * @param fileIds  文件Ids
     * @param response 响应
     * @param request  请求
     */
    @Override
    public void download(String fileIds, HttpServletResponse response, HttpServletRequest request) {
        List<FileInfo> fileInfoList = fileInfoMapper.selectList(new QueryWrapper<FileInfo>().lambda().in(FileInfo::getId, Arrays.asList(fileIds.split(","))));
        if (!CollectionUtils.isEmpty(fileInfoList)) {
            //单个下载
            if (fileInfoList.size() == 1) {
                FileInfo fileInfo = fileInfoList.get(0);
                iDistributedFileSystemService.download(fileInfo, fileInfo.getFileName(), fileInfo.getFileExtension(), response, request);
            } else {
                //批量下载
                iDistributedFileSystemService.batchDownload(fileInfoList, DateUtils.dateTimeNow(), response, request);
            }
        }
    }

    @Transactional
    @Override
    public int updateFileInfo(String fileId, String fileInfo) {
        return fileInfoMapper.updateFileInfo(fileId, fileInfo, SecurityUtils.getUserId());
    }

    /**
     * 通过文件对象和文件url生成fileInfo对象
     *
     * @param tenantId 租户Id
     * @param file     文件
     * @param url      文件地址
     * @return
     */
    @NotNull
    private FileInfo getFileInfo(Long tenantId, Long directoryId, String serviceName, MultipartFile file, String url) {
        LoginUser loginUser = SecurityUtils.getLoginUser();

        FileInfo fileInfo = new FileInfo();
        fileInfo.setTenantId(tenantId);
        fileInfo.setFileExtension(FilenameUtils.getExtension(file.getOriginalFilename()));
        fileInfo.setFileName(file.getOriginalFilename().replace("." + fileInfo.getFileExtension(), ""));
        fileInfo.setFileFullName(file.getOriginalFilename());
        fileInfo.setFileUrl(url);
        fileInfo.setFilePreviewUrl(url);
        fileInfo.setFileStorageName(url.substring(url.lastIndexOf("/") + 1));
        fileInfo.setFileParentId(directoryId);
        fileInfo.setFileSize(file.getSize());
        fileInfo.setServiceName(serviceName);
        fileInfo.setFileType(FileUtils.parseFileType(FilenameUtils.getExtension(file.getOriginalFilename()).toLowerCase()));
        fileInfo.setIsDeleted(ConstantUtils.IS_DELETED_0);
        fileInfo.setFileIsHide(ConstantUtils.IS_DELETED_0);
        fileInfo.setFileStatus(ConstantUtils.SHOW_STATUS_FIX_10);
        fileInfo.setFileIsDirectory(ConstantUtils.IS_DIRECTORY_FILE_0);
        doSetFileInfo(fileInfo, loginUser);
        return fileInfo;
    }

    /**
     * 设置文件对象默认值
     */
    private void doSetFileInfo(FileInfo fileInfo, LoginUser loginUser) {
        fileInfo.setCreateById(loginUser.getUserid());
        fileInfo.setCreateTime(new Date());
        fileInfo.setUpdateById(loginUser.getUserid());
        fileInfo.setUpdateTime(new Date());
    }
    /**
     * 递归获取某个父节点下面的所有子节点
     *
     * @param fileInfoList 数据库查询出来的所有机构集合
     * @param fileParentId 父id
     */
    private static List<FileInfo> getChildFileInfoByFileParentId(List<FileInfo> fileInfoList, Long fileParentId) {
        List<FileInfo> childFileInfoList = new ArrayList<>();
        fileInfoList.stream()
                //过滤出父id等于参数的id
                .filter(fileInfo -> Objects.nonNull(fileInfo.getFileParentId()) && fileInfo.getFileParentId().longValue() == fileParentId.longValue()).forEach(fileInfo -> {
            //递归遍历下一级
            getChildFileInfoByFileParentId(fileInfoList, fileInfo.getId());
            //添加
            childFileInfoList.add(fileInfo);
        });
        return childFileInfoList;
    }

    /**
     * 将文件信息列表改成文件信息树状
     *
     * @param list
     * @param parentId
     * @return
     */
    // 递归方法
    private List<FileInfo> convertFileListToFileTree(List<FileInfo> list, long parentId) {
        return list.stream()
                .filter(fileInfo -> Objects.nonNull(fileInfo.getFileParentId()) && parentId == fileInfo.getFileParentId())
                .peek(fileInfo -> fileInfo.setSysFileList(convertFileListToFileTree(list, fileInfo.getId())))
                .collect(Collectors.toList());
    }
}
