Преглед на файлове

批量移动云盘文件

kobe6258 преди 1 месец
родител
ревизия
cd789c4eff
променени са 5 файла, в които са добавени 768 реда и са изтрити 3 реда
  1. 537 0
      controllers/cloud_disk.go
  2. 14 2
      models/cloud_disk_menu.go
  3. 107 1
      models/cloud_disk_resource.go
  4. 36 0
      routers/commentsRouter.go
  5. 74 0
      services/cloud_disk.go

+ 537 - 0
controllers/cloud_disk.go

@@ -10,11 +10,13 @@ import (
 	"fmt"
 	"github.com/h2non/filetype"
 	"github.com/rdlucklib/rdluck_tools/http"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"io/ioutil"
 	"os"
 	"path"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -94,6 +96,7 @@ func (this *CloudDiskController) MenuCreate() {
 }
 
 // MenuRename 重命名文件夹
+// Deprecated 已过期--改用modify入口
 // @Title 重命名文件夹
 // @Description 重命名文件夹
 // @Param	request	body models.CloudDiskMenuRenameReq true "type json string"
@@ -166,6 +169,110 @@ func (this *CloudDiskController) MenuRename() {
 	br.Msg = "操作成功"
 }
 
+// MenuModify 重命名移动文件夹
+// @Title 重命名移动文件夹
+// @Description 重命名移动文件夹
+// @Param	request	body models.CloudDiskMenuRenameReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /menu/modify [post]
+func (this *CloudDiskController) MenuModify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.CloudDiskMenuModifyReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.MenuId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	req.MenuName = strings.TrimSpace(req.MenuName)
+	if req.MenuName == "" {
+		br.Msg = "文件夹名称不可为空"
+		return
+	}
+	req.MenuName = strings.TrimSpace(req.MenuName)
+
+	// 权限校验
+	ok, e := services.CheckCloudDiskManagerAuth(this.SysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+
+	// 重名校验
+	menuItem := new(models.CloudDiskMenu)
+	if e = menuItem.GetItemById(req.MenuId); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取目录信息失败, Err: " + e.Error()
+		return
+	}
+	var oldParentId, newParentId int
+	moved := false
+	existItem := new(models.CloudDiskMenu)
+	existCond := ` AND menu_name = ? AND parent_id = ? AND menu_id <> ? `
+	existPars := make([]interface{}, 0)
+	menuName := req.MenuName
+	parentId := req.ParentId
+	menuId := req.MenuId
+	//没有移动目录检查重名时需要判断当前目录是否有同名的其他menu切不为自己本身
+	if req.ParentId != menuItem.ParentId {
+		menuId = 0
+		moved = true
+		oldParentId = menuItem.ParentId
+		newParentId = req.ParentId
+	}
+	existPars = append(existPars, menuName, parentId, menuId)
+	if e = existItem.GetItemByCondition(existCond, existPars); e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名目录失败, Err: " + e.Error()
+		return
+	}
+	if existItem != nil && existItem.MenuId > 0 {
+		br.Msg = "该文件夹已存在"
+		return
+	}
+
+	menuItem.MenuName = req.MenuName
+	menuItem.ParentId = req.ParentId
+	menuItem.ModifyTime = time.Now().Local()
+	if e = menuItem.Update([]string{"MenuName", "ParentId", "ModifyTime"}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "目录重命名失败, Err: " + e.Error()
+		return
+	}
+	// 更新目录大小
+	if moved {
+		go func() {
+			if e = services.UpdateCloudDiskMenuSize(oldParentId); e != nil {
+				alarm_msg.SendAlarmMsg("更新云盘目录大小失败, Err: "+e.Error(), 3)
+			}
+		}()
+		go func() {
+			if e = services.UpdateCloudDiskMenuSize(newParentId); e != nil {
+				alarm_msg.SendAlarmMsg("更新云盘目录大小失败, Err: "+e.Error(), 3)
+			}
+		}()
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
 // MenuDelete 删除文件夹
 // @Title 删除文件夹
 // @Description 删除文件夹
@@ -677,6 +784,7 @@ func (this *CloudDiskController) ResourceUpload() {
 }
 
 // ResourceRename 重命名文件
+// Deprecated 已过期--改用modify入口
 // @Title 重命名文件
 // @Description 重命名文件
 // @Param	request	body models.CloudDiskResourceRenameReq true "type json string"
@@ -751,6 +859,119 @@ func (this *CloudDiskController) ResourceRename() {
 	br.Msg = "操作成功"
 }
 
+// ResourceModify 编辑文件
+// @Title 编辑文件
+// @Description 编辑文件
+// @Param	request	body models.CloudDiskResourceRenameReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /resource/modify [post]
+func (this *CloudDiskController) ResourceModify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.CloudDiskResourceModifyReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.ResourceId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	if req.MenuId < 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	req.ResourceName = strings.TrimSpace(req.ResourceName)
+	if req.ResourceName == "" {
+		br.Msg = "文件名称不可为空"
+		return
+	}
+
+	// 权限校验-仅上传人或管理员可操作
+	resourceItem := new(models.CloudDiskResource)
+	if e := resourceItem.GetItemById(req.ResourceId); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取文件信息失败, Err: " + e.Error()
+		return
+	}
+	if resourceItem.AdminId != this.SysUser.AdminId {
+		ok, e := services.CheckCloudDiskManagerAuth(this.SysUser.RoleTypeCode)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+			return
+		}
+		if !ok {
+			br.Msg = "无权操作"
+			return
+		}
+	}
+	moved := false
+	var ordMenuId, newMenuId int
+	if resourceItem.MenuId != req.MenuId && req.MenuId > 0 {
+		moved = true
+		ordMenuId = resourceItem.MenuId
+		newMenuId = req.MenuId
+	}
+	// 重名校验
+	existItem := new(models.CloudDiskResource)
+	menuId := req.MenuId
+
+	resourceName := req.ResourceName
+	resourceId := req.ResourceId
+	if req.MenuId != resourceItem.MenuId {
+		resourceId = 0
+	}
+	existCond := ` AND resource_name = ? AND menu_id = ? AND resource_id <> ? `
+	if req.MenuId == 0 {
+		menuId = resourceItem.MenuId
+		resourceName = resourceItem.ResourceName
+		resourceId = resourceItem.ResourceId
+	}
+	existPars := make([]interface{}, 0)
+	existPars = append(existPars, resourceName, menuId, resourceId)
+	if e := existItem.GetItemByCondition(existCond, existPars); e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名文件失败, Err: " + e.Error()
+		return
+	}
+	if existItem != nil && existItem.MenuId > 0 {
+		br.Msg = "该文件名已存在"
+		return
+	}
+
+	resourceItem.ResourceName = req.ResourceName
+	resourceItem.MenuId = req.MenuId
+	resourceItem.ModifyTime = time.Now().Local()
+	if e := resourceItem.Update([]string{"ResourceName", "MenuId", "ModifyTime"}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "文件重命名失败, Err: " + e.Error()
+		return
+	}
+	// 更新目录大小
+
+	if moved {
+		go func() {
+			if e := services.UpdateCloudDiskMenuSize(ordMenuId); e != nil {
+				alarm_msg.SendAlarmMsg("更新云盘目录大小失败, Err: "+e.Error(), 3)
+			}
+		}()
+		go func() {
+			if e := services.UpdateCloudDiskMenuSize(newMenuId); e != nil {
+				alarm_msg.SendAlarmMsg("更新云盘目录大小失败, Err: "+e.Error(), 3)
+			}
+		}()
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
 // ResourceDelete 删除文件
 // @Title 删除文件
 // @Description 删除文件
@@ -998,3 +1219,319 @@ func (this *CloudDiskController) BatchDelete() {
 	br.Success = true
 	br.Msg = "操作成功"
 }
+
+// Page 云盘文件分页查询
+// @Title 云盘文件分页查询
+// @Description 云盘列表
+// @Param   MenuId  query  int  false  "目录ID"
+// @Param   Keyword  query  string  false  "关键词"
+// @Param   SortType  query  int  false  "排序方式: 0-默认; 1-创建时间倒序; 2-创建时间正序"
+// @Success 200 Ret=200 获取成功
+// @router /page [get]
+func (this *CloudDiskController) Page() {
+	// 记录开始时间
+	startTime := time.Now()
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var treeNode *models.MenuNode
+	var menuNameMap map[int]string
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+
+		treeNode, menuNameMap = services.BuildCloudDiskMenuTree()
+		if treeNode == nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取目录树失败, Err: 创建目录树结构失败"
+			return
+		}
+	}()
+	subMenu, _ := this.GetBool("SubMenu", false)
+	menuIds := this.GetString("MenuIds", "")
+	keyword := this.GetString("Keyword", "")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("Current")
+	//查询条件
+	resourceCond := ``
+	resourcePars := make([]interface{}, 0)
+	orderRule := `create_time DESC`
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize := utils.StartIndex(currentIndex, pageSize)
+	if keyword != "" {
+		// 有关键词时全局搜索
+		resourceCond += ` AND resource_name LIKE ? `
+		resourcePars = append(resourcePars, utils.GetLikeKeyword(keyword))
+	}
+	var menuIdList []int
+	if menuIds != "" {
+		menuIdStrList := strings.Split(menuIds, ",")
+		for i := range menuIdStrList {
+			menuId, err := strconv.Atoi(menuIdStrList[i])
+			if err != nil {
+				br.Msg = "参数有误"
+				br.ErrMsg = "参数有误, Err:分类ID包含非数字类型字符"
+				return
+			}
+			menuIdList = append(menuIdList, menuId)
+		}
+	}
+	// 打印查询总数耗时
+	fmt.Printf("初始化查询参数耗时: %v\n", time.Since(startTime))
+	//等待完成建树
+	wg.Wait()
+	fmt.Println()
+	if len(menuIdList) > 0 {
+		if subMenu {
+			menuIdChan := make(chan []int, len(menuIdList))
+			for i := range menuIdList {
+				wg.Add(1)
+				go func(menuId int) {
+					defer wg.Done()
+					menuIdChan <- services.TraceTreeNode(menuId, treeNode.Children)
+				}(menuIdList[i])
+			}
+			// 启动一个 goroutine 来关闭通道
+			wg.Wait()
+			close(menuIdChan)
+			for i := range menuIdChan {
+				menuIdList = append(menuIdList, i...)
+			}
+		}
+		//去重
+		filterMap := make(map[int]int)
+		var filterList []int
+		for i := range menuIdList {
+			if _, ok := filterMap[menuIdList[i]]; ok {
+				continue
+			}
+			filterMap[menuIdList[i]] = 1
+			filterList = append(filterList, menuIdList[i])
+		}
+		fmt.Println(filterList)
+		resourceCond += fmt.Sprintf(` AND menu_id IN (%s) `, utils.GetOrmInReplace(len(filterList)))
+		resourcePars = append(resourcePars, filterList)
+	}
+	// 打印查询总数耗时
+	fmt.Printf("构建查询条件耗时: %v\n", time.Since(startTime))
+	total, err := models.GetCloudDiskResourcePageCount(resourceCond, resourcePars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取云盘文件列表失败, Err:" + err.Error()
+		return
+	}
+	fmt.Printf("查询数据库耗时1: %v\n", time.Since(startTime))
+	resp := models.CloudDiskMenuResourcePageResp{}
+	var resourceList []*models.CloudDiskResourcePageItem
+	if total > 0 {
+		resourceList, err = models.GetCloudDiskResourcePageList(resourceCond, resourcePars, orderRule, startSize, pageSize, menuNameMap)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取云盘文件列表失败, Err:" + err.Error()
+			return
+		}
+		resp.List = resourceList
+	}
+	// 打印查询总数耗时
+	fmt.Printf("查询数据库耗时2: %v\n", time.Since(startTime))
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp.List = resourceList
+	resp.Paging = page
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// BatchMove 批量移动
+// @Title 批量移动
+// @Description 批量移动
+// @Param	request	body models.CloudDiskBatchDeleteReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /batch_move [post]
+func (this *CloudDiskController) BatchMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.CloudDiskBatchMoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	if req.ResourceIds == "" && !req.MoveAll {
+		br.Msg = "请选择文件"
+		br.ErrMsg = "请选择文件"
+		return
+	}
+	if req.ResourceIds != "" && req.MoveAll {
+		br.Msg = "请求参数冲突"
+		br.ErrMsg = "请求参数冲突,Err:不能同时指定文件资源ID和全选移动"
+		return
+	}
+	if req.TargetMenuId <= 0 {
+		br.Msg = "请选择目标目录"
+		br.ErrMsg = "请选择目标目录"
+		return
+	}
+
+	// 权限校验-仅管理员可操作
+	ok, e := services.CheckCloudDiskManagerAuth(this.SysUser.RoleTypeCode)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作权限校验失败, ErrMsg: " + e.Error()
+		return
+	}
+	if !ok {
+		br.Msg = "无权操作"
+		return
+	}
+	var menuStrIds []string
+	var menuIds []int
+	var ResourceStrIds []string
+	var ResourceIds []int
+	var resourceCond string
+	resourcePars := make([]interface{}, 0)
+
+	targetCond := ` AND menu_id = ?`
+	targetPars := make([]interface{}, 0)
+	targetPars = append(targetPars, req.TargetMenuId)
+	//重名校验
+	if req.MoveAll {
+		if req.MenuIds != "" {
+			menuStrIds = strings.Split(req.MenuIds, ",")
+			for i := range menuStrIds {
+				menuId, err := strconv.Atoi(menuStrIds[i])
+				if err != nil {
+					br.Msg = "参数有误"
+					br.ErrMsg = "参数有误, Err:目录ID包含非数字类型字符"
+					return
+				}
+				menuIds = append(menuIds, menuId)
+			}
+			// 获取要移动的文件信息
+			resourceCond = ` AND menu_id IN (` + utils.GetOrmInReplace(len(menuIds)) + `) `
+			resourcePars = append(resourcePars, menuIds)
+		} else {
+			menuItem := new(models.CloudDiskMenu)
+			menuCond := `And menu_id != ?`
+			menuPars := make([]interface{}, 0)
+			menuPars = append(menuPars, req.TargetMenuId)
+			menus, err := menuItem.GetItemsByCondition(menuCond, menuPars, nil, "")
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取移动目录列表失败, Err:" + err.Error()
+				return
+			}
+			for i := range menus {
+				menuIds = append(menuIds, menus[i].MenuId)
+			}
+			// 获取要移动的文件信息
+			resourceCond = ` AND menu_id  != ?`
+			resourcePars = append(resourcePars, req.TargetMenuId)
+		}
+	} else {
+		if req.ResourceIds == "" {
+			br.Msg = "请选择文件"
+			br.ErrMsg = "请选择文件"
+			return
+		}
+		ResourceStrIds = strings.Split(req.ResourceIds, ",")
+		for i := range ResourceStrIds {
+			ResourceId, err := strconv.Atoi(ResourceStrIds[i])
+			if err != nil {
+				br.Msg = "参数有误"
+				br.ErrMsg = "参数有误, Err:文件ID包含非数字类型字符"
+				return
+			}
+			ResourceIds = append(ResourceIds, ResourceId)
+		}
+		// 获取要移动的文件信息
+		resourceCond = ` AND resource_id IN (` + utils.GetOrmInReplace(len(ResourceIds)) + `) and menu_id !=?`
+		resourcePars = append(resourcePars, ResourceIds)
+		resourcePars = append(resourcePars, req.TargetMenuId)
+	}
+	if req.KeyWord != "" {
+		resourceCond += ` AND resource_name LIKE ?`
+		resourcePars = append(resourcePars, utils.GetLikeKeyword(req.KeyWord))
+	}
+	//获取目标目录下文件名
+	targetResourceList, err := models.GetCloudDiskResourceList(targetCond, targetPars)
+	if err != nil {
+		br.Msg = "批量移动失败"
+		br.ErrMsg = "批量移动失败,获取目标目录下云盘文件列表失败, Err:" + err.Error()
+		return
+	}
+	// 构建目标目录下的文件名集合
+	targetFileNameSet := make(map[string]int)
+	for _, resource := range targetResourceList {
+		targetFileNameSet[resource.ResourceName] = 1
+	}
+	//获取需要移动的文件名
+	resourceList, err := models.GetCloudDiskResourceList(resourceCond, resourcePars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取云盘文件列表失败, Err:" + err.Error()
+		return
+	}
+
+	// 重名校验并生成新的文件名
+	// 构建目标目录下的文件名集合,并判断是否有同名文件
+	resourceFileNameSet := make(map[string]int)
+	//移动的文件id
+	var moveResourceIds []int
+	for _, resource := range resourceList {
+		if _, exists := targetFileNameSet[resource.ResourceName]; exists {
+			br.Msg = "批量移动失败"
+			br.ErrMsg = "批量移动失败,移动文件包含同名文件,文件名:" + resource.ResourceName
+			return
+		}
+		resourceFileNameSet[resource.ResourceName] = 1
+		moveResourceIds = append(moveResourceIds, resource.ResourceId)
+	}
+	// 重名校验并生成新的文件名
+	for resourceName, _ := range targetFileNameSet {
+		if _, exists := resourceFileNameSet[resourceName]; exists {
+			br.Msg = "批量移动失败"
+			br.ErrMsg = "批量移动失败,目标目录包含同名文件,文件名:" + resourceName
+			return
+		}
+
+	}
+	err = models.BatchMoveCloudDiskResource(req.TargetMenuId, moveResourceIds)
+	if err != nil {
+		br.Msg = "批量移动失败"
+		br.ErrMsg = "批量移动失败, Err:" + err.Error()
+		return
+	}
+	// 更新目录大小
+	for menuId := range menuIds {
+		go func(menuId int) {
+			if menuId <= 0 || menuId == req.TargetMenuId {
+				return
+			}
+			if e = services.UpdateCloudDiskMenuSize(menuId); e != nil {
+				alarm_msg.SendAlarmMsg("更新批量移动云盘目录大小失败, Err: "+e.Error(), 3)
+			}
+		}(menuId)
+	}
+	go func() {
+		if e = services.UpdateCloudDiskMenuSize(req.TargetMenuId); e != nil {
+			alarm_msg.SendAlarmMsg("更新批量移动目标云盘目录大小失败, Err: "+e.Error(), 3)
+		}
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 14 - 2
models/cloud_disk_menu.go

@@ -1,9 +1,9 @@
 package models
 
 import (
+	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"eta/eta_api/utils"
 	"strings"
 	"time"
 )
@@ -19,6 +19,14 @@ type CloudDiskMenu struct {
 	ModifyTime time.Time `description:"修改时间"`
 }
 
+type MenuNode struct {
+	MenuId   int `json:"menuId"`
+	ParentId int `json:"parentId"`
+	MenuName string
+	Level    int
+	Children []*MenuNode
+}
+
 func (m *CloudDiskMenu) TableName() string {
 	return "cloud_disk_menu"
 }
@@ -79,7 +87,11 @@ type CloudDiskMenuRenameReq struct {
 	MenuId   int    `description:"目录ID"`
 	MenuName string `description:"目录名称"`
 }
-
+type CloudDiskMenuModifyReq struct {
+	MenuId   int    `description:"目录ID"`
+	ParentId int    `description:"父级ID"`
+	MenuName string `description:"目录名称"`
+}
 type CloudDiskMenuDeleteReq struct {
 	MenuId int `description:"目录ID"`
 }

+ 107 - 1
models/cloud_disk_resource.go

@@ -1,9 +1,10 @@
 package models
 
 import (
+	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
-	"eta/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"strings"
 	"time"
 )
@@ -102,6 +103,11 @@ type CloudDiskResourceRenameReq struct {
 	ResourceId   int    `description:"文件ID"`
 	ResourceName string `description:"文件名称"`
 }
+type CloudDiskResourceModifyReq struct {
+	ResourceId   int    `description:"文件ID"`
+	MenuId       int    `description:"目录ID"`
+	ResourceName string `description:"文件名称"`
+}
 
 // CloudDiskResourceDeleteReq 删除文件请求体
 type CloudDiskResourceDeleteReq struct {
@@ -149,6 +155,12 @@ type CloudDiskMenuResourcePath struct {
 	Selected bool   `description:"是否选中"`
 }
 
+// 分页查询响应结果
+type CloudDiskMenuResourcePageResp struct {
+	List   []*CloudDiskResourcePageItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
 // GetCloudDiskMenuAndResourceList UNION获取云盘列表数据
 func GetCloudDiskMenuAndResourceList(menuCond, resourceCond string, menuPars, resourcePars []interface{}, orderRule string) (items []*CloudDiskListItem, err error) {
 	o := orm.NewOrm()
@@ -183,6 +195,13 @@ type CloudDiskBatchDeleteReq struct {
 	MenuIds     string `description:"目录IDs"`
 	ResourceIds string `description:"文件IDs"`
 }
+type CloudDiskBatchMoveReq struct {
+	KeyWord      string `description:"关键字"`
+	ResourceIds  string `description:"文件IDs"`
+	MenuIds      string `description:"当前目录ID"`
+	MoveAll      bool   `description:"是否全选"`
+	TargetMenuId int    `description:"移动新目录ID"`
+}
 
 // BatchDeleteCloudDiskMenuAndResource 批量删除目录及文件
 func BatchDeleteCloudDiskMenuAndResource(menuIds, resourceIds []int) (err error) {
@@ -223,3 +242,90 @@ func BatchDeleteCloudDiskMenuAndResource(menuIds, resourceIds []int) (err error)
 	}
 	return
 }
+
+func GetCloudDiskResourcePageCount(resourceCond string, resourcePars []interface{}) (total int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM cloud_disk_resource WHERE 1=1 %s `, resourceCond)
+	err = o.Raw(sql, resourcePars).QueryRow(&total)
+	return
+}
+func GetCloudDiskResourcePageList(resourceCond string, resourcePars []interface{}, orderRule string, startSize, pageSize int, menuNameMap map[int]string) (resourceList []*CloudDiskResourcePageItem, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT resource_id , resource_name, resource_suffix, menu_id, size, admin_name, create_time FROM cloud_disk_resource WHERE 1=1 %s order by  %s LIMIT ?,?`, resourceCond, orderRule)
+	var ormList []*CloudDiskResource
+	_, err = o.Raw(sql, resourcePars, startSize, pageSize).QueryRows(&ormList)
+	if err != nil {
+		return
+	}
+	resourceList = toCloudDiskResourcePageList(ormList, menuNameMap)
+	return
+}
+
+type CloudDiskResourcePageItem struct {
+	ResourceId     int
+	ResourceName   string
+	ResourceSuffix string
+	MenuId         int
+	MenuPath       string
+	AdminName      string
+	CreateTime     string
+	Size           int64
+}
+
+func (this *CloudDiskResource) toPageItem(menuNameMap map[int]string) *CloudDiskResourcePageItem {
+	return &CloudDiskResourcePageItem{
+		ResourceId:     this.ResourceId,
+		ResourceName:   this.ResourceName,
+		MenuId:         this.MenuId,
+		ResourceSuffix: this.ResourceSuffix,
+		MenuPath:       menuNameMap[this.MenuId],
+		AdminName:      this.AdminName,
+		CreateTime:     this.CreateTime.Format(utils.FormatDateTime),
+		Size:           this.Size,
+	}
+}
+func (this *CloudDiskResource) toItem() *CloudDiskResourcePageItem {
+	return &CloudDiskResourcePageItem{
+		ResourceId:     this.ResourceId,
+		ResourceName:   this.ResourceName,
+		MenuId:         this.MenuId,
+		ResourceSuffix: this.ResourceSuffix,
+		AdminName:      this.AdminName,
+		CreateTime:     this.CreateTime.Format(utils.FormatDateTime),
+		Size:           this.Size,
+	}
+}
+func toCloudDiskResourcePageList(items []*CloudDiskResource, menuNameMap map[int]string) (resourceList []*CloudDiskResourcePageItem) {
+	for _, item := range items {
+		resourceList = append(resourceList, item.toPageItem(menuNameMap))
+	}
+	return
+}
+func toCloudDiskResourceList(items []*CloudDiskResource) (resourceList []*CloudDiskResourcePageItem) {
+	for _, item := range items {
+		resourceList = append(resourceList, item.toItem())
+	}
+	return
+}
+func GetCloudDiskResourceList(resourceCond string, resourcePars []interface{}) (resourceList []*CloudDiskResourcePageItem, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT resource_id , resource_name, resource_suffix, menu_id, size, admin_name, create_time FROM cloud_disk_resource WHERE 1=1 %s `, resourceCond)
+	var ormList []*CloudDiskResource
+	_, err = o.Raw(sql, resourcePars).QueryRows(&ormList)
+	if err != nil {
+		return
+	}
+	resourceList = toCloudDiskResourceList(ormList)
+	return
+}
+
+func BatchMoveCloudDiskResource(menuId int, resourceIds []int) (err error) {
+	resourceLens := len(resourceIds)
+	if resourceLens == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `UPDATE cloud_disk_resource SET menu_id = ?  WHERE resource_id IN (` + utils.GetOrmInReplace(resourceLens) + `)`
+	_, err = o.Raw(sql, menuId, resourceIds).Exec()
+	return
+}

+ 36 - 0
routers/commentsRouter.go

@@ -10366,6 +10366,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
+        beego.ControllerComments{
+            Method: "BatchMove",
+            Router: `/batch_move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
         beego.ControllerComments{
             Method: "List",
@@ -10402,6 +10411,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
+        beego.ControllerComments{
+            Method: "MenuModify",
+            Router: `/menu/modify`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
         beego.ControllerComments{
             Method: "MenuRename",
@@ -10429,6 +10447,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
+        beego.ControllerComments{
+            Method: "Page",
+            Router: `/page`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
         beego.ControllerComments{
             Method: "CheckResourceRepeat",
@@ -10447,6 +10474,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
+        beego.ControllerComments{
+            Method: "ResourceModify",
+            Router: `/resource/modify`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:CloudDiskController"],
         beego.ControllerComments{
             Method: "ResourceRename",

+ 74 - 0
services/cloud_disk.go

@@ -103,6 +103,57 @@ func loadMenuTree(parentId int, menus []*models.CloudDiskMenu, parentItem *model
 	return
 }
 
+func BuildCloudDiskMenuTree() (root *models.MenuNode, menuNameMap map[int]string) {
+	menuOB := new(models.CloudDiskMenu)
+	menuCond := ``
+	menuPars := make([]interface{}, 0)
+	menuList, err := menuOB.GetItemsByCondition(menuCond, menuPars, []string{}, "create_time DESC")
+	if err != nil {
+		utils.FileLog.Error("获取目录列表失败, Err: %s", err.Error())
+		return nil, nil
+	}
+	if len(menuList) == 0 {
+		return
+	}
+	// 使用 map 存储每个节点的子节点列表
+	subMenuMap := make(map[int][]*models.CloudDiskMenu)
+	menuNameMap = make(map[int]string)
+	for _, menu := range menuList {
+		subMenuMap[menu.ParentId] = append(subMenuMap[menu.ParentId], menu)
+	}
+	root = new(models.MenuNode)
+	root.MenuName = "虚拟根目录"
+	buildMenuTree(10, 0, subMenuMap, root, menuNameMap)
+	return
+}
+
+func buildMenuTree(depth, current int, subMenuMap map[int][]*models.CloudDiskMenu, node *models.MenuNode, menuNameMap map[int]string) {
+	if current > depth {
+		return
+	}
+	children, exists := subMenuMap[node.MenuId]
+	if !exists {
+		return
+	}
+	for _, childMenu := range children {
+		var menuName string
+		if depth == 0 {
+			menuName = childMenu.MenuName
+		} else {
+			menuName = fmt.Sprintf("%s/%s", node.MenuName, childMenu.MenuName)
+		}
+		childNode := &models.MenuNode{
+			MenuId:   childMenu.MenuId,
+			MenuName: menuName,
+			ParentId: childMenu.ParentId,
+			Level:    node.Level + 1,
+		}
+		menuNameMap[childMenu.MenuId] = menuName
+		node.Children = append(node.Children, childNode)
+		buildMenuTree(depth, current+1, subMenuMap, childNode, menuNameMap)
+	}
+}
+
 // CheckFileNameFormFiles 校验文件名并返回可用名
 func CheckFileNameFormFiles(originName, fileName string, fileList []*models.CloudDiskResource, num int) (isRepeat bool, availableName string) {
 	if originName == "" || !strings.Contains(originName, ".") {
@@ -263,3 +314,26 @@ func FillMenuPath2File(originMenuId, menuId int, resourcePath string, menuMap ma
 	}
 	return resourcePath
 }
+
+func TraceTreeNode(menuId int, node []*models.MenuNode) (menuIds []int) {
+	for i := 0; i < len(node); i++ {
+		if node[i].MenuId == menuId {
+			menuIds = append(menuIds, collectChildMenuIds(node[i].Children)...)
+		}
+	}
+	return
+}
+
+// 辅助函数,用于递归收集子节点的ID
+func collectChildMenuIds(node []*models.MenuNode) (menuIds []int) {
+	if node == nil || len(node) == 0 {
+		return
+	}
+	for _, n := range node {
+		menuIds = append(menuIds, n.MenuId)
+		if len(n.Children) > 0 {
+			menuIds = append(menuIds, collectChildMenuIds(n.Children)...)
+		}
+	}
+	return
+}