package com.github.zuihou.file.service.impl; import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.github.zuihou.base.service.SuperServiceImpl; import com.github.zuihou.database.mybatis.conditions.Wraps; import com.github.zuihou.database.mybatis.conditions.update.LbuWrapper; import com.github.zuihou.file.biz.FileBiz; import com.github.zuihou.file.dao.FileMapper; import com.github.zuihou.file.domain.FileAttrDO; import com.github.zuihou.file.domain.FileDO; import com.github.zuihou.file.domain.FileDeleteDO; import com.github.zuihou.file.domain.FileStatisticsDO; import com.github.zuihou.file.dto.FileOverviewDTO; import com.github.zuihou.file.dto.FileStatisticsAllDTO; import com.github.zuihou.file.dto.FolderDTO; import com.github.zuihou.file.dto.FolderSaveDTO; import com.github.zuihou.file.entity.File; import com.github.zuihou.file.enumeration.DataType; import com.github.zuihou.file.enumeration.IconType; import com.github.zuihou.file.service.FileService; import com.github.zuihou.file.strategy.FileStrategy; import com.github.zuihou.utils.BeanPlusUtil; import com.github.zuihou.utils.BizAssert; import com.github.zuihou.utils.DateUtils; import lombok.Getter; import lombok.extern.slf4j.Slf4j; 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.github.zuihou.exception.code.ExceptionCode.BASE_VALID_PARAM; import static com.github.zuihou.utils.StrPool.DEF_PARENT_ID; import static com.github.zuihou.utils.StrPool.DEF_ROOT_PATH; import static java.util.stream.Collectors.groupingBy; /** * <p> * 业务实现类 * 文件表 * </p> * * @author zuihou * @date 2019-06-24 */ @Slf4j @Service public class FileServiceImpl extends SuperServiceImpl<FileMapper, File> implements FileService { @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 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 = BeanPlusUtil.toBean(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 BeanPlusUtil.toBean(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, List<Long> ids) { if (CollectionUtil.isEmpty(ids)) { return Boolean.TRUE; } List<File> list = super.list(Wrappers.<File>lambdaQuery().in(File::getId, ids)); if (list.isEmpty()) { return true; } super.removeByIds(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; } } }