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  }