第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全網最全的資料

全套代碼及資料全部完整提供,點此處下載