第2-3-7章 个人网盘服务接口开发-文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss

5.8 导入其他接口代码

第2-1-2章 传统方式安装FastDFS-附FastDFS常用命令
第2-1-3章 docker-compose安装FastDFS,实现文件存储服务
第2-1-5章 docker安装MinIO实现文件存储服务-springboot整合minio-minio全网最全的资料

全套代码及资料全部完整提供,点此处下载

5.8.1 接口导入-分页查询附件

接口文档:

在这里插入图片描述

AttachmentController代码:

/**
* 分页查询附件
*
*/
@ApiOperation(value = "分页查询附件", notes = "分页查询附件")
@ApiImplicitParams({
    @ApiImplicitParam(name = "current", value = "当前页", dataType = "long", paramType = "query", defaultValue = "1"),
    @ApiImplicitParam(name = "size", value = "每页显示几条", dataType = "long", paramType = "query", defaultValue = "10"),
})
@GetMapping(value = "/page")
public R<IPage<Attachment>> page(FilePageReqDTO data) {
    Page<Attachment> page = getPage();
    attachmentService.page(page, data);
    return success(page);
}

AttachmentService接口:

/**
* 查询附件分页数据
*
* @param page
* @param data
* @return
*/
IPage<Attachment> page(Page<Attachment> page, FilePageReqDTO data);

AttachmentServiceImpl类:

/**
* 查询附件分页数据
*
* @param page
* @param data
* @return
*/
public IPage<Attachment> page(Page<Attachment> page, FilePageReqDTO data) {
    Attachment attachment = dozer.map(data, Attachment.class);

    // ${ew.customSqlSegment} 语法一定要手动eq like 等 不能用lbQ!
    LbqWrapper<Attachment> wrapper = Wraps.<Attachment>lbQ()
        .like(Attachment::getSubmittedFileName, attachment.getSubmittedFileName())
        .like(Attachment::getBizType, attachment.getBizType())
        .like(Attachment::getBizId, attachment.getBizId())
        .eq(Attachment::getDataType, attachment.getDataType())
        .orderByDesc(Attachment::getId);
    return baseMapper.page(page, wrapper);
}

5.8.2 接口导入-根据业务类型/业务id查询附件

接口文档:
在这里插入图片描述

AttachmentController代码:

@ApiOperation(value = "查询附件", notes = "查询附件")
@ApiResponses(
    @ApiResponse(code = 60103, message = "文件id为空")
)
@GetMapping
public R<List<AttachmentResultDTO>> findAttachment(@RequestParam(value = "bizTypes", required = false) String[] bizTypes,
                                                   @RequestParam(value = "bizIds", required = false) String[] bizIds) {
    //不能同时为空
    BizAssert.isTrue(!(ArrayUtils.isEmpty(bizTypes) && ArrayUtils.isEmpty(bizIds)), BASE_VALID_PARAM.build("业务类型不能为空"));
    return success(attachmentService.find(bizTypes, bizIds));
}

AttachmentService接口:

/**
* 根据业务类型和业务id查询附件
*
* @param bizTypes
* @param bizIds
* @return
*/
List<AttachmentResultDTO> find(String[] bizTypes, String[] bizIds);

AttachmentServiceImpl类:

/**
* 根据业务类型和业务id查询附件
*
* @param bizTypes
* @param bizIds
* @return
*/
public List<AttachmentResultDTO> find(String[] bizTypes, String[] bizIds) {
        return baseMapper.find(bizTypes, bizIds);
}

5.9 导入网盘服务接口

前面我们已经完成了文件服务中的附件服务相关接口的开发,附件服务最终是将上传的文件信息保存在pd_attachment表中。

本小节要完成的是文件服务中的网盘服务功能,此功能最终是将上传的文件信息保存在pd_file表中。网盘服务和附件服务非常类似,只是多了一个文件夹的概念。

5.9.1 导入FileController

package com.itheima.pinda.file.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.itheima.pinda.base.BaseController;
import com.itheima.pinda.base.R;
import com.itheima.pinda.dozer.DozerUtils;
import com.itheima.pinda.file.dto.FilePageReqDTO;
import com.itheima.pinda.file.dto.FileUpdateDTO;
import com.itheima.pinda.file.dto.FolderDTO;
import com.itheima.pinda.file.dto.FolderSaveDTO;
import com.itheima.pinda.file.entity.File;
import com.itheima.pinda.file.manager.FileRestManager;
import com.itheima.pinda.file.service.FileService;
import com.itheima.pinda.log.annotation.SysLog;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;

/**
 * 文件前端控制器
 */
@Validated
@RestController
@RequestMapping("/file")
@Slf4j
@Api(value = "文件表", tags = "文件表")
public class FileController extends BaseController {
    @Autowired
    private FileService fileService;
    @Autowired
    private FileRestManager fileRestManager;
    @Autowired
    private DozerUtils dozerUtils;

    /**
     * 查询单个文件信息
     *
     * @param id
     * @return
     */
    @ApiOperation(value = "查询文件", notes = "查询文件")
    @GetMapping
    public R<File> get(@RequestParam(value = "id") Long id) {
        File file = fileService.getById(id);
        if (file != null && file.getIsDelete()) {
            return success(null);
        }
        return success(file);
    }

    /**
     * 获取文件分页
     */
    @ApiOperation(value = "分页查询文件", notes = "获取文件分页")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "current", value = "当前页", dataType = "long", paramType = "query", defaultValue = "1"),
            @ApiImplicitParam(name = "size", value = "每页显示几条", dataType = "long", paramType = "query", defaultValue = "10"),
    })
    @GetMapping(value = "/page")
    public R<IPage<File>> page(FilePageReqDTO data) {
        return success(fileRestManager.page(getPage(), data));
    }

    /**
     * 上传文件
     */
    @ApiOperation(value = "上传文件", notes = "上传文件 ")
    @ApiResponses({
            @ApiResponse(code = 60102, message = "文件夹为空"),
    })
    @ApiImplicitParams({
            @ApiImplicitParam(name = "source", value = "文件来源", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "long", paramType = "query"),
            @ApiImplicitParam(name = "folderId", value = "文件夹id", dataType = "long", paramType = "query"),
            @ApiImplicitParam(name = "file", value = "附件", dataType = "MultipartFile", allowMultiple = true, required = true),
    })
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    public R<File> upload(
            @NotNull(message = "文件夹不能为空")
            @RequestParam(name = "source", defaultValue = "inner") String source,
            @RequestParam(name = "userId", required = false) Long userId,
            @RequestParam(value = "folderId") Long folderId,
            @RequestParam(value = "file") MultipartFile simpleFile) {
        //1,先将文件存在本地,并且生成文件名
        log.info("contentType={}, name={} , sfname={}", simpleFile.getContentType(), simpleFile.getName(), simpleFile.getOriginalFilename());
        // 忽略路径字段,只处理文件类型
        if (simpleFile.getContentType() == null) {
            return fail("文件为空");
        }

        File file = fileService.upload(simpleFile, folderId, source, userId);

        return success(file);
    }


    /**
     * 保存文件夹
     */
    @ApiResponses({
            @ApiResponse(code = 60000, message = "文件夹为空"),
            @ApiResponse(code = 60001, message = "文件夹名称为空"),
            @ApiResponse(code = 60002, message = "父文件夹为空"),
    })
    @ApiOperation(value = "新增文件夹", notes = "新增文件夹")
    @RequestMapping(value = "", method = RequestMethod.POST)
    public R<FolderDTO> saveFolder(@Valid @RequestBody FolderSaveDTO folderSaveDto) {
        //2,获取身份

        FolderDTO folder = fileService.saveFolder(folderSaveDto);
        return success(folder);
    }

    /**
     * 修改文件、文件夹信息
     *
     * @param fileUpdateDTO
     * @return
     */
    @ApiOperation(value = "修改文件/文件夹名称", notes = "修改文件/文件夹名称")
    @ApiResponses({
            @ApiResponse(code = 60100, message = "文件为空"),
    })
    @RequestMapping(value = "", method = RequestMethod.PUT)
    public R<Boolean> update(@Valid @RequestBody FileUpdateDTO fileUpdateDTO) {
        // 判断文件名是否有 后缀
        if (StringUtils.isNotEmpty(fileUpdateDTO.getSubmittedFileName())) {
            File oldFile = fileService.getById(fileUpdateDTO.getId());
            if (oldFile.getExt() != null && !fileUpdateDTO.getSubmittedFileName().endsWith(oldFile.getExt())) {
                fileUpdateDTO.setSubmittedFileName(fileUpdateDTO.getSubmittedFileName() + "." + oldFile.getExt());
            }
        }
        File file = dozerUtils.map2(fileUpdateDTO, File.class);

        fileService.updateById(file);
        return success(true);
    }

    /**
     * 根据Ids进行文件删除
     *
     * @param ids
     * @return
     */
    @ApiOperation(value = "根据Ids进行文件删除", notes = "根据Ids进行文件删除  ")
    @DeleteMapping(value = "/ids")
    public R<Boolean> removeList(@RequestParam(value = "ids[]") Long[] ids) {
        Long userId = getUserId();
        return success(fileService.removeList(userId, ids));
    }

    /**
     * 下载一个文件或多个文件打包下载
     *
     * @param ids
     * @param response
     * @throws Exception
     */
    @ApiOperation(value = "下载一个文件或多个文件打包下载", notes = "下载一个文件或多个文件打包下载")
    @GetMapping(value = "/download", produces = "application/octet-stream")
    public void download(
            @ApiParam(name = "ids[]", value = "文件id 数组")
            @RequestParam(value = "ids[]") Long[] ids,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        fileRestManager.download(request, response, ids, null);
    }

}

5.9.2 导入StatisticsController

package com.itheima.pinda.file.controller;

import com.itheima.pinda.base.BaseController;
import com.itheima.pinda.base.R;
import com.itheima.pinda.file.domain.FileStatisticsDO;
import com.itheima.pinda.file.dto.FileOverviewDTO;
import com.itheima.pinda.file.dto.FileStatisticsAllDTO;
import com.itheima.pinda.file.service.FileService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.List;

/**
 * 文件统计接口
 */
@Slf4j
@RestController
@RequestMapping("/statistics")
@Api(value = "Statistics", tags = "统计接口")
public class StatisticsController extends BaseController {
    @Autowired
    private FileService fileService;

    @ApiOperation(value = "云盘首页数据概览", notes = "云盘首页数据概览")
    @GetMapping(value = "/overview")
    public R<FileOverviewDTO> overview(@RequestParam(name = "userId", required = false) Long userId) {
        return success(fileService.findOverview(userId, null, null));
    }

    @ApiOperation(value = "按照类型,统计各种类型的 大小和数量", notes = "按照类型,统计当前登录人各种类型的大小和数量")
    @GetMapping(value = "/type")
    public R<List<FileStatisticsDO>> findAllByDataType(@RequestParam(name = "userId", required = false) Long userId) {
        return success(fileService.findAllByDataType(userId));
    }

    @ApiOperation(value = "按照时间统计各种类型的文件的数量和大小", notes = "按照时间统计各种类型的文件的数量和大小 不指定时间,默认查询一个月")
    @GetMapping(value = "")
    public R<FileStatisticsAllDTO> findNumAndSizeToTypeByDate(@RequestParam(name = "userId", required = false) Long userId,
                                                              @RequestParam(value = "startTime", required = false) LocalDateTime startTime,
                                                              @RequestParam(value = "endTime", required = false) LocalDateTime endTime) {
        return success(fileService.findNumAndSizeToTypeByDate(userId, startTime, endTime));
    }
}

5.9.3 导入FileRestManager

package com.itheima.pinda.file.manager;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.itheima.pinda.context.BaseContextHandler;
import com.itheima.pinda.file.constant.FileConstants;
import com.itheima.pinda.file.dto.FilePageReqDTO;
import com.itheima.pinda.file.entity.File;
import com.itheima.pinda.file.service.FileService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static com.itheima.pinda.utils.StrPool.DEF_PARENT_ID;

/**
 * 文件 公共代码 管理类
 */
@Component
public class FileRestManager {
    @Autowired
    private FileService fileService;

    public IPage<File> page(IPage<File> page, FilePageReqDTO filePageReq) {
        //类型和文件夹id同时为null时, 表示查询 全部文件
        if (filePageReq.getFolderId() == null && filePageReq.getDataType() == null) {
            filePageReq.setFolderId(DEF_PARENT_ID);
        }

        QueryWrapper<File> query = new QueryWrapper<>();
        LambdaQueryWrapper<File> lambdaQuery = query.lambda()
                .eq(File::getIsDelete, false)
                .eq(filePageReq.getDataType() != null, File::getDataType, filePageReq.getDataType())
                .eq(filePageReq.getFolderId() != null, File::getFolderId, filePageReq.getFolderId())
                .like(StringUtils.isNotEmpty(filePageReq.getSubmittedFileName()), File::getSubmittedFileName, filePageReq.getSubmittedFileName());

        query.orderByDesc(String.format("case when %s='DIR' THEN 1 else 0 end", FileConstants.DATA_TYPE));
        lambdaQuery.orderByDesc(File::getCreateTime);

        fileService.page(page, lambdaQuery);
        return page;
    }

    public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids, Long userId) throws Exception {
        userId = userId == null || userId <= 0 ? BaseContextHandler.getUserId() : userId;
        fileService.download(request, response, ids, userId);
    }
}

5.9.4 导入FileService

package com.itheima.pinda.file.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.pinda.file.domain.FileAttrDO;
import com.itheima.pinda.file.domain.FileStatisticsDO;
import com.itheima.pinda.file.dto.FileOverviewDTO;
import com.itheima.pinda.file.dto.FileStatisticsAllDTO;
import com.itheima.pinda.file.dto.FolderDTO;
import com.itheima.pinda.file.dto.FolderSaveDTO;
import com.itheima.pinda.file.entity.File;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.List;
/**
 * 业务接口
 * 文件表
 *
 */
public interface FileService extends IService<File> {
    /**
     * 保存文件夹
     *
     * @param folderSaveDto 文件夹
     * @return
     */
    FolderDTO saveFolder(FolderSaveDTO folderSaveDto);

    /**
     * 根据文件id下载文件,并统计下载次数
     *
     * @param request  请求
     * @param response 响应
     * @param ids      文件id集合
     * @param userId   用户id
     * @throws Exception
     */
    void download(HttpServletRequest request, HttpServletResponse response,
                  Long[] ids, Long userId) throws Exception;

    /**
     * 根据文件id和用户id 删除文件或者文件夹
     *
     * @param userId 用户id
     * @param ids    文件id集合
     * @return
     */
    Boolean removeList(Long userId, Long[] ids);

    /**
     * 根据文件夹id查询
     *
     * @param folderId
     * @return
     */
    FileAttrDO getFileAttrDo(Long folderId);

    /**
     * 文件上传
     *
     * @param simpleFile 文件
     * @param folderId   文件夹id
     * @return
     */
    File upload(MultipartFile simpleFile, Long folderId);

	/**
	 * 文件上传
	 *
	 * @param simpleFile
	 * @param folderId
	 * @param source
	 * @param userId
	 * @return
	 */
	File upload(MultipartFile simpleFile, Long folderId, String source, Long userId);

    /**
     * 首页概览
     *
     * @param userId
     * @param startTime
     * @param endTime
     * @return
     */
    FileOverviewDTO findOverview(Long userId, LocalDateTime startTime, LocalDateTime endTime);

    /**
     * 首页个人文件发展概览
     *
     * @param userId
     * @param startTime
     * @param endTime
     * @return
     */
    FileStatisticsAllDTO findAllByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime);


    /**
     * 按照 数据类型分类查询 当前人的所有文件的数量和大小
     *
     * @param userId
     * @return
     */
    List<FileStatisticsDO> findAllByDataType(Long userId);

    /**
     * 查询下载排行前20的文件
     *
     * @param userId
     * @return
     */
    List<FileStatisticsDO> downTop20(Long userId);

    /**
     * 根据日期查询,特定类型的数量和大小
     *
     * @param userId
     * @param startTime
     * @param endTime
     * @return
     */
    FileStatisticsAllDTO findNumAndSizeToTypeByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime);

    /**
     * 根据日期查询下载大小
     *
     * @param userId
     * @param startTime
     * @param endTime
     * @return
     */
    FileStatisticsAllDTO findDownSizeByDate(Long userId, LocalDateTime startTime,
                                            LocalDateTime endTime);
}

5.9.5 导入FileServiceImpl

package com.itheima.pinda.file.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.pinda.database.mybatis.conditions.Wraps;
import com.itheima.pinda.database.mybatis.conditions.update.LbuWrapper;
import com.itheima.pinda.dozer.DozerUtils;
import com.itheima.pinda.file.biz.FileBiz;
import com.itheima.pinda.file.dao.FileMapper;
import com.itheima.pinda.file.domain.FileAttrDO;
import com.itheima.pinda.file.domain.FileDO;
import com.itheima.pinda.file.domain.FileDeleteDO;
import com.itheima.pinda.file.domain.FileStatisticsDO;
import com.itheima.pinda.file.dto.FileOverviewDTO;
import com.itheima.pinda.file.dto.FileStatisticsAllDTO;
import com.itheima.pinda.file.dto.FolderDTO;
import com.itheima.pinda.file.dto.FolderSaveDTO;
import com.itheima.pinda.file.entity.File;
import com.itheima.pinda.file.enumeration.DataType;
import com.itheima.pinda.file.enumeration.IconType;
import com.itheima.pinda.file.service.FileService;
import com.itheima.pinda.file.strategy.FileStrategy;
import com.itheima.pinda.utils.BizAssert;
import com.itheima.pinda.utils.DateUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.itheima.pinda.exception.code.ExceptionCode.BASE_VALID_PARAM;
import static com.itheima.pinda.utils.StrPool.DEF_PARENT_ID;
import static com.itheima.pinda.utils.StrPool.DEF_ROOT_PATH;
import static java.util.stream.Collectors.groupingBy;

/**
 * 业务实现类
 * 文件表
 */
@Slf4j
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {
    @Autowired
    private DozerUtils dozerUtils;

    @Autowired
    private FileBiz fileBiz;
    @Resource
    private FileStrategy fileStrategy;

    @Override
    public File upload(MultipartFile simpleFile, Long folderId) {
        FileAttrDO fileAttrDO = this.getFileAttrDo(folderId);
        String treePath = fileAttrDO.getTreePath();
        String folderName = fileAttrDO.getFolderName();
        Integer grade = fileAttrDO.getGrade();

        File file = fileStrategy.upload(simpleFile);
        file.setFolderId(folderId);
        file.setFolderName(folderName);
        file.setGrade(grade);
        file.setTreePath(treePath);
        super.save(file);
        return file;
    }

	@Override
	public File upload(MultipartFile simpleFile, Long folderId, String source, Long userId) {
		FileAttrDO fileAttrDO = this.getFileAttrDo(folderId);
		String treePath = fileAttrDO.getTreePath();
		String folderName = fileAttrDO.getFolderName();
		Integer grade = fileAttrDO.getGrade();

		File file = fileStrategy.upload(simpleFile);
		file.setFolderId(folderId);
		file.setFolderName(folderName);
		file.setGrade(grade);
		file.setTreePath(treePath);

		file.setSource(source);
		if (userId != null) {
			file.setCreateUser(userId);
		}

		super.save(file);
		return file;
	}

	@Override
    public FileAttrDO getFileAttrDo(Long folderId) {
        String treePath = DEF_ROOT_PATH;
        String folderName = "";
        Integer grade = 1;
        if (folderId == null || folderId <= 0) {
            return new FileAttrDO(treePath, grade, folderName, DEF_PARENT_ID);
        }
        File folder = this.getById(folderId);

        if (folder != null && !folder.getIsDelete() && DataType.DIR.eq(folder.getDataType())) {
            folderName = folder.getSubmittedFileName();
            treePath = StringUtils.join(folder.getTreePath(), folder.getId(), DEF_ROOT_PATH);
            grade = folder.getGrade() + 1;
        }
        BizAssert.isTrue(grade <= 10, BASE_VALID_PARAM.build("文件夹层级不能超过10层"));
        return new FileAttrDO(treePath, grade, folderName, folderId);
    }


    @Override
    public FolderDTO saveFolder(FolderSaveDTO folderSaveDto) {
        File folder = dozerUtils.map2(folderSaveDto, File.class);
        if (folderSaveDto.getFolderId() == null || folderSaveDto.getFolderId() <= 0) {
            folder.setFolderId(DEF_PARENT_ID);
            folder.setTreePath(DEF_ROOT_PATH);
            folder.setGrade(1);
        } else {
            File parent = super.getById(folderSaveDto.getFolderId());
            BizAssert.notNull(parent, BASE_VALID_PARAM.build("父文件夹不能为空"));
            BizAssert.isFalse(parent.getIsDelete(), BASE_VALID_PARAM.build("父文件夹已经被删除"));
            BizAssert.equals(DataType.DIR.name(), parent.getDataType().name(), BASE_VALID_PARAM.build("父文件夹不存在"));
            BizAssert.isTrue(parent.getGrade() < 10, BASE_VALID_PARAM.build("文件夹层级不能超过10层"));
            folder.setFolderName(parent.getSubmittedFileName());
            folder.setTreePath(StringUtils.join(parent.getTreePath(), parent.getId(), DEF_ROOT_PATH));
            folder.setGrade(parent.getGrade() + 1);
        }
        if (folderSaveDto.getOrderNum() == null) {
            folderSaveDto.setOrderNum(0);
        }
        folder.setIsDelete(false);
        folder.setDataType(DataType.DIR);
        folder.setIcon(IconType.DIR.getIcon());
        setDate(folder);
        super.save(folder);
        return dozerUtils.map2(folder, FolderDTO.class);
    }

    private void setDate(File file) {
        LocalDateTime now = LocalDateTime.now();
        file.setCreateMonth(DateUtils.formatAsYearMonthEn(now))
                .setCreateWeek(DateUtils.formatAsYearWeekEn(now))
                .setCreateDay(DateUtils.formatAsDateEn(now));
    }

    public boolean removeFile(Long[] ids, Long userId) {
        LbuWrapper<File> lambdaUpdate =
                Wraps.<File>lbU()
                        .in(File::getId, ids)
                        .eq(File::getCreateUser, userId);
        File file = File.builder().isDelete(Boolean.TRUE).build();

        return super.update(file, lambdaUpdate);
    }

    @Override
    public Boolean removeList(Long userId, Long[] ids) {
        if (ArrayUtils.isEmpty(ids)) {
            return Boolean.TRUE;
        }
        List<File> list = super.list(Wrappers.<File>lambdaQuery().in(File::getId, ids));
        if (list.isEmpty()) {
            return true;
        }
        super.removeByIds(Arrays.asList(ids));

        fileStrategy.delete(list.stream().map((fi) -> FileDeleteDO.builder()
                .relativePath(fi.getRelativePath())
                .fileName(fi.getFilename())
                .group(fi.getGroup())
                .path(fi.getPath())
                .file(false)
                .build())
                .collect(Collectors.toList()));
        return true;
    }

    @Override
    public void download(HttpServletRequest request, HttpServletResponse response, Long[] ids, Long userId) throws Exception {
        if (ids == null || ids.length == 0) {
            return;
        }
        List<File> list = (List<File>) super.listByIds(Arrays.asList(ids));

        if (list == null || list.size() == 0) {
            return;
        }
        List<FileDO> listDo = list.stream().map((file) ->
                FileDO.builder()
                        .dataType(file.getDataType())
                        .size(file.getSize())
                        .submittedFileName(file.getSubmittedFileName())
                        .url(file.getUrl())
                        .build())
                .collect(Collectors.toList());
        fileBiz.down(listDo, request, response);
    }


    @Override
    public FileOverviewDTO findOverview(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
        InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke();
        startTime = innerQueryDate.getStartTime();
        endTime = innerQueryDate.getEndTime();

        List<FileStatisticsDO> list = baseMapper.findNumAndSizeByUserId(userId, null, "ALL", startTime, endTime);
        FileOverviewDTO.FileOverviewDTOBuilder builder = FileOverviewDTO.myBuilder();

        long allSize = 0L;
        int allNum = 0;
        for (FileStatisticsDO fs : list) {
            allSize += fs.getSize();
            allNum += fs.getNum();
            switch (fs.getDataType()) {
                case DIR:
                    builder.dirNum(fs.getNum());
                    break;
                case IMAGE:
                    builder.imgNum(fs.getNum());
                    break;
                case VIDEO:
                    builder.videoNum(fs.getNum());
                    break;
                case DOC:
                    builder.docNum(fs.getNum());
                    break;
                case AUDIO:
                    builder.audioNum(fs.getNum());
                    break;
                case OTHER:
                    builder.otherNum(fs.getNum());
                    break;
                default:
                    break;
            }
        }
        builder.allFileNum(allNum).allFileSize(allSize);
        return builder.build();
    }

    @Override
    public FileStatisticsAllDTO findAllByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
        InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke();
        startTime = innerQueryDate.getStartTime();
        endTime = innerQueryDate.getEndTime();
        List<String> dateList = innerQueryDate.getDateList();
        String dateType = innerQueryDate.getDateType();

        //不完整的数据
        List<FileStatisticsDO> list = baseMapper.findNumAndSizeByUserId(userId, dateType, null, startTime, endTime);

        //按月份分类
        Map<String, List<FileStatisticsDO>> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType));

        List<Long> sizeList = new ArrayList<>();
        List<Integer> numList = new ArrayList<>();

        dateList.forEach((date) -> {
            if (map.containsKey(date)) {
                List<FileStatisticsDO> subList = map.get(date);

                Long size = subList.stream().mapToLong(FileStatisticsDO::getSize).sum();
                Integer num = subList.stream().filter((fs) -> !DataType.DIR.eq(fs.getDataType()))
                        .mapToInt(FileStatisticsDO::getNum).sum();
                sizeList.add(size);
                numList.add(num);
            } else {
                sizeList.add(0L);
                numList.add(0);
            }
        });

        return FileStatisticsAllDTO.builder().dateList(dateList).numList(numList).sizeList(sizeList).build();
    }


    @Override
    public List<FileStatisticsDO> findAllByDataType(Long userId) {
        List<DataType> dataTypes = Arrays.asList(DataType.values());
        List<FileStatisticsDO> list = baseMapper.findNumAndSizeByUserId(userId, null, "ALL", null, null);

        Map<DataType, List<FileStatisticsDO>> map = list.stream().collect(groupingBy(FileStatisticsDO::getDataType));

        return dataTypes.stream().map((type) -> {
            FileStatisticsDO fs = null;
            if (map.containsKey(type)) {
                fs = map.get(type).get(0);
            } else {
                fs = FileStatisticsDO.builder().dataType(type).size(0L).num(0).build();
            }
            return fs;
        }).collect(Collectors.toList());
    }

    @Override
    public List<FileStatisticsDO> downTop20(Long userId) {
        return baseMapper.findDownTop20(userId);
    }

    @Override
    public FileStatisticsAllDTO findNumAndSizeToTypeByDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
        return common(userId, startTime, endTime,
                (qd) -> baseMapper.findNumAndSizeByUserId(qd.getUserId(), qd.getDateType(), "ALL", qd.getStartTime(), qd.getEndTime()));
    }

    @Override
    public FileStatisticsAllDTO findDownSizeByDate(Long userId, LocalDateTime startTime,
                                                   LocalDateTime endTime) {
        return common(userId, startTime, endTime,
                (qd) -> baseMapper.findDownSizeByDate(qd.getUserId(), qd.getDateType(), qd.getStartTime(), qd.getEndTime()));
    }

    /**
     * 抽取公共查询公共代码
     *
     * @param userId    用户id
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param function  回调函数
     * @return
     */
    private FileStatisticsAllDTO common(Long userId, LocalDateTime startTime, LocalDateTime endTime, Function<InnerQueryDate, List<FileStatisticsDO>> function) {
        InnerQueryDate innerQueryDate = new InnerQueryDate(userId, startTime, endTime).invoke();
        List<String> dateList = innerQueryDate.getDateList();

        List<FileStatisticsDO> list = function.apply(innerQueryDate);

        //按月份分类
        Map<String, List<FileStatisticsDO>> map = list.stream().collect(groupingBy(FileStatisticsDO::getDateType));

        List<Long> sizeList = new ArrayList<>(dateList.size());
        List<Integer> numList = new ArrayList<>(dateList.size());

        List<Integer> dirNumList = new ArrayList<>(dateList.size());

        List<Long> imgSizeList = new ArrayList<>(dateList.size());
        List<Integer> imgNumList = new ArrayList<>(dateList.size());

        List<Long> videoSizeList = new ArrayList<>(dateList.size());
        List<Integer> videoNumList = new ArrayList<>(dateList.size());

        List<Long> audioSizeList = new ArrayList<>(dateList.size());
        List<Integer> audioNumList = new ArrayList<>(dateList.size());

        List<Long> docSizeList = new ArrayList<>(dateList.size());
        List<Integer> docNumList = new ArrayList<>(dateList.size());

        List<Long> otherSizeList = new ArrayList<>(dateList.size());
        List<Integer> otherNumList = new ArrayList<>(dateList.size());

        dateList.forEach((date) -> {
            if (map.containsKey(date)) {
                List<FileStatisticsDO> subList = map.get(date);

                Function<DataType, Stream<FileStatisticsDO>> stream = (dataType) -> subList.stream().filter((fs) -> !dataType.eq(fs.getDataType()));
                Long size = stream.apply(DataType.DIR).mapToLong(FileStatisticsDO::getSize).sum();
                Integer num = stream.apply(DataType.DIR).mapToInt(FileStatisticsDO::getNum).sum();
                sizeList.add(size);
                numList.add(num);

                Integer dirNum = subList.stream().filter((fs) -> DataType.DIR.eq(fs.getDataType()))
                        .mapToInt(FileStatisticsDO::getNum).sum();
                dirNumList.add(dirNum);

                add(imgSizeList, imgNumList, subList, DataType.IMAGE);
                add(videoSizeList, videoNumList, subList, DataType.VIDEO);
                add(audioSizeList, audioNumList, subList, DataType.AUDIO);
                add(docSizeList, docNumList, subList, DataType.DOC);
                add(otherSizeList, otherNumList, subList, DataType.OTHER);

            } else {
                sizeList.add(0L);
                numList.add(0);
                dirNumList.add(0);
                imgSizeList.add(0L);
                imgNumList.add(0);
                videoSizeList.add(0L);
                videoNumList.add(0);
                audioSizeList.add(0L);
                audioNumList.add(0);
                docSizeList.add(0L);
                docNumList.add(0);
                otherSizeList.add(0L);
                otherNumList.add(0);
            }
        });

        return FileStatisticsAllDTO.builder()
                .dateList(dateList)
                .numList(numList).sizeList(sizeList)
                .dirNumList(dirNumList)
                .imgNumList(imgNumList).imgSizeList(imgSizeList)
                .videoNumList(videoNumList).videoSizeList(videoSizeList)
                .audioNumList(audioNumList).audioSizeList(audioSizeList)
                .docNumList(docNumList).docSizeList(docSizeList)
                .otherNumList(otherNumList).otherSizeList(otherSizeList)
                .build();
    }

    private void add(List<Long> sizeList, List<Integer> numList, List<FileStatisticsDO> subList, DataType dt) {
        Function<DataType, Stream<FileStatisticsDO>> stream =
                dataType -> subList.stream().filter(fs -> dataType.eq(fs.getDataType()));

        Long size = stream.apply(dt).mapToLong(FileStatisticsDO::getSize).sum();
        Integer num = stream.apply(dt).mapToInt(FileStatisticsDO::getNum).sum();
        sizeList.add(size);
        numList.add(num);
    }

    @Getter
    private static class InnerQueryDate {
        private LocalDateTime startTime;
        private LocalDateTime endTime;
        private List<String> dateList;
        private String dateType;
        private Long userId;

        public InnerQueryDate(Long userId, LocalDateTime startTime, LocalDateTime endTime) {
            this.userId = userId;
            this.startTime = startTime;
            this.endTime = endTime;
        }

        public InnerQueryDate invoke() {
            if (startTime == null) {
                startTime = LocalDateTime.now().plusDays(-9);
            }
            if (endTime == null) {
                endTime = LocalDateTime.now();
            }
            endTime = LocalDateTime.of(endTime.toLocalDate(), LocalTime.MAX);
            dateList = new ArrayList<>();
            dateType = DateUtils.calculationEn(startTime, endTime, dateList);
            return this;
        }
    }
}

5.9.6 扩展FileMapper接口方法

/**
* 查询下次次数前20的文件
*
* @param userId
* @return
*/
List<FileStatisticsDO> findDownTop20(@Param("userId") Long userId);

/**
* 统计时间区间内文件的下次次数和大小
*
* @param userId
* @param dateType  日期类型 {MONTH:按月;WEEK:按周;DAY:按日} 来统计
* @param startTime
* @param endTime
* @return
*/
List<FileStatisticsDO> findDownSizeByDate(@Param("userId") Long userId,
                                          @Param("dateType") String dateType,
                                          @Param("startTime") LocalDateTime startTime,
                                          @Param("endTime") LocalDateTime endTime);

第2-1-2章 传统方式安装FastDFS-附FastDFS常用命令
第2-1-3章 docker-compose安装FastDFS,实现文件存储服务
第2-1-5章 docker安装MinIO实现文件存储服务-springboot整合minio-minio全网最全的资料

全套代码及资料全部完整提供,点此处下载