github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/models/folder.go (about) 1 package model 2 3 import ( 4 "errors" 5 "path" 6 "time" 7 8 "github.com/cloudreve/Cloudreve/v3/pkg/util" 9 "github.com/jinzhu/gorm" 10 ) 11 12 // Folder 目录 13 type Folder struct { 14 // 表字段 15 gorm.Model 16 Name string `gorm:"unique_index:idx_only_one_name"` 17 ParentID *uint `gorm:"index:parent_id;unique_index:idx_only_one_name"` 18 OwnerID uint `gorm:"index:owner_id"` 19 20 // 数据库忽略字段 21 Position string `gorm:"-"` 22 WebdavDstName string `gorm:"-"` 23 } 24 25 // Create 创建目录 26 func (folder *Folder) Create() (uint, error) { 27 if err := DB.FirstOrCreate(folder, *folder).Error; err != nil { 28 folder.Model = gorm.Model{} 29 err2 := DB.First(folder, *folder).Error 30 return folder.ID, err2 31 } 32 33 return folder.ID, nil 34 } 35 36 // GetChild 返回folder下名为name的子目录,不存在则返回错误 37 func (folder *Folder) GetChild(name string) (*Folder, error) { 38 var resFolder Folder 39 err := DB. 40 Where("parent_id = ? AND owner_id = ? AND name = ?", folder.ID, folder.OwnerID, name). 41 First(&resFolder).Error 42 43 // 将子目录的路径传递下去 44 if err == nil { 45 resFolder.Position = path.Join(folder.Position, folder.Name) 46 } 47 return &resFolder, err 48 } 49 50 // TraceRoot 向上递归查找父目录 51 func (folder *Folder) TraceRoot() error { 52 if folder.ParentID == nil { 53 return nil 54 } 55 56 var parentFolder Folder 57 err := DB. 58 Where("id = ? AND owner_id = ?", folder.ParentID, folder.OwnerID). 59 First(&parentFolder).Error 60 61 if err == nil { 62 err := parentFolder.TraceRoot() 63 folder.Position = path.Join(parentFolder.Position, parentFolder.Name) 64 return err 65 } 66 67 return err 68 } 69 70 // GetChildFolder 查找子目录 71 func (folder *Folder) GetChildFolder() ([]Folder, error) { 72 var folders []Folder 73 result := DB.Where("parent_id = ?", folder.ID).Find(&folders) 74 75 if result.Error == nil { 76 for i := 0; i < len(folders); i++ { 77 folders[i].Position = path.Join(folder.Position, folder.Name) 78 } 79 } 80 return folders, result.Error 81 } 82 83 // GetRecursiveChildFolder 查找所有递归子目录,包括自身 84 func GetRecursiveChildFolder(dirs []uint, uid uint, includeSelf bool) ([]Folder, error) { 85 folders := make([]Folder, 0, len(dirs)) 86 var err error 87 88 var parFolders []Folder 89 result := DB.Where("owner_id = ? and id in (?)", uid, dirs).Find(&parFolders) 90 if result.Error != nil { 91 return folders, err 92 } 93 94 // 整理父目录的ID 95 var parentIDs = make([]uint, 0, len(parFolders)) 96 for _, folder := range parFolders { 97 parentIDs = append(parentIDs, folder.ID) 98 } 99 100 if includeSelf { 101 // 合并至最终结果 102 folders = append(folders, parFolders...) 103 } 104 parFolders = []Folder{} 105 106 // 递归查询子目录,最大递归65535次 107 for i := 0; i < 65535; i++ { 108 109 result = DB.Where("owner_id = ? and parent_id in (?)", uid, parentIDs).Find(&parFolders) 110 111 // 查询结束条件 112 if len(parFolders) == 0 { 113 break 114 } 115 116 // 整理父目录的ID 117 parentIDs = make([]uint, 0, len(parFolders)) 118 for _, folder := range parFolders { 119 parentIDs = append(parentIDs, folder.ID) 120 } 121 122 // 合并至最终结果 123 folders = append(folders, parFolders...) 124 parFolders = []Folder{} 125 126 } 127 128 return folders, err 129 } 130 131 // DeleteFolderByIDs 根据给定ID批量删除目录记录 132 func DeleteFolderByIDs(ids []uint) error { 133 result := DB.Where("id in (?)", ids).Unscoped().Delete(&Folder{}) 134 return result.Error 135 } 136 137 // GetFoldersByIDs 根据ID和用户查找所有目录 138 func GetFoldersByIDs(ids []uint, uid uint) ([]Folder, error) { 139 var folders []Folder 140 result := DB.Where("id in (?) AND owner_id = ?", ids, uid).Find(&folders) 141 return folders, result.Error 142 } 143 144 // MoveOrCopyFileTo 将此目录下的files移动或复制至dstFolder, 145 // 返回此操作新增的容量 146 func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy bool) (uint64, error) { 147 // 已复制文件的总大小 148 var copiedSize uint64 149 150 if isCopy { 151 // 检索出要复制的文件 152 var originFiles = make([]File, 0, len(files)) 153 if err := DB.Where( 154 "id in (?) and user_id = ? and folder_id = ?", 155 files, 156 folder.OwnerID, 157 folder.ID, 158 ).Find(&originFiles).Error; err != nil { 159 return 0, err 160 } 161 162 // 复制文件记录 163 for _, oldFile := range originFiles { 164 if !oldFile.CanCopy() { 165 util.Log().Warning("Cannot copy file %q because it's being uploaded now, skipping...", oldFile.Name) 166 continue 167 } 168 169 oldFile.Model = gorm.Model{} 170 oldFile.FolderID = dstFolder.ID 171 oldFile.UserID = dstFolder.OwnerID 172 173 // webdav目标名重置 174 if dstFolder.WebdavDstName != "" { 175 oldFile.Name = dstFolder.WebdavDstName 176 } 177 178 if err := DB.Create(&oldFile).Error; err != nil { 179 return copiedSize, err 180 } 181 182 copiedSize += oldFile.Size 183 } 184 185 } else { 186 var updates = map[string]interface{}{ 187 "folder_id": dstFolder.ID, 188 } 189 // webdav目标名重置 190 if dstFolder.WebdavDstName != "" { 191 updates["name"] = dstFolder.WebdavDstName 192 } 193 194 // 更改顶级要移动文件的父目录指向 195 err := DB.Model(File{}).Where( 196 "id in (?) and user_id = ? and folder_id = ?", 197 files, 198 folder.OwnerID, 199 folder.ID, 200 ). 201 Update(updates). 202 Error 203 if err != nil { 204 return 0, err 205 } 206 207 } 208 209 return copiedSize, nil 210 211 } 212 213 // CopyFolderTo 将此目录及其子目录及文件递归复制至dstFolder 214 // 返回此操作新增的容量 215 func (folder *Folder) CopyFolderTo(folderID uint, dstFolder *Folder) (size uint64, err error) { 216 // 列出所有子目录 217 subFolders, err := GetRecursiveChildFolder([]uint{folderID}, folder.OwnerID, true) 218 if err != nil { 219 return 0, err 220 } 221 222 // 抽离所有子目录的ID 223 var subFolderIDs = make([]uint, len(subFolders)) 224 for key, value := range subFolders { 225 subFolderIDs[key] = value.ID 226 } 227 228 // 复制子目录 229 var newIDCache = make(map[uint]uint) 230 for _, folder := range subFolders { 231 // 新的父目录指向 232 var newID uint 233 // 顶级目录直接指向新的目的目录 234 if folder.ID == folderID { 235 newID = dstFolder.ID 236 // webdav目标名重置 237 if dstFolder.WebdavDstName != "" { 238 folder.Name = dstFolder.WebdavDstName 239 } 240 } else if IDCache, ok := newIDCache[*folder.ParentID]; ok { 241 newID = IDCache 242 } else { 243 util.Log().Warning("Failed to get parent folder %q", *folder.ParentID) 244 return size, errors.New("Failed to get parent folder") 245 } 246 247 // 插入新的目录记录 248 oldID := folder.ID 249 folder.Model = gorm.Model{} 250 folder.ParentID = &newID 251 folder.OwnerID = dstFolder.OwnerID 252 if err = DB.Create(&folder).Error; err != nil { 253 return size, err 254 } 255 // 记录新的ID以便其子目录使用 256 newIDCache[oldID] = folder.ID 257 258 } 259 260 // 复制文件 261 var originFiles = make([]File, 0, len(subFolderIDs)) 262 if err := DB.Where( 263 "user_id = ? and folder_id in (?)", 264 folder.OwnerID, 265 subFolderIDs, 266 ).Find(&originFiles).Error; err != nil { 267 return 0, err 268 } 269 270 // 复制文件记录 271 for _, oldFile := range originFiles { 272 if !oldFile.CanCopy() { 273 util.Log().Warning("Cannot copy file %q because it's being uploaded now, skipping...", oldFile.Name) 274 continue 275 } 276 277 oldFile.Model = gorm.Model{} 278 oldFile.FolderID = newIDCache[oldFile.FolderID] 279 oldFile.UserID = dstFolder.OwnerID 280 if err := DB.Create(&oldFile).Error; err != nil { 281 return size, err 282 } 283 284 size += oldFile.Size 285 } 286 287 return size, nil 288 289 } 290 291 // MoveFolderTo 将folder目录下的dirs子目录复制或移动到dstFolder, 292 // 返回此过程中增加的容量 293 func (folder *Folder) MoveFolderTo(dirs []uint, dstFolder *Folder) error { 294 295 // 如果目标位置为待移动的目录,会导致 parent 为自己 296 // 造成死循环且无法被除搜索以外的组件展示 297 if folder.OwnerID == dstFolder.OwnerID && util.ContainsUint(dirs, dstFolder.ID) { 298 return errors.New("cannot move a folder into itself") 299 } 300 301 var updates = map[string]interface{}{ 302 "parent_id": dstFolder.ID, 303 } 304 // webdav目标名重置 305 if dstFolder.WebdavDstName != "" { 306 updates["name"] = dstFolder.WebdavDstName 307 } 308 309 // 更改顶级要移动目录的父目录指向 310 err := DB.Model(Folder{}).Where( 311 "id in (?) and owner_id = ? and parent_id = ?", 312 dirs, 313 folder.OwnerID, 314 folder.ID, 315 ).Update(updates).Error 316 317 return err 318 319 } 320 321 // Rename 重命名目录 322 func (folder *Folder) Rename(new string) error { 323 return DB.Model(&folder).UpdateColumn("name", new).Error 324 } 325 326 /* 327 实现 FileInfo.FileInfo 接口 328 TODO 测试 329 */ 330 331 func (folder *Folder) GetName() string { 332 return folder.Name 333 } 334 335 func (folder *Folder) GetSize() uint64 { 336 return 0 337 } 338 func (folder *Folder) ModTime() time.Time { 339 return folder.UpdatedAt 340 } 341 func (folder *Folder) IsDir() bool { 342 return true 343 } 344 func (folder *Folder) GetPosition() string { 345 return folder.Position 346 }