mirror of
				https://github.com/cloudreve/cloudreve.git
				synced 2025-11-01 00:57:15 +08:00 
			
		
		
		
	Feat: add option to force delete file record in database
This commit is contained in:
		| @ -131,8 +131,8 @@ func (fs *FileSystem) Move(ctx context.Context, dirs, files []uint, src, dst str | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Delete 递归删除对象 | ||||
| func (fs *FileSystem) Delete(ctx context.Context, dirs, files []uint) error { | ||||
| // Delete 递归删除对象, force 为 true 时强制删除文件记录,忽略物理删除是否成功 | ||||
| func (fs *FileSystem) Delete(ctx context.Context, dirs, files []uint, force bool) error { | ||||
| 	// 已删除的总容量,map用于去重 | ||||
| 	var deletedStorage = make(map[uint]uint64) | ||||
| 	var totalStorage = make(map[uint]uint64) | ||||
| @ -182,7 +182,12 @@ func (fs *FileSystem) Delete(ctx context.Context, dirs, files []uint) error { | ||||
| 		totalStorage[fs.FileTarget[i].ID] = fs.FileTarget[i].Size | ||||
| 		allFileIDs = append(allFileIDs, fs.FileTarget[i].ID) | ||||
| 	} | ||||
| 	// TODO 用户自主选择是否强制删除 | ||||
|  | ||||
| 	// 如果强制删除,则将全部文件视为删除成功 | ||||
| 	if force { | ||||
| 		deletedFileIDs = allFileIDs | ||||
| 		deletedStorage = totalStorage | ||||
| 	} | ||||
|  | ||||
| 	// 删除文件记录 | ||||
| 	err = model.DeleteFileByIDs(deletedFileIDs) | ||||
|  | ||||
| @ -342,12 +342,65 @@ func TestFileSystem_Delete(t *testing.T) { | ||||
| 			WillReturnResult(sqlmock.NewResult(0, 3)) | ||||
| 		mock.ExpectCommit() | ||||
|  | ||||
| 		err := fs.Delete(ctx, []uint{1}, []uint{1}) | ||||
| 		err := fs.Delete(ctx, []uint{1}, []uint{1}, false) | ||||
| 		asserts.Error(err) | ||||
| 		asserts.Equal(203, err.(serializer.AppError).Code) | ||||
| 		asserts.Equal(uint64(3), fs.User.Storage) | ||||
| 		asserts.NoError(mock.ExpectationsWereMet()) | ||||
| 	} | ||||
| 	//全部未成功,强制 | ||||
| 	{ | ||||
| 		fs.CleanTargets() | ||||
| 		mock.ExpectQuery("SELECT(.+)"). | ||||
| 			WillReturnRows( | ||||
| 				sqlmock.NewRows([]string{"id"}). | ||||
| 					AddRow(1). | ||||
| 					AddRow(2). | ||||
| 					AddRow(3), | ||||
| 			) | ||||
| 		mock.ExpectQuery("SELECT(.+)"). | ||||
| 			WithArgs(1, 2, 3). | ||||
| 			WillReturnRows( | ||||
| 				sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}). | ||||
| 					AddRow(4, "1.txt", "1.txt", 365, 1), | ||||
| 			) | ||||
| 		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "source_name", "policy_id", "size"}).AddRow(1, "2.txt", "2.txt", 365, 2)) | ||||
| 		mock.ExpectQuery("SELECT(.+)files(.+)"). | ||||
| 			WillReturnRows(sqlmock.NewRows([]string{"id", "policy_id", "source_name"})) | ||||
| 		// 查询上传策略 | ||||
| 		mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id", "type"}).AddRow(365, "local")) | ||||
| 		// 删除文件记录 | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("DELETE(.+)"). | ||||
| 			WillReturnResult(sqlmock.NewResult(0, 3)) | ||||
| 		mock.ExpectCommit() | ||||
| 		// 删除对应分享 | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("UPDATE(.+)shares"). | ||||
| 			WillReturnResult(sqlmock.NewResult(0, 3)) | ||||
| 		mock.ExpectCommit() | ||||
| 		// 归还容量 | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("UPDATE(.+)"). | ||||
| 			WillReturnResult(sqlmock.NewResult(0, 3)) | ||||
| 		mock.ExpectCommit() | ||||
| 		// 删除目录 | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("DELETE(.+)"). | ||||
| 			WillReturnResult(sqlmock.NewResult(0, 3)) | ||||
| 		mock.ExpectCommit() | ||||
| 		// 删除对应分享 | ||||
| 		mock.ExpectBegin() | ||||
| 		mock.ExpectExec("UPDATE(.+)shares"). | ||||
| 			WillReturnResult(sqlmock.NewResult(0, 3)) | ||||
| 		mock.ExpectCommit() | ||||
|  | ||||
| 		fs.FileTarget = []model.File{} | ||||
| 		fs.DirTarget = []model.Folder{} | ||||
| 		err := fs.Delete(ctx, []uint{1}, []uint{1}, true) | ||||
| 		asserts.NoError(err) | ||||
| 		asserts.Equal(uint64(0), fs.User.Storage) | ||||
| 	} | ||||
| 	//全部成功 | ||||
| 	{ | ||||
| 		fs.CleanTargets() | ||||
| @ -402,7 +455,7 @@ func TestFileSystem_Delete(t *testing.T) { | ||||
|  | ||||
| 		fs.FileTarget = []model.File{} | ||||
| 		fs.DirTarget = []model.Folder{} | ||||
| 		err = fs.Delete(ctx, []uint{1}, []uint{1}) | ||||
| 		err = fs.Delete(ctx, []uint{1}, []uint{1}, false) | ||||
| 		asserts.NoError(err) | ||||
| 		asserts.Equal(uint64(0), fs.User.Storage) | ||||
| 	} | ||||
|  | ||||
| @ -281,7 +281,7 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *files | ||||
|  | ||||
| 	// 尝试作为文件删除 | ||||
| 	if ok, file := fs.IsFileExist(reqPath); ok { | ||||
| 		if err := fs.Delete(ctx, []uint{}, []uint{file.ID}); err != nil { | ||||
| 		if err := fs.Delete(ctx, []uint{}, []uint{file.ID}, false); err != nil { | ||||
| 			return http.StatusMethodNotAllowed, err | ||||
| 		} | ||||
| 		return http.StatusNoContent, nil | ||||
| @ -289,7 +289,7 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *files | ||||
|  | ||||
| 	// 尝试作为目录删除 | ||||
| 	if ok, folder := fs.IsPathExist(reqPath); ok { | ||||
| 		if err := fs.Delete(ctx, []uint{folder.ID}, []uint{}); err != nil { | ||||
| 		if err := fs.Delete(ctx, []uint{folder.ID}, []uint{}, false); err != nil { | ||||
| 			return http.StatusMethodNotAllowed, err | ||||
| 		} | ||||
| 		return http.StatusNoContent, nil | ||||
|  | ||||
| @ -58,7 +58,7 @@ func (service *FileBatchService) Delete(c *gin.Context) serializer.Response { | ||||
| 			} | ||||
|  | ||||
| 			// 执行删除 | ||||
| 			fs.Delete(context.Background(), []uint{}, ids) | ||||
| 			fs.Delete(context.Background(), []uint{}, ids, false) | ||||
| 			fs.Recycle() | ||||
| 		} | ||||
| 	}(userFile) | ||||
|  | ||||
| @ -65,7 +65,7 @@ func (service *UserBatchService) Delete() serializer.Response { | ||||
| 		if err != nil { | ||||
| 			return serializer.Err(serializer.CodeNotFound, "无法找到用户根目录", err) | ||||
| 		} | ||||
| 		fs.Delete(context.Background(), []uint{root.ID}, []uint{}) | ||||
| 		fs.Delete(context.Background(), []uint{root.ID}, []uint{}, false) | ||||
|  | ||||
| 		// 删除相关任务 | ||||
| 		model.DB.Where("user_id = ?", uid).Delete(&model.Download{}) | ||||
|  | ||||
| @ -264,7 +264,7 @@ func (service *ItemIDService) Delete(ctx context.Context, c *gin.Context) serial | ||||
|  | ||||
| 	// 删除对象 | ||||
| 	items := service.Raw() | ||||
| 	err = fs.Delete(ctx, items.Dirs, items.Items) | ||||
| 	err = fs.Delete(ctx, items.Dirs, items.Items, false) | ||||
| 	if err != nil { | ||||
| 		return serializer.Err(serializer.CodeNotSet, err.Error(), err) | ||||
| 	} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 HFO4
					HFO4