|
|
@ -1,5 +1,6 @@ |
|
|
|
package org.ycloud.aipan.service.impl; |
|
|
|
|
|
|
|
import cn.hutool.core.util.IdUtil; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
@ -9,10 +10,7 @@ import org.springframework.transaction.annotation.Transactional; |
|
|
|
import org.springframework.util.CollectionUtils; |
|
|
|
import org.ycloud.aipan.component.StoreEngine; |
|
|
|
import org.ycloud.aipan.config.MinioConfig; |
|
|
|
import org.ycloud.aipan.controller.req.FileBatchReq; |
|
|
|
import org.ycloud.aipan.controller.req.FileUpdateReq; |
|
|
|
import org.ycloud.aipan.controller.req.FileUploadReq; |
|
|
|
import org.ycloud.aipan.controller.req.FolderCreateReq; |
|
|
|
import org.ycloud.aipan.controller.req.*; |
|
|
|
import org.ycloud.aipan.dto.AccountFileDTO; |
|
|
|
import org.ycloud.aipan.dto.FolderTreeNodeDTO; |
|
|
|
import org.ycloud.aipan.enums.BizCodeEnum; |
|
|
@ -21,8 +19,10 @@ import org.ycloud.aipan.enums.FolderFlagEnum; |
|
|
|
import org.ycloud.aipan.exception.BizException; |
|
|
|
import org.ycloud.aipan.mapper.AccountFileMapper; |
|
|
|
import org.ycloud.aipan.mapper.FileMapper; |
|
|
|
import org.ycloud.aipan.mapper.StorageMapper; |
|
|
|
import org.ycloud.aipan.model.AccountFileDO; |
|
|
|
import org.ycloud.aipan.model.FileDO; |
|
|
|
import org.ycloud.aipan.model.StorageDO; |
|
|
|
import org.ycloud.aipan.service.AccountFileService; |
|
|
|
import org.ycloud.aipan.util.CommonUtil; |
|
|
|
import org.ycloud.aipan.util.SpringBeanUtil; |
|
|
@ -45,6 +45,8 @@ public class AccountFileServiceImpl implements AccountFileService { |
|
|
|
private AccountFileMapper accountFileMapper; |
|
|
|
@Autowired |
|
|
|
private FileMapper fileMapper; |
|
|
|
@Autowired |
|
|
|
private StorageMapper storageMapper; |
|
|
|
|
|
|
|
@Override |
|
|
|
public List<AccountFileDTO> listFile(Long accountId, Long parentId) { |
|
|
@ -261,6 +263,80 @@ public class AccountFileServiceImpl implements AccountFileService { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void delBatch(FileDelReq req) { |
|
|
|
//步骤一:检查是否满足:1、文件ID数量是否合法,2、文件是否属于当前用户
|
|
|
|
List<AccountFileDO> accountFileDOList = checkFileIdLegal(req.getFileIds(), req.getAccountId()); |
|
|
|
|
|
|
|
//步骤二:判断文件是否是文件夹,文件夹的话需要递归获取里面子文件ID,然后进行批量删除
|
|
|
|
List<AccountFileDO> storeAccountFileDOList = new ArrayList<>(); |
|
|
|
findAllAccountFileDOWithRecur(storeAccountFileDOList, accountFileDOList, false); |
|
|
|
|
|
|
|
//拿到全部文件ID列表
|
|
|
|
List<Long> allFileIdList = storeAccountFileDOList.stream().map(AccountFileDO::getId).collect(Collectors.toList()); |
|
|
|
|
|
|
|
//步骤三:需要更新账号存储空间使用情况 可以加个分布式锁,redission 作业,提示可以用account_id锁粒度
|
|
|
|
long allFileSize = storeAccountFileDOList.stream() |
|
|
|
.filter(file -> file.getIsDir().equals(FolderFlagEnum.NO.getCode())) |
|
|
|
.mapToLong(AccountFileDO::getFileSize).sum(); |
|
|
|
StorageDO storageDO = storageMapper.selectOne(new QueryWrapper<StorageDO>().eq("account_id", req.getAccountId())); |
|
|
|
storageDO.setUsedSize(storageDO.getUsedSize() - allFileSize); |
|
|
|
storageMapper.updateById(storageDO); |
|
|
|
|
|
|
|
// 步骤四:批量删除账号映射文件,考虑回收站如何设计
|
|
|
|
accountFileMapper.deleteBatchIds(allFileIdList); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void copyBatch(FileBatchReq req) { |
|
|
|
//检查被转移的文件ID是否合法
|
|
|
|
List<AccountFileDO> accountFileDOList = checkFileIdLegal(req.getFileIds(), req.getAccountId()); |
|
|
|
//检查目标文件夹ID是否合法
|
|
|
|
checkTargetParentIdLegal(req); |
|
|
|
//执行拷贝,递归查找【差异点,ID是全新的】
|
|
|
|
List<AccountFileDO> newAccountFileDOList = findBatchCopyFileWithRecur(accountFileDOList, req.getTargetParentId()); |
|
|
|
//计算存储空间大小,检查是否足够【差异点,空间需要检查】
|
|
|
|
long totalFileSize = newAccountFileDOList.stream().filter(file -> file.getIsDir().equals(FolderFlagEnum.NO.getCode())) |
|
|
|
.mapToLong(AccountFileDO::getFileSize).sum(); |
|
|
|
if (!checkAndUpdateCapacity(req.getAccountId(), totalFileSize)) { |
|
|
|
throw new BizException(BizCodeEnum.FILE_STORAGE_NOT_ENOUGH); |
|
|
|
} |
|
|
|
//存储
|
|
|
|
accountFileMapper.insertFileBatch(newAccountFileDOList); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public Boolean secondUpload(FileSecondUploadReq req) { |
|
|
|
//检查文件是否存在
|
|
|
|
FileDO fileDO = fileMapper.selectOne(new QueryWrapper<FileDO>().eq("identifier", req.getIdentifier())); |
|
|
|
//检查空间是否足够
|
|
|
|
if (fileDO != null && checkAndUpdateCapacity(req.getAccountId(), fileDO.getFileSize())) { |
|
|
|
//处理文件秒传
|
|
|
|
AccountFileDTO accountFileDTO = new AccountFileDTO(); |
|
|
|
accountFileDTO.setAccountId(req.getAccountId()); |
|
|
|
accountFileDTO.setFileId(fileDO.getId()); |
|
|
|
accountFileDTO.setParentId(req.getParentId()); |
|
|
|
accountFileDTO.setFileName(req.getFilename()); |
|
|
|
accountFileDTO.setFileSize(fileDO.getFileSize()); |
|
|
|
accountFileDTO.setDel(false); |
|
|
|
accountFileDTO.setIsDir(FolderFlagEnum.NO.getCode()); |
|
|
|
|
|
|
|
//保存关联文件关系,里面有做相关检查
|
|
|
|
saveAccountFile(accountFileDTO); |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 检查目标文件夹ID是否合法,包括子文件夹 |
|
|
|
* 1、目标的文件ID不能是文件 |
|
|
@ -414,8 +490,108 @@ public class AccountFileServiceImpl implements AccountFileService { |
|
|
|
accountFileDO.setFileName(split[0] + "_" + System.currentTimeMillis() + "." + split[1]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 递归查找 |
|
|
|
* |
|
|
|
* @param allAccountFileDOList 容器存储查询到到全部文件或者文件夹 |
|
|
|
* @param prepareAccountFileDOList 待查询的文件和文件夹 |
|
|
|
* @param onlyFolder 控制是否只存储文件 |
|
|
|
*/ |
|
|
|
public void findAllAccountFileDOWithRecur(List<AccountFileDO> allAccountFileDOList, List<AccountFileDO> prepareAccountFileDOList, boolean onlyFolder) { |
|
|
|
|
|
|
|
for (AccountFileDO accountFileDO : prepareAccountFileDOList) { |
|
|
|
if (Objects.equals(accountFileDO.getIsDir(), FolderFlagEnum.YES.getCode())) { |
|
|
|
//递归查找
|
|
|
|
List<AccountFileDO> childAccountFileDOList = accountFileMapper.selectList(new QueryWrapper<AccountFileDO>() |
|
|
|
.eq("parent_id", accountFileDO.getId())); |
|
|
|
findAllAccountFileDOWithRecur(allAccountFileDOList, childAccountFileDOList, onlyFolder); |
|
|
|
} |
|
|
|
//如果通过onlyFolder是true,只存储文件夹到allAccountFileDOList,否则都存储到allAccountFileDOList
|
|
|
|
if (!onlyFolder || Objects.equals(accountFileDO.getIsDir(), FolderFlagEnum.YES.getCode())) { |
|
|
|
allAccountFileDOList.add(accountFileDO); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 包括递归处理,生成新的ID |
|
|
|
* |
|
|
|
* @param accountFileDOList |
|
|
|
* @param targetParentId |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
public List<AccountFileDO> findBatchCopyFileWithRecur(List<AccountFileDO> accountFileDOList, Long targetParentId) { |
|
|
|
List<AccountFileDO> newAccountFileDOList = new ArrayList<>(); |
|
|
|
accountFileDOList.forEach(accountFileDO -> doCopyChildRecord(newAccountFileDOList, accountFileDO, targetParentId)); |
|
|
|
return newAccountFileDOList; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 递归处理,包括子文件夹 |
|
|
|
* |
|
|
|
* @param newAccountFileDOList |
|
|
|
* @param accountFileDO |
|
|
|
* @param targetParentId |
|
|
|
*/ |
|
|
|
private void doCopyChildRecord(List<AccountFileDO> newAccountFileDOList, AccountFileDO accountFileDO, Long targetParentId) { |
|
|
|
//保存旧的ID,方便查找子文件夹
|
|
|
|
Long oldAccountFileId = accountFileDO.getId(); |
|
|
|
//创建新记录
|
|
|
|
accountFileDO.setId(IdUtil.getSnowflakeNextId()); |
|
|
|
accountFileDO.setParentId(targetParentId); |
|
|
|
accountFileDO.setGmtModified(null); |
|
|
|
accountFileDO.setGmtCreate(null); |
|
|
|
|
|
|
|
//处理重复文件夹
|
|
|
|
processFileNameDuplicate(accountFileDO); |
|
|
|
|
|
|
|
//纳入容器存储
|
|
|
|
newAccountFileDOList.add(accountFileDO); |
|
|
|
|
|
|
|
//判断是文件还是文件夹,递归处理
|
|
|
|
if (Objects.equals(accountFileDO.getIsDir(), FolderFlagEnum.YES.getCode())) { |
|
|
|
//继续获取子文件夹列表
|
|
|
|
List<AccountFileDO> childAccountFileDOList = findChildAccountFile(accountFileDO.getAccountId(), oldAccountFileId); |
|
|
|
if (CollectionUtils.isEmpty(childAccountFileDOList)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
//递归处理
|
|
|
|
childAccountFileDOList |
|
|
|
.forEach(childAccountFileDO -> doCopyChildRecord(newAccountFileDOList, childAccountFileDO, accountFileDO.getId())); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 查找文件记录,只查询下一级,不递归 |
|
|
|
* |
|
|
|
* @param accountId |
|
|
|
* @param parentId |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
private List<AccountFileDO> findChildAccountFile(Long accountId, Long parentId) { |
|
|
|
return accountFileMapper.selectList(new QueryWrapper<AccountFileDO>() |
|
|
|
.eq("account_id", accountId).eq("parent_id", parentId)); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 检查存储空间和更新存储空间 |
|
|
|
* |
|
|
|
* @param accountId |
|
|
|
* @param fileSize |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
public boolean checkAndUpdateCapacity(Long accountId, Long fileSize) { |
|
|
|
StorageDO storageDO = storageMapper.selectOne(new QueryWrapper<StorageDO>().eq("account_id", accountId)); |
|
|
|
Long totalSize = storageDO.getTotalSize(); |
|
|
|
if (storageDO.getUsedSize() + fileSize <= totalSize) { |
|
|
|
storageDO.setUsedSize(storageDO.getUsedSize() + fileSize); |
|
|
|
storageMapper.updateById(storageDO); |
|
|
|
return true; |
|
|
|
} else { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |