package com.senmesh.sfile.service.impl; import com.senmesh.common.constant.CommonConstants; import com.senmesh.common.exception.util.Assert; import com.senmesh.common.util.ListUtil; import com.senmesh.common.util.UserContextHolder; import com.senmesh.sfile.constant.FileTypeEnum; import com.senmesh.sfile.constant.RootTypeEnum; import com.senmesh.sfile.convert.UserFileConvert; import com.senmesh.sfile.dao.UserFileMapper; import com.senmesh.sfile.dto.ExpandUserFileCreateDTO; import com.senmesh.sfile.dto.UserFileCreateDTO; import com.senmesh.sfile.dto.UserFileDTO; import com.senmesh.sfile.dto.UserFileModifyDTO; import com.senmesh.sfile.dto.UserFileModifyStatusDTO; import com.senmesh.sfile.dto.UserFileQueryDTO; import com.senmesh.sfile.entity.UserFile; import com.senmesh.sfile.service.UserFileService; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; /** * 用户文件ServiceImpl */ @Service public class UserFileServiceImpl implements UserFileService { @Autowired private UserFileMapper userFileMapper; @Override @Transactional public Long create(UserFileCreateDTO createDTO) { Long parentId = createDTO.getParentId(); String parentPath = createDTO.getParentPath(); Long userId = createDTO.getUserId(); String rootType = createDTO.getRootType(); // 1. 校验文件类型时fileCode不能为空 if (FileTypeEnum.isFile(createDTO.getFileType())) { Assert.notBlank(createDTO.getFileCode(), "文件编码不能为空"); } // 2. 校验父节点(如果不是根目录) if (parentId != null && parentId != 0L) { // 如果没有提供parentPath,需要查询父节点 if (StringUtils.isBlank(parentPath)) { UserFileDTO parentFile = this.findOne(UserFileQueryDTO.builder() .id(parentId) .userId(userId) .rootType(rootType) .build()); Assert.isTrue(FileTypeEnum.isFolder(parentFile.getFileType()), "父节点必须是文件夹"); // 获取父路径用于后续拼接 parentPath = parentFile.getPath(); } // 如果提供了parentPath,信任调用方,不做校验 } // 3. 插入数据获取ID UserFile userFile = UserFileConvert.INSTANCE.toEntity(createDTO); userFileMapper.saveEntity(userFile); Long newId = userFile.getId(); // 4. 构建path String path = StringUtils.isNotBlank(parentPath) ? parentPath + CommonConstants.COMMA + newId : String.valueOf(newId); // 5. 更新path userFile.setPath(path); userFileMapper.updateEntitySelective(userFile); return newId; } @Override @Transactional public Long create(ExpandUserFileCreateDTO expandCreateDTO) { Long userFileId = expandCreateDTO.getUserFileId(); Long userId = expandCreateDTO.getUserId(); String rootType = expandCreateDTO.getRootType(); // 1. 查询普通文件(限制rootType为普通) UserFileDTO normalFile = this.findOne(UserFileQueryDTO.builder() .id(userFileId) .userId(userId) .rootType(RootTypeEnum.NORMAL.getType()) .build()); // 2. 创建文件夹,文件名 = 原文件名 + 当前时间 String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); String folderName = normalFile.getFileName() + "_" + timestamp; UserFileCreateDTO folderCreateDTO = UserFileCreateDTO.builder() .userId(userId) .rootType(rootType) .fileType(FileTypeEnum.FOLDER.getType()) .fileName(folderName) .parentId(0L) .endTime(expandCreateDTO.getEndTime()) .accessCode(expandCreateDTO.getAccessCode()) .build(); Long folderId = this.create(folderCreateDTO); String folderPath = String.valueOf(folderId); // 3. 查询需要复制的文件列表 // 查询条件:path LIKE 'xxx%' AND fileType = '1' // 如果是文件:会查到自己 // 如果是文件夹:会查到其下所有文件类型的子文件 UserFileQueryDTO queryDTO = UserFileQueryDTO.builder() .userId(userId) .rootType(RootTypeEnum.NORMAL.getType()) .fileType(FileTypeEnum.FILE.getType()) .pathLike(normalFile.getPath()) .build(); queryDTO.disablePage(); List filesToCopy = this.list(queryDTO); // 4. 在文件夹下创建文件对象 for (UserFileDTO fileDTO : filesToCopy) { UserFileCreateDTO fileCreateDTO = UserFileCreateDTO.builder() .userId(userId) .rootType(rootType) .fileType(FileTypeEnum.FILE.getType()) .fileName(fileDTO.getFileName()) .fileCode(fileDTO.getFileCode()) .parentPath(folderPath) .build(); this.create(fileCreateDTO); } return folderId; } @Override @Transactional public void modify(UserFileModifyDTO modifyDTO) { Long id = modifyDTO.getId(); Long userId = modifyDTO.getUserId(); Long newParentId = modifyDTO.getParentId(); String rootType = modifyDTO.getRootType(); // 1. 根据id+userId+rootType查询文件是否存在 UserFileDTO existFile = this.findOne(UserFileQueryDTO.builder() .id(id) .userId(userId) .rootType(rootType) .build()); // 2. 判断是否需要更新path(parentId有值且与现存的parentId不一致) boolean needUpdatePath = newParentId != null && !newParentId.equals(existFile.getParentId()); if (needUpdatePath) { // 2.1 校验新父节点(如果不是根目录) if (newParentId != 0L) { UserFileDTO newParentFile = this.findOne(UserFileQueryDTO.builder() .id(newParentId) .userId(userId) .rootType(rootType) .build()); Assert.isTrue(FileTypeEnum.isFolder(newParentFile.getFileType()), "父节点必须是文件夹"); // 校验新父节点的rootType必须与当前文件相同 Assert.isTrue(existFile.getRootType().equals(newParentFile.getRootType()), "不能将文件移动到不同类型的文件夹"); } // 2.2 在SQL中一次性更新当前节点和所有子孙节点的path(SQL内部会限制相同userId和rootType) userFileMapper.updatePathByParentIdChange(id, newParentId); } // 3. 更新文件信息 UserFile userFile = UserFileConvert.INSTANCE.toEntity(modifyDTO); userFileMapper.updateEntitySelective(userFile); } @Override @Transactional public void modifyStatus(UserFileModifyStatusDTO modifyStatusDTO) { Long id = modifyStatusDTO.getId(); String oldStatus = modifyStatusDTO.getOldStatus(); String newStatus = modifyStatusDTO.getNewStatus(); Long operateUserId = UserContextHolder.getLoginUserId(); // 更新文件状态及其所有子文件状态(SQL内部会校验oldStatus并限制相同userId和rootType) userFileMapper.updateStatusWithChildren(id, oldStatus, newStatus, operateUserId); } @Override @Transactional public void modifyStatus(UserFileModifyStatusDTO modifyStatusDTO, Long userId) { Assert.notNull(userId, "用户ID不能为空"); Long id = modifyStatusDTO.getId(); // 校验文件是否存在且属于该用户 this.findOne(UserFileQueryDTO.builder() .id(id) .userId(userId) .build()); // 调用原方法修改状态 modifyStatus(modifyStatusDTO); } @Override @Transactional public void remove(Long id) { // 删除文件及其所有子文件(SQL内部会限制相同userId和rootType) userFileMapper.deleteFileWithChildren(id, UserContextHolder.getLoginUserId()); } @Override @Transactional public void remove(Long id, Long userId) { Assert.notNull(id, "文件ID不能为空"); Assert.notNull(userId, "用户ID不能为空"); // 校验文件是否存在且属于该用户 this.findOne(UserFileQueryDTO.builder() .id(id) .userId(userId) .build()); // 删除文件及其所有子文件(SQL内部会限制相同userId和rootType) remove(id); } @Override public List list(UserFileQueryDTO queryDTO) { queryDTO.disablePage(); return userFileMapper.list(queryDTO); } @Override public void page(UserFileQueryDTO queryDTO) { userFileMapper.list(queryDTO); } @Override public UserFileDTO find(UserFileQueryDTO queryDTO) { List userFileDTOs = this.list(queryDTO); return ListUtil.first(userFileDTOs); } @Override public UserFileDTO findOne(UserFileQueryDTO queryDTO) { UserFileDTO userFileDTO = this.find(queryDTO); Assert.notNull(userFileDTO, "文件不存在"); return userFileDTO; } }