登录增加验证码切换

This commit is contained in:
2026-04-06 22:17:50 +08:00
parent f556bdc09a
commit b0751cf45e
3 changed files with 148 additions and 84 deletions

View File

@@ -50,19 +50,18 @@ public class FileService {
boolean hasKeyword = keyword != null && !keyword.isEmpty(); boolean hasKeyword = keyword != null && !keyword.isEmpty();
// 根目录folderId == null 或 0 都视为根目录,统一用 0 查询
Long actualFolderId = (folderId == null || folderId == 0L) ? 0L : folderId;
if (hasKeyword) { if (hasKeyword) {
// 有搜索关键词:根目录搜索查所有,子目录搜索限当前目录 // 有搜索关键词:根目录搜索查所有,子目录搜索限当前目录
if (folderId != null) { if (!actualFolderId.equals(0L)) {
wrapper.eq(FileEntity::getFolderId, folderId); wrapper.eq(FileEntity::getFolderId, folderId);
} }
wrapper.like(FileEntity::getName, keyword); wrapper.like(FileEntity::getName, keyword);
} else { } else {
// 无搜索关键词:正常浏览当前目录 // 无搜索关键词:正常浏览当前目录
if (folderId != null) { wrapper.eq(FileEntity::getFolderId, actualFolderId);
wrapper.eq(FileEntity::getFolderId, folderId);
} else {
wrapper.isNull(FileEntity::getFolderId);
}
} }
wrapper.orderByDesc(FileEntity::getIsFolder) wrapper.orderByDesc(FileEntity::getIsFolder)
@@ -77,10 +76,13 @@ public class FileService {
.eq(FileEntity::getIsDeleted, 1) .eq(FileEntity::getIsDeleted, 1)
.orderByDesc(FileEntity::getDeletedAt); .orderByDesc(FileEntity::getDeletedAt);
if (folderId != null) { // 根目录folderId == null 或 0 都视为根目录
wrapper.eq(FileEntity::getFolderId, folderId); Long actualFolderId = (folderId == null || folderId == 0L) ? 0L : folderId;
// 根目录在数据库中兼容 null 和 0 两种值
if (actualFolderId.equals(0L)) {
wrapper.and(w -> w.eq(FileEntity::getFolderId, 0L).or().isNull(FileEntity::getFolderId));
} else { } else {
wrapper.isNull(FileEntity::getFolderId); wrapper.eq(FileEntity::getFolderId, actualFolderId);
} }
return fileMapper.selectList(wrapper); return fileMapper.selectList(wrapper);
@@ -93,26 +95,45 @@ public class FileService {
@Transactional @Transactional
public void moveToTrash(Long id, Long userId) { public void moveToTrash(Long id, Long userId) {
FileEntity file = fileMapper.selectById(id); FileEntity file = fileMapper.selectById(id);
if (file != null && file.getUserId().equals(userId)) { if (file == null || !file.getUserId().equals(userId)) return;
// 如果是文件夹,检查是否有子文件
if (file.getIsFolder() != null && file.getIsFolder() == 1) { if (file.getIsFolder() != null && file.getIsFolder() == 1) {
Long childCount = fileMapper.selectCount( // 文件夹:递归将所有子文件标记删除,移动到回收站根目录
new LambdaQueryWrapper<FileEntity>() moveChildrenToTrash(id, userId);
.eq(FileEntity::getFolderId, id) }
.eq(FileEntity::getIsDeleted, 0)
.eq(FileEntity::getUserId, userId) // 当前文件/文件夹:移动到回收站根目录
); LambdaUpdateWrapper<FileEntity> wrapper = new LambdaUpdateWrapper<>();
if (childCount != null && childCount > 0) { wrapper.eq(FileEntity::getId, id)
throw new RuntimeException("请删除该目录下文件后重试"); .set(FileEntity::getFolderId, 0L)
} .set(FileEntity::getIsDeleted, 1)
} .set(FileEntity::getDeletedAt, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
fileMapper.update(null, wrapper);
// 使用 LambdaUpdateWrapper 明确指定要更新的字段 }
/**
* 递归将文件夹下所有子文件/子文件夹标记为删除folderId 保持不变)
*/
private void moveChildrenToTrash(Long parentFolderId, Long userId) {
List<FileEntity> children = fileMapper.selectList(
new LambdaQueryWrapper<FileEntity>()
.eq(FileEntity::getFolderId, parentFolderId)
.eq(FileEntity::getIsDeleted, 0)
.eq(FileEntity::getUserId, userId)
);
for (FileEntity child : children) {
// 子文件/子文件夹只标删除folderId 保持原值不变
LambdaUpdateWrapper<FileEntity> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<FileEntity> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(FileEntity::getId, id) wrapper.eq(FileEntity::getId, child.getId())
.set(FileEntity::getIsDeleted, 1) .set(FileEntity::getIsDeleted, 1)
.set(FileEntity::getDeletedAt, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); .set(FileEntity::getDeletedAt, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
fileMapper.update(null, wrapper); fileMapper.update(null, wrapper);
if (child.getIsFolder() != null && child.getIsFolder() == 1) {
// 递归处理子文件夹
moveChildrenToTrash(child.getId(), userId);
}
} }
} }
@@ -121,21 +142,29 @@ public class FileService {
FileEntity file = fileMapper.selectById(id); FileEntity file = fileMapper.selectById(id);
if (file == null || !file.getUserId().equals(userId)) return; if (file == null || !file.getUserId().equals(userId)) return;
// 检查父文件夹是否在回收站,如果是,将文件移到根目录 // 检查父文件夹状态,决定还原目标
Long folderId = file.getFolderId(); // - folderId == 0 或 null根目录还原到根目录folderId=0
if (folderId != null) { // - 父文件夹存在且未删除:还原到原父文件夹
FileEntity parentFolder = fileMapper.selectById(folderId); // - 父文件夹不存在或已删除:还原到根目录(folderId=0
if (parentFolder != null && parentFolder.getIsDeleted() == 1) { Long originalFolderId = file.getFolderId();
// 父文件夹在回收站,将文件移到根目录 Long targetFolderId = originalFolderId;
folderId = null;
if (targetFolderId != null && !targetFolderId.equals(0L)) {
FileEntity parentFolder = fileMapper.selectById(targetFolderId);
if (parentFolder == null || parentFolder.getIsDeleted() == 1) {
// 父文件夹不在了,还原到根目录
targetFolderId = 0L;
} }
} else if (targetFolderId == null || targetFolderId.equals(0L)) {
// 本身就是根目录
targetFolderId = 0L;
} }
LambdaUpdateWrapper<FileEntity> wrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<FileEntity> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(FileEntity::getId, id) wrapper.eq(FileEntity::getId, id)
.set(FileEntity::getIsDeleted, 0) .set(FileEntity::getIsDeleted, 0)
.set(FileEntity::getDeletedAt, null) .set(FileEntity::getDeletedAt, null)
.set(FileEntity::getFolderId, folderId); .set(FileEntity::getFolderId, targetFolderId);
fileMapper.update(null, wrapper); fileMapper.update(null, wrapper);
// 如果是文件夹,递归还原所有子文件 // 如果是文件夹,递归还原所有子文件
@@ -169,28 +198,9 @@ public class FileService {
FileEntity file = fileMapper.selectById(id); FileEntity file = fileMapper.selectById(id);
if (file == null || !file.getUserId().equals(userId)) return; if (file == null || !file.getUserId().equals(userId)) return;
// 如果是文件夹,检查是否有未删除的子文件
if (file.getIsFolder() != null && file.getIsFolder() == 1) { if (file.getIsFolder() != null && file.getIsFolder() == 1) {
Long undeletedCount = fileMapper.selectCount( // 递归彻底删除所有子文件/子文件夹
new LambdaQueryWrapper<FileEntity>() deleteChildrenPermanently(id, userId);
.eq(FileEntity::getFolderId, id)
.eq(FileEntity::getIsDeleted, 0)
.eq(FileEntity::getUserId, userId)
);
if (undeletedCount != null && undeletedCount > 0) {
throw new RuntimeException("请先处理该目录下的子文件后重试");
}
// 检查是否有已删除的子文件(在回收站里的)
Long deletedChildrenCount = fileMapper.selectCount(
new LambdaQueryWrapper<FileEntity>()
.eq(FileEntity::getFolderId, id)
.eq(FileEntity::getIsDeleted, 1)
.eq(FileEntity::getUserId, userId)
);
if (deletedChildrenCount != null && deletedChildrenCount > 0) {
throw new RuntimeException("请先处理该目录下的子文件后重试");
}
} }
// 删除当前文件的物理文件并扣减存储 // 删除当前文件的物理文件并扣减存储
@@ -208,11 +218,58 @@ public class FileService {
fileMapper.deleteById(id); fileMapper.deleteById(id);
} }
/**
* 递归彻底删除文件夹下所有子文件/子文件夹
*/
private void deleteChildrenPermanently(Long parentFolderId, Long userId) {
List<FileEntity> children = fileMapper.selectList(
new LambdaQueryWrapper<FileEntity>()
.eq(FileEntity::getFolderId, parentFolderId)
.eq(FileEntity::getIsDeleted, 1)
.eq(FileEntity::getUserId, userId)
);
for (FileEntity child : children) {
if (child.getIsFolder() != null && child.getIsFolder() == 1) {
deleteChildrenPermanently(child.getId(), userId);
}
// 删除物理文件并扣减存储
if (child.getPath() != null && !child.getPath().isEmpty()) {
try {
Path filePath = Paths.get(storagePath).toAbsolutePath().resolve("files").resolve(child.getPath());
Files.deleteIfExists(filePath);
} catch (IOException e) {
// ignore
}
if (child.getSize() != null && child.getSize() > 0) {
userService.decreaseStorage(userId, child.getSize());
}
}
fileMapper.deleteById(child.getId());
}
}
@Transactional @Transactional
public void emptyTrash(Long userId) { public void emptyTrash(Long userId) {
List<FileEntity> trashFiles = getTrashFiles(userId, null); List<FileEntity> trashFiles = getTrashFiles(userId, null);
// 找出回收站里所有文件夹的 ID
java.util.Set<Long> folderIds = trashFiles.stream()
.filter(f -> f.getIsFolder() != null && f.getIsFolder() == 1)
.map(FileEntity::getId)
.collect(java.util.stream.Collectors.toSet());
for (FileEntity file : trashFiles) { for (FileEntity file : trashFiles) {
deletePermanently(file.getId(), userId); // 跳过子文件folderId 指向的父文件夹也在回收站中,会被级联删除)
if (folderIds.contains(file.getFolderId())) {
continue;
}
try {
deletePermanently(file.getId(), userId);
} catch (Exception e) {
// ignore级联删除时子文件可能已不存在
}
} }
} }
@@ -223,7 +280,8 @@ public class FileService {
folder.setType("folder"); folder.setType("folder");
folder.setIsFolder(1); folder.setIsFolder(1);
folder.setUserId(userId); folder.setUserId(userId);
folder.setFolderId(parentId); // 根目录 parentId = null 或 0 → 统一用 0
folder.setFolderId((parentId == null || parentId == 0L) ? 0L : parentId);
folder.setSize(0L); folder.setSize(0L);
folder.setIsShared(0); folder.setIsShared(0);
folder.setIsDeleted(0); folder.setIsDeleted(0);
@@ -498,8 +556,13 @@ public class FileService {
throw new RuntimeException("无法移动回收站中的文件"); throw new RuntimeException("无法移动回收站中的文件");
} }
// 检查目标文件夹是否存在(如果不是根目录) // 规范化 targetFolderIdnull → 0L根目录)
if (targetFolderId != null) { if (targetFolderId == null) {
targetFolderId = 0L;
}
// 检查目标文件夹是否存在如果不是根目录folderId=0 视为根目录)
if (!targetFolderId.equals(0L)) {
FileEntity targetFolder = fileMapper.selectById(targetFolderId); FileEntity targetFolder = fileMapper.selectById(targetFolderId);
if (targetFolder == null) { if (targetFolder == null) {
throw new RuntimeException("目标文件夹不存在"); throw new RuntimeException("目标文件夹不存在");
@@ -529,20 +592,22 @@ public class FileService {
} }
// 检查目标位置是否已有同名文件 // 检查目标位置是否已有同名文件
FileEntity existing = fileMapper.selectOne( LambdaQueryWrapper<FileEntity> dupWrapper = new LambdaQueryWrapper<FileEntity>()
new LambdaQueryWrapper<FileEntity>() .eq(FileEntity::getUserId, userId)
.eq(FileEntity::getUserId, userId) .eq(FileEntity::getName, file.getName())
.eq(FileEntity::getFolderId, targetFolderId) .eq(FileEntity::getIsDeleted, 0)
.eq(FileEntity::getName, file.getName()) .ne(FileEntity::getId, fileId);
.eq(FileEntity::getIsDeleted, 0)
.ne(FileEntity::getId, fileId) // targetFolderId 为 null 或 0 → 根目录,统一用 eq(0L)
); Long actualTarget = (targetFolderId == null || targetFolderId == 0L) ? 0L : targetFolderId;
dupWrapper.eq(FileEntity::getFolderId, actualTarget);
FileEntity existing = fileMapper.selectOne(dupWrapper);
if (existing != null) { if (existing != null) {
throw new RuntimeException("目标位置已存在同名文件"); throw new RuntimeException("目标位置已存在同名文件");
} }
file.setFolderId(targetFolderId); // 使用直接更新folderId=0 表示根目录null 表示"我的文档"原始状态(已在 moveToTrash 时处理)
// 使用直接更新确保 null 值也能被设置
fileMapper.update(null, fileMapper.update(null,
new LambdaUpdateWrapper<FileEntity>() new LambdaUpdateWrapper<FileEntity>()
.eq(FileEntity::getId, fileId) .eq(FileEntity::getId, fileId)
@@ -700,9 +765,13 @@ public class FileService {
private List<FileEntity> buildFolderTree(List<FileEntity> allFolders, Long parentId, List<Long> excludeIds) { private List<FileEntity> buildFolderTree(List<FileEntity> allFolders, Long parentId, List<Long> excludeIds) {
List<FileEntity> tree = new ArrayList<>(); List<FileEntity> tree = new ArrayList<>();
for (FileEntity folder : allFolders) { for (FileEntity folder : allFolders) {
// 匹配父级关系 // 匹配父级关系parentId == null 或 0 都视为根目录,匹配 folderId == 0 的文件夹
boolean isChildOfParent = (parentId == null && folder.getFolderId() == null) boolean isChildOfParent;
|| (parentId != null && parentId.equals(folder.getFolderId())); if (parentId == null || parentId == 0L) {
isChildOfParent = (folder.getFolderId() != null && folder.getFolderId() == 0L);
} else {
isChildOfParent = parentId.equals(folder.getFolderId());
}
if (isChildOfParent) { if (isChildOfParent) {
// 递归构建子文件夹 // 递归构建子文件夹

View File

@@ -50,7 +50,7 @@ const visible = computed({
set: (val) => emit('update:modelValue', val) set: (val) => emit('update:modelValue', val)
}) })
const targetFolderId = ref(null) const targetFolderId = ref(0)
// 树形配置 // 树形配置
const treeProps = { const treeProps = {
@@ -64,7 +64,7 @@ const folderTreeData = computed(() => {
// 根节点 // 根节点
const rootNodes = [ const rootNodes = [
{ {
id: 'root', id: 0,
name: '根目录', name: '根目录',
children: [] children: []
} }
@@ -90,20 +90,21 @@ const folderTreeData = computed(() => {
}) })
const handleConfirm = () => { const handleConfirm = () => {
// targetFolderId = 0 表示根目录null 表示未选择
emit('confirm', targetFolderId.value) emit('confirm', targetFolderId.value)
visible.value = false visible.value = false
targetFolderId.value = null targetFolderId.value = 0
} }
const open = () => { const open = () => {
targetFolderId.value = null targetFolderId.value = 0
visible.value = true visible.value = true
} }
// 重置选中状态 // 重置选中状态
watch(visible, (val) => { watch(visible, (val) => {
if (!val) { if (!val) {
targetFolderId.value = null targetFolderId.value = 0
} }
}) })

View File

@@ -623,14 +623,8 @@ const handleBatchMove = async () => {
} }
const handleConfirmBatchMove = async (targetFolderId) => { const handleConfirmBatchMove = async (targetFolderId) => {
// Handle target folder ID // targetFolderId = 0 表示根目录null 表示未选择(此时用 0 作为默认值)
let finalFolderId = null let finalFolderId = (targetFolderId === null || targetFolderId === undefined) ? 0 : targetFolderId
if (targetFolderId === 'root' || targetFolderId === '' || targetFolderId === null || targetFolderId === undefined) {
finalFolderId = null
} else {
finalFolderId = targetFolderId
}
let successCount = 0 let successCount = 0
let failCount = 0 let failCount = 0