xyxie 6 ماه پیش
والد
کامیت
7803333d6f
5فایلهای تغییر یافته به همراه1971 افزوده شده و 0 حذف شده
  1. 1184 0
      controllers/material/material.go
  2. 13 0
      models/db.go
  3. 321 0
      models/material/material.go
  4. 279 0
      models/material/material_classify.go
  5. 174 0
      services/material/material.go

+ 1184 - 0
controllers/material/material.go

@@ -0,0 +1,1184 @@
+package sandbox
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/material"
+	"eta/eta_api/services/data"
+	materialService "eta/eta_api/services/material"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+// versionSize 版本列表第一页数据约定是:3条
+var versionSize = 3
+
+// MaterialController 逻辑导图
+type MaterialController struct {
+	controllers.BaseAuthController
+}
+
+// MaterialClassifyItems
+// @Title 获取所有沙盘分类接口-包含沙盘
+// @Description 获取所有沙盘分类接口-包含沙盘
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /classify/list [get]
+func (this *MaterialController) MaterialClassifyItems() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	resp := new(material.MaterialClassifyListResp)
+	MaterialClassifyId, _ := this.GetInt("MaterialClassifyId")
+
+	isShowMe, _ := this.GetBool("IsShowMe")
+	if isShowMe {
+		errMsg, err := materialService.GetMaterialClassifyListForMe(*this.SysUser, resp, MaterialClassifyId)
+		if err != nil {
+			br.Msg = errMsg
+			br.ErrMsg = err.Error()
+			return
+		}
+		// 移除没有权限的图表
+
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		fmt.Println("source my classify")
+		return
+	}
+
+	rootList, err := material.GetMaterialClassifyAndInfoByParentId(MaterialClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := material.GetMaterialClassifyAndInfoByParentId(MaterialClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	//sandboxAll, err := material.GetMaterialItemsByClassifyId(MaterialClassifyId)
+	//if err != nil && err.Error() != utils.ErrNoRow() {
+	//	br.Msg = "获取失败"
+	//	br.ErrMsg = "获取数据失败,Err:" + err.Error()
+	//	return
+	//}
+
+	//sandListMap := make(map[int][]*material.MaterialClassifyItems)
+	//for _, v := range sandboxAll {
+	//	if _, ok := sandListMap[v.ClassifyId]; !ok {
+	//		list := make([]*material.MaterialClassifyItems, 0)
+	//		list = append(list, v)
+	//		sandListMap[v.ClassifyId] = list
+	//	} else {
+	//		sandListMap[v.ClassifyId] = append(sandListMap[v.ClassifyId], v)
+	//	}
+	//}
+
+	nodeAll := make([]*material.MaterialClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		materialService.MaterialClassifyItemsMakeTreeV2(this.SysUser, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	//newAll := materialService.MaterialItemsMakeTree(nodeAll, sandListMap, MaterialClassifyId)
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 新增沙盘分类
+// @Description 新增沙盘分类接口
+// @Param	request	body data_manage.AddChartClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /classify/add [post]
+func (this *MaterialController) AddMaterialClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req material.AddMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	count, err := material.GetMaterialClassifyCount(req.ClassifyName, req.ParentId)
+	if err != nil {
+		br.Msg = "判断名称是否已存在失败"
+		br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "分类名称已存在,请重新输入"
+		br.IsSendEmail = false
+		return
+	}
+	//获取该层级下最大的排序数
+	maxSort, err := material.GetMaterialClassifyMaxSort(req.ParentId)
+
+	classify := new(material.MaterialClassify)
+	classify.ParentId = req.ParentId
+	classify.ClassifyName = req.ClassifyName
+	classify.CreateTime = time.Now()
+	classify.ModifyTime = time.Now()
+	classify.SysUserId = this.SysUser.AdminId
+	classify.SysUserRealName = this.SysUser.RealName
+	classify.Level = req.Level + 1
+	classify.Sort = maxSort + 1
+
+	_, err = material.AddMaterialClassify(classify)
+	if err != nil {
+		br.Msg = "保存分类失败"
+		br.ErrMsg = "保存分类失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+}
+
+// @Title 修改沙盘分类
+// @Description 修改沙盘分类接口
+// @Param	request	body data_manage.EditChartClassifyReq true "type json string"
+// @Success 200 Ret=200 修改成功
+// @router /classify/edit [post]
+func (this *MaterialController) EditMaterialClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req material.EditMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	//item, err := material.GetMaterialClassifyById(req.ClassifyId)
+	//if err != nil {
+	//	br.Msg = "保存失败"
+	//	br.Msg = "获取分类信息失败,Err:" + err.Error()
+	//	return
+	//}
+
+	//count, err := material.GetMaterialClassifyCount(req.ClassifyName, item.ParentId)
+	//if err != nil {
+	//	br.Msg = "判断名称是否已存在失败"
+	//	br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+	//	return
+	//}
+	//if count > 0 {
+	//	br.Msg = "分类名称已存在,请重新输入"
+	//	br.IsSendEmail = false
+	//	return
+	//}
+
+	err = material.EditMaterialClassify(req.ClassifyId, req.ChartPermissionId, req.ClassifyName, req.ChartPermissionName)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+	ids, err := material.GetMaterialClassifySubcategories(req.ClassifyId)
+	if err != nil {
+		br.Msg = "查询子级分类id失败"
+		br.ErrMsg = "查询子级分类id失败,Err:" + err.Error()
+		return
+	}
+	err = material.UpdateMaterialClassifyChartPermissionById(req.ChartPermissionId, req.ChartPermissionName, ids)
+	if err != nil {
+		br.Msg = "修改子级分类错误"
+		br.ErrMsg = "修改子级分类错误,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// @Title 删除沙盘检测接口
+// @Description 删除沙盘检测接口
+// @Param	request	body data_manage.ChartClassifyDeleteCheckResp true "type json string"
+// @Success 200 Ret=200 检测成功
+// @router /classify/delete/check [post]
+func (this *MaterialController) DeleteMaterialClassifyCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req material.MaterialClassifyDeleteCheckReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+	var deleteStatus int
+	var tipsMsg string
+	//删除分类
+	if req.ClassifyId > 0 {
+		//判断沙盘分类下,是否含有沙盘
+		count, err := material.GetMaterialInfoCountByClassifyId(req.ClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有指标失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			deleteStatus = 1
+			tipsMsg = "该分类下关联沙盘不可删除"
+		}
+	}
+
+	if deleteStatus != 1 {
+		classifyCount, err := material.GetMaterialInfoCountByClassifyId(req.ClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有沙盘失败,Err:" + err.Error()
+			return
+		}
+		if classifyCount > 0 {
+			deleteStatus = 2
+			tipsMsg = "确认删除当前目录及包含的子目录吗"
+		}
+	}
+	if deleteStatus == 0 {
+		tipsMsg = "可删除,进行删除操作"
+	}
+
+	resp := new(material.MaterialClassifyDeleteCheckResp)
+	resp.DeleteStatus = deleteStatus
+	resp.TipsMsg = tipsMsg
+	br.Ret = 200
+	br.Msg = "检测成功"
+	br.Success = true
+	br.Data = resp
+}
+
+// @Title 删除沙盘分类/沙盘
+// @Description 删除沙盘分类/沙盘接口
+// @Param	request	body data_manage.DeleteChartClassifyReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /classify/delete [post]
+func (this *MaterialController) DeleteMaterialClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req material.DeleteMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId < 0 && req.MaterialId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	//删除分类
+	if req.ClassifyId > 0 && req.MaterialId == 0 {
+		//判断是否含有指标
+		count, err := material.GetMaterialInfoCountByClassifyId(req.MaterialId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			br.Msg = "该目录下存在关联指标,不可删除"
+			br.IsSendEmail = false
+			return
+		}
+
+		err = material.DeleteMaterialClassify(req.ClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	//删除沙盘
+	if req.MaterialId > 0 {
+		sandboxInfo, err := material.GetMaterialById(req.MaterialId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "沙盘已删除,请刷新页面"
+				br.ErrMsg = "指标不存在,Err:" + err.Error()
+				return
+			} else {
+				br.Msg = "删除失败"
+				br.ErrMsg = "删除失败,获取指标信息失败,Err:" + err.Error()
+				return
+			}
+		}
+		if sandboxInfo == nil {
+			br.Msg = "沙盘已删除,请刷新页面"
+			return
+		}
+		err = materialService.DeleteMaterial(req.MaterialId)
+		if err != nil {
+			br.Msg = err.Error()
+			return
+		}
+	}
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// ChartClassifyMove
+// @Title 沙盘分类移动接口
+// @Description 沙盘分类移动接口
+// @Success 200 {object} data_manage.MoveChartClassifyReq
+// @router /classify/move [post]
+func (this *MaterialController) ChartClassifyMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req material.MoveMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id小于等于0"
+		return
+	}
+	//判断分类是否存在
+	MaterialClassifyInfo, err := material.GetMaterialClassifyById(req.ClassifyId)
+	if err != nil {
+		br.Msg = "移动失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+
+	updateCol := make([]string, 0)
+
+	// 判断移动的是分类还是沙盘
+	if req.MaterialId > 0 {
+		//判断分类是否存在
+		count, _ := material.GetMaterialClassifyCountById(req.ClassifyId)
+		if count <= 0 {
+			br.Msg = "分类已被删除,不可移动,请刷新页面"
+			return
+		}
+
+		sandboxInfo, err := material.GetMaterialById(req.MaterialId)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取沙盘信息失败,Err:" + err.Error()
+			return
+		}
+
+		//如果改变了分类,那么移动该图表数据
+		// 11/22 ETA逻辑优化去除名称重复限制
+		if sandboxInfo.ClassifyId != req.ParentClassifyId {
+			////查询需要修改的分类下是否存在同一个图表名称
+			//tmpSandboxInfo, tmpErr := material.GetMaterialByClassifyIdAndName(req.ParentClassifyId, sandboxInfo.Name)
+			//if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+			//	br.Msg = "移动失败"
+			//	br.ErrMsg = "移动失败,Err:" + tmpErr.Error()
+			//	return
+			//}
+			//if tmpSandboxInfo != nil {
+			//	br.Msg = "移动失败,同一个分类下沙盘名称不允许重复"
+			//	br.ErrMsg = "移动失败,同一个分类下沙盘名称不允许重复"
+			//	return
+			//}
+			err = material.MoveSandbox(req.MaterialId, req.ParentClassifyId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "移动失败,Err:" + err.Error()
+				return
+			}
+		}
+
+		//移动排序
+		updateCol := make([]string, 0)
+		//如果有传入 上一个兄弟节点分类id
+		if req.PrevId > 0 {
+			if req.PrevType == 1 {
+				//上一个兄弟节点
+				prevClassify, err := material.GetMaterialClassifyById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是分类 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := material.GetMaterialClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+						if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == MaterialClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是分类 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := material.GetMaterialById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(分类)与下一个兄弟(沙盘)的排序权重是一致的,那么需要将下一个兄弟(沙盘)(以及下个兄弟(沙盘)的同样排序权重)的排序权重+2,自己变成上一个兄弟(分类)的排序权重+1
+						if prevClassify.Sort == nextChartInfo.Sort || prevClassify.Sort == sandboxInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟(沙盘)的排序权重正好是上个兄弟节点(分类)的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.ClassifyId, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					}
+
+				}
+
+				sandboxInfo.Sort = prevClassify.Sort + 1
+				sandboxInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+
+			} else {
+				prevSandbox, err := material.GetMaterialById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是沙盘 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := material.GetMaterialClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevmaterial.Sort == nextClassify.Sort || prevmaterial.Sort == MaterialClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevmaterial.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是沙盘 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := material.GetMaterialById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevmaterial.Sort == nextChartInfo.Sort || prevmaterial.Sort == sandboxInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevmaterial.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+							}
+						}
+					}
+
+				}
+
+				sandboxInfo.Sort = prevmaterial.Sort + 1
+				sandboxInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+			}
+
+		} else {
+			// prevId为0,也就是沙盘移到最前端
+			firstClassify, err := material.GetFirstSandboxByClassifyId(req.ClassifyId)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = material.UpdateMaterialSortByClassifyId(firstClassify.ClassifyId, 0, firstClassify.MaterialId-1, updateSortStr)
+			}
+
+			sandboxInfo.Sort = 0 //那就是排在第一位
+			sandboxInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = sandboxInfo.Update(updateCol)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "修改失败,Err:" + err.Error()
+				return
+			}
+		}
+	} else {
+		//移动的是分类
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if MaterialClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
+			parentChartClassifyInfo, err := material.GetMaterialClassifyById(req.ParentClassifyId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+				return
+			}
+			MaterialClassifyInfo.ParentId = parentChartClassifyInfo.ClassifyId
+			MaterialClassifyInfo.Level = parentChartClassifyInfo.Level + 1
+			MaterialClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+		} else if MaterialClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId == 0 {
+			//改为一级分类
+			MaterialClassifyInfo.ParentId = req.ParentClassifyId
+			MaterialClassifyInfo.Level = 1
+			MaterialClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+		}
+
+		//如果有传入 上一个兄弟节点分类id
+		if req.PrevId > 0 {
+			if req.PrevType == 1 {
+				//上一个节点是分类
+				//上一个兄弟节点
+				prevClassify, err := material.GetMaterialClassifyById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是分类 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := material.GetMaterialClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+						if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == MaterialClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是分类 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := material.GetMaterialById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(分类)与下一个兄弟(沙盘)的排序权重是一致的,那么需要将下一个兄弟(沙盘)(以及下个兄弟(沙盘)的同样排序权重)的排序权重+2,自己变成上一个兄弟(分类)的排序权重+1
+						if prevClassify.Sort == nextChartInfo.Sort || prevClassify.Sort == MaterialClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+						} else {
+							//如果下一个兄弟(沙盘)的排序权重正好是上个兄弟节点(分类)的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevClassify.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.ClassifyId, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+							}
+						}
+					}
+
+				}
+
+				MaterialClassifyInfo.Sort = prevClassify.Sort + 1
+				MaterialClassifyInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+
+			} else {
+				//上一个节点是沙盘
+				prevSandbox, err := material.GetMaterialById(req.PrevId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+					return
+				}
+
+				//如果是移动在两个兄弟节点之间
+				if req.NextId > 0 {
+					if req.NextType == 1 {
+						//上一个节点是沙盘 下一个节点是分类的情况
+						//下一个兄弟节点
+						nextClassify, err := material.GetMaterialClassifyById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevmaterial.Sort == nextClassify.Sort || prevmaterial.Sort == MaterialClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextClassify.Sort-prevmaterial.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+							}
+						}
+					} else {
+						//上一个节点是沙盘 下一个节点是沙盘的情况
+						//下一个兄弟节点
+						nextChartInfo, err := material.GetMaterialById(req.NextId)
+						if err != nil {
+							br.Msg = "移动失败"
+							br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+							return
+						}
+						//如果上一个兄弟(沙盘)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(沙盘)的排序权重+1
+						if prevmaterial.Sort == nextChartInfo.Sort || prevmaterial.Sort == MaterialClassifyInfo.Sort {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 2`
+							_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+						} else {
+							//如果下一个兄弟(分类)的排序权重正好是上个兄弟(沙盘)节点的下一层,那么需要再加一层了
+							if nextChartInfo.Sort-prevmaterial.Sort == 1 {
+								//变更兄弟节点的排序
+								updateSortStr := `sort + 1`
+								_ = material.UpdateMaterialClassifySortByParentId(prevmaterial.ClassifyId, 0, prevmaterial.Sort, updateSortStr)
+								_ = material.UpdateMaterialSortByClassifyId(prevmaterial.ClassifyId, prevmaterial.Sort, prevmaterial.MaterialId, updateSortStr)
+							}
+						}
+					}
+
+				}
+				MaterialClassifyInfo.Sort = prevmaterial.Sort + 1
+				MaterialClassifyInfo.ModifyTime = time.Now()
+				updateCol = append(updateCol, "Sort", "ModifyTime")
+
+			}
+
+		} else {
+			firstClassify, err := material.GetFirstMaterialClassifyByParentId(MaterialClassifyInfo.ParentId)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = material.UpdateMaterialClassifySortByParentId(firstClassify.ParentId, firstClassify.ClassifyId-1, 0, updateSortStr)
+			}
+
+			MaterialClassifyInfo.Sort = 0 //那就是排在第一位
+			MaterialClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = MaterialClassifyInfo.Update(updateCol)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "修改失败,Err:" + err.Error()
+				return
+			}
+			if req.ParentClassifyId > 0 {
+				ids, err := material.GetMaterialClassifySubcategories(req.ClassifyId)
+				if err != nil {
+					br.Msg = "查询子级分类id失败"
+					br.ErrMsg = "查询子级分类id失败,Err:" + err.Error()
+					return
+				}
+				parentChartClassifyInfo, err := material.GetMaterialClassifyById(req.ParentClassifyId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+					return
+				}
+				err = material.UpdateMaterialClassifyChartPermissionById(parentChartClassifyInfo.ChartPermissionId, parentChartClassifyInfo.ChartPermissionName, ids)
+				if err != nil {
+					br.Msg = "修改子级分类错误"
+					br.ErrMsg = "修改子级分类错误,Err:" + err.Error()
+					return
+				}
+			}
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}
+
+// @Title ETA图表列表接口
+// @Description ETA图表列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ChartClassifyId   query   int  true       "分类id"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartListResp
+// @router /list [get]
+func (this *MaterialController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	MaterialClassifyId, _ := this.GetInt("MaterialClassifyId")
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyWord := this.GetString("KeyWord")
+
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if MaterialClassifyId > 0 {
+		MaterialClassifyId, err := material.GetMaterialClassify(MaterialClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取图表信息失败"
+			br.ErrMsg = "获取信息失败,GetChartClassify,Err:" + err.Error()
+			return
+		}
+		condition += " AND sandbox_classify_id IN(" + MaterialClassifyId + ") "
+		//pars = append(pars, chartClassifyId)
+	}
+	if keyWord != "" {
+		condition += ` AND  ( name LIKE '%` + keyWord + `%' )`
+	}
+
+	//只看我的
+	isShowMe, _ := this.GetBool("IsShowMe")
+	if isShowMe {
+		condition += ` AND sys_user_id = ? `
+		pars = append(pars, sysUser.AdminId)
+	}
+
+	//获取图表信息
+	condition += ` AND is_delete = 0 `
+	list, err := material.GetMaterialListByCondition(condition, pars, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Success = true
+		br.Msg = "获取沙盘信息失败"
+		br.ErrMsg = "获取沙盘信息失败,Err:" + err.Error()
+		return
+	}
+
+	for i, v := range list {
+		ids, err := material.GetMaterialAllParentByClassifyId(v.ClassifyId)
+		if err != nil {
+			br.Msg = "获取父级信息错误!"
+			br.ErrMsg = "获取父级信息错误,Err:" + err.Error()
+			return
+		}
+		list[i].ParentIds = ids
+	}
+	resp := new(material.MaterialListResp)
+	if list == nil || len(list) <= 0 || (err != nil && err.Error() == utils.ErrNoRow()) {
+		items := make([]*material.MaterialListItems, 0)
+		resp.Paging = page
+		resp.List = items
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	dataCount, err := material.GetMaterialListCountByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标数据总数失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, dataCount)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Save
+// @Title 新增/编辑保存沙盘
+// @Description 新增/编辑保存沙盘接口
+// @Param	request	body material.AddAndEditSandbox true "type json string"
+// @Success 200 {object} material.Material
+// @router /save [post]
+func (this *MaterialController) Save() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.AddAndEditMaterial
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	var sandboxResp *material.MaterialSaveResp
+
+	var errMsg string
+
+	var sandBoxData *material.Material
+	if req.MaterialId <= 0 {
+		//新增沙盘
+		sandboxResp, err = materialService.AddMaterial(req, sysUser.AdminId, sysUser.RealName)
+		if err != nil {
+			br.Msg = "保存失败!"
+			if errMsg != `` {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+		sandBoxData = sandboxResp.Material
+	} else {
+		//编辑沙盘
+		sandboxInfo := &material.Material{
+			SandboxId:          req.MaterialId,
+			Name:               utils.TrimStr(req.Name),
+			Content:            req.Content,
+			MindmapData:        req.MindmapData,
+			PicUrl:             utils.TrimStr(req.PicUrl),
+			ModifyTime:         time.Now(),
+			MaterialClassifyId: req.ClassifyId,
+			Style:              req.Style,
+		}
+		//缩略图为空时不更新
+		var updateSandboxColumn = []string{}
+		if req.PicUrl == "" {
+			updateSandboxColumn = []string{"Name", "Content", "MindmapData", "ModifyTime", "MaterialClassifyId", "Style"}
+		} else {
+			updateSandboxColumn = []string{"Name", "Content", "MindmapData", "PicUrl", "ModifyTime", "MaterialClassifyId", "Style"}
+		}
+		err = sandboxInfo.Update(updateSandboxColumn)
+		if err != nil {
+			br.Msg = "保存失败!"
+			if errMsg != `` {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+		sandBoxData = sandboxInfo
+	}
+
+	//解析逻辑图的指标
+	_ = data.SaveSandBoxEdbInfoRelation(sandBoxData.MaterialId, sandBoxData.Content)
+
+	msg := "保存成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+	br.Data = sandboxResp
+}
+
+// Delete
+// @Title 删除沙盘
+// @Description 删除沙盘接口
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /delete [post]
+func (this *MaterialController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.DeleteMaterial
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.MaterialId <= 0 {
+		br.Msg = "缺少沙盘编号"
+		return
+	}
+
+	//删除沙盘
+	err = materialService.DeleteSandbox(req.MaterialId)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+
+	msg := "删除成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// GetMaterialVersionDetail
+// @Title 获取沙盘版本数据详情(已保存的)
+// @Description 获取沙盘版本数据详情接口(已保存的)
+// @Param   SandboxVersionCode   query   string  true       "沙盘版本code"
+// @Success 200 {object} material.MaterialVersion
+// @router /detail [get]
+func (this *MaterialController) GetMaterialDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	sandboxId, _ := this.GetInt("SandboxId")
+	if sandboxId == 0 {
+		br.Msg = "缺少沙盘Id"
+		return
+	}
+
+	//获取沙盘数据详情(已保存的)
+	sandboxInfo, err := material.GetMaterialById(sandboxId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	msg := "获取成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+	br.Data = sandboxInfo
+}
+
+// MaterialClassifyItems
+// @Title 获取所有沙盘分类接口-不包含沙盘
+// @Description 获取所有沙盘分类接口-不包含沙盘
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /classifyList [get]
+func (this *MaterialController) MaterialClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	resp := new(material.MaterialClassifyListResp)
+
+	rootList, err := material.GetMaterialClassifyByParentId(0)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := material.GetMaterialClassifyAll()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	nodeAll := make([]*material.MaterialClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		materialService.MaterialClassifyItemsMakeTree(this.SysUser, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 13 - 0
models/db.go

@@ -15,6 +15,7 @@ import (
 	"eta/eta_api/models/data_stat"
 	"eta/eta_api/models/eta_trial"
 	"eta/eta_api/models/fe_calendar"
+	"eta/eta_api/models/material"
 	"eta/eta_api/models/ppt_english"
 	"eta/eta_api/models/report"
 	"eta/eta_api/models/report_approve"
@@ -204,6 +205,9 @@ func init() {
 	// 初始化因子指标系列
 	initFactorEdbSeries()
 
+	// 注册素材库表
+	initMaterial()
+
 	// 初始化部分数据表变量(直接init会有顺序问题=_=!)
 	afterInitTable()
 }
@@ -636,6 +640,15 @@ func initFactorEdbSeries() {
 	)
 }
 
+// initMaterial 注册素材库表
+func initMaterial() {
+	//注册对象
+	orm.RegisterModel(
+		new(material.Material),         //素材库表
+		new(material.MaterialClassify), //素材库分类表
+	)
+}
+
 // afterInitTable
 // @Description: 初始化表结构的的后置操作
 // @author: Roc

+ 321 - 0
models/material/material.go

@@ -0,0 +1,321 @@
+package material
+
+import (
+	"eta/eta_api/models/system"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type Material struct {
+	MaterialId   int       `orm:"column(material_id);pk" description:"素材id"`
+	MaterialName string    `description:"素材名称"`
+	ImgUrl       string    `description:"素材图片地址"`
+	SysUserId    int       `description:"作者id"`
+	SysUserName  string    `description:"作者名称"`
+	ModifyTime   time.Time `description:"修改时间"`
+	CreateTime   time.Time `description:"创建时间"`
+	ClassifyId   int       `description:"分类id"`
+	Sort         int       `description:"排序"`
+}
+
+// Update 素材字段变更
+func (material *Material) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(material, cols...)
+	return
+}
+
+// GetMaterialById 根据素材id获取素材详情
+func GetMaterialById(MaterialId int) (materialInfo *Material, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `select * FROM material where material_id = ? and is_delete = 0`
+	err = o.Raw(sql, MaterialId).QueryRow(&materialInfo)
+	return
+}
+
+// AddMultiMaterial 批量添加素材
+func AddMultiMaterial(materialInfoList []*Material) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(utils.MultiAddNum, materialInfoList)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// UpdateMaterial 更新素材
+func UpdateMaterial(materialInfo *Material, updateMaterialColumn []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	_, err = to.Update(materialInfo, updateMaterialColumn...)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// MaterialListItem 素材推演列表数据
+type MaterialListItem struct {
+	MaterialId   int    `description:"素材id"`
+	MaterialName string `description:"素材名称"`
+	ImgUrl       string `description:"素材图片地址"`
+	ModifyTime   string `description:"修改时间"`
+	CreateTime   string `description:"创建时间"`
+	SysUserId    int    `description:"作者id"`
+	SysUserName  string `description:"作者名称"`
+}
+
+// GetList 获取素材列表页
+func GetList(condition string, pars []interface{}, startSize, pageSize int) (total int, list []*Material, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := "select a.material_id,a.material_name,a.code,a.pic_url,a.sys_user_id,a.sys_user_name,a.modify_time,a.create_time FROM material as a where 1=1 AND a.is_delete = 0 "
+	sql += condition
+	sql += ` order by a.modify_time desc,a.material_id desc`
+
+	totalSql := `select count(1) total from (` + sql + `) z `
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+	sql += ` LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// MaterialSaveResp 保存素材响应体
+type MaterialSaveResp struct {
+	*Material
+}
+
+func GetMaterialAll() (list []*MaterialClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT material_id,classify_id,material_name AS classify_name, sort
+		FROM material `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// CheckOpMaterialPermission 判断素材操作权限
+func CheckOpMaterialPermission(sysUser *system.Admin, createUserId int) (ok bool) {
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN {
+		ok = true
+	}
+	// 如果图表创建人与当前操作人相同的话,那么就是允许操作
+	if ok == false && createUserId == sysUser.AdminId {
+		ok = true
+	}
+	// 如果图表权限id 是 1 ,那么允许编辑
+	if ok == false && sysUser.ChartPermission == 1 {
+		ok = true
+	}
+	return
+}
+
+// GetMaterialInfoByAdminId 获取所有我创建的素材,用于分类展示
+func GetMaterialInfoByAdminId(adminId int) (items []*MaterialClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT material_id,classify_id,material_name AS classify_name,code,
+             sys_user_id,sys_user_name
+            FROM material where sys_user_id = ? ORDER BY sort asc,create_time ASC `
+	_, err = o.Raw(sql, adminId).QueryRows(&items)
+	return
+}
+
+func GetMaterialClassify(classifyId int) (classify_id string, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT GROUP_CONCAT(t.classify_id) AS classify_id FROM (
+			SELECT a.classify_id FROM material_classify AS a 
+			WHERE a.classify_id=?
+			UNION ALL
+			SELECT a.classify_id FROM material_classify AS a 
+			WHERE a.parent_id=? UNION ALL
+	SELECT
+		classify_id 
+	FROM
+		material_classify 
+WHERE
+	parent_id IN ( SELECT classify_id FROM material_classify WHERE parent_id = ? )
+			)AS t`
+	err = o.Raw(sql, classifyId, classifyId, classifyId).QueryRow(&classify_id)
+	return
+}
+
+type MaterialListItems struct {
+	Material
+	ParentIds string
+}
+
+func GetMaterialListByCondition(condition string, pars []interface{}, startSize, pageSize int) (item []*MaterialListItems, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM material WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += " ORDER BY create_time DESC LIMIT ?,? "
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&item)
+	return
+}
+
+func GetMaterialListCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT COUNT(1) AS count FROM material WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type MaterialListResp struct {
+	Paging *paging.PagingItem
+	List   []*MaterialListItems
+}
+
+func AddMaterial(item *Material) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	lastId, err = o.Insert(item)
+	return
+}
+
+func GetMaterialItemsByClassifyId(classifyId int) (list []*MaterialClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT material_id,classify_id,material_name AS classify_name, sort
+		FROM material  WHERE classify_id = ? AND is_delete = 0  ORDER BY sort `
+	_, err = o.Raw(sql, classifyId).QueryRows(&list)
+	return
+}
+
+func GetMaterialAllParentByClassifyId(classifyId int) (ids string, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT
+	GROUP_CONCAT(DISTINCT m.classify_id  ORDER BY m.level) AS ids 
+FROM
+	(
+	SELECT
+		@id AS _id,(
+		SELECT
+			@id := parent_id 
+		FROM
+			material_classify 
+		WHERE
+			classify_id = _id 
+		) 
+	FROM
+		(
+		SELECT
+			@id :=(
+			SELECT
+				parent_id 
+			FROM
+				material_classify 
+			WHERE
+				classify_id = ? 
+			)) vm,
+		material_classify m 
+	WHERE
+		@id IS NOT NULL 
+	) vm
+	INNER JOIN material_classify m 
+WHERE
+	classify_id = vm._id `
+	err = o.Raw(sql, classifyId).QueryRow(&ids)
+	return
+}
+
+type MoveMaterialReq struct {
+	MaterialId     int `description:"素材ID"`
+	PrevMaterialId int `description:"上一个素材ID"`
+	NextMaterialId int `description:"下一个素材ID"`
+	ClassifyId     int `description:"分类id"`
+}
+
+func GetMaterialClassifyCountById(classifyId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT count(1) AS count FROM material_classify WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+// GetMaterialByClassifyIdAndName 根据分类id和素材名获取图表信息
+func GetMaterialByClassifyIdAndName(classifyId int, name string) (item *Material, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM material WHERE classify_id = ? and material_name=? `
+	err = o.Raw(sql, classifyId, name).QueryRow(&item)
+	return
+}
+
+// GetMaterialNameByIds 根据素材名称
+func GetMaterialNameByIds(ids []int) (items []*Material, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT material_id, material_name FROM material WHERE material_id in (` + utils.GetOrmInReplace(len(ids)) + `) `
+	_, err = o.Raw(sql, ids).QueryRows(&items)
+	return
+}
+
+func MoveMaterial(MaterialId, classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` UPDATE  material
+			SET
+			  classify_id = ?
+			WHERE material_id = ?`
+	_, err = o.Raw(sql, classifyId, MaterialId).Exec()
+	return
+}
+
+// UpdateMaterialSortByClassifyId 根据素材id更新排序
+func UpdateMaterialSortByClassifyId(classifyId, nowSort, prevMaterialId int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` UPDATE  material set sort = ` + updateSort + ` WHERE classify_id=?  AND `
+	if prevMaterialId > 0 {
+		sql += ` (sort > ? or (material_id > ` + fmt.Sprint(prevMaterialId) + ` and sort = ` + fmt.Sprint(nowSort) + `))`
+	}
+	_, err = o.Raw(sql, classifyId, nowSort).Exec()
+	return
+}
+
+// GetFirstMaterialByClassifyId 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstMaterialByClassifyId(classifyId int) (item *Material, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM material WHERE classify_id=? order by sort asc,material_id asc limit 1`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+type DetailParams struct {
+	Code       string `json:"code"`
+	Id         int    `json:"id"`
+	ClassifyId int    `json:"classifyId"`
+}
+
+// AddAndEditMaterial 添加/编辑素材的请求数据
+type AddAndEditMaterial struct {
+	MaterialId   int    `description:"素材id"`
+	MaterialName string `description:"素材名称"`
+	ImgUrl       string `description:"素材图片地址"`
+	ClassifyId   int    `description:"分类id"`
+}
+
+// DeleteMaterial 删除素材的请求数据
+type DeleteMaterial struct {
+	MaterialId int `description:"素材id"`
+}
+
+// RenameMaterialReq 添加/编辑素材的请求数据
+type RenameMaterialReq struct {
+	MaterialId   int    `description:"素材id"`
+	MaterialName string `description:"素材名称"`
+}

+ 279 - 0
models/material/material_classify.go

@@ -0,0 +1,279 @@
+package material
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type MaterialClassify struct {
+	ClassifyId      int       `orm:"column(classify_id);pk"`
+	ClassifyName    string    `description:"分类名称"`
+	ParentId        int       `description:"父级id"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"修改时间"`
+	SysUserId       int       `description:"创建人id"`
+	SysUserRealName string    `description:"创建人姓名"`
+	Level           int       `description:"层级"`
+	Sort            int       `description:"排序字段,越小越靠前,默认值:10"`
+}
+
+func AddMaterialClassify(item *MaterialClassify) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	lastId, err = o.Insert(item)
+	return
+}
+
+// GetMaterialClassifyByParentId
+func GetMaterialClassifyByParentId(parentId int) (items []*MaterialClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM material_classify WHERE parent_id=? order by sort asc,classify_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetMaterialClassifyAll
+func GetMaterialClassifyAll() (items []*MaterialClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM material_classify WHERE parent_id<>0 order by sort asc,classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type MaterialClassifyItems struct {
+	ClassifyId   int       `orm:"column(classify_id);pk"`
+	ClassifyName string    `description:"分类名称"`
+	ParentId     int       `description:"父级id"`
+	CreateTime   time.Time `description:"创建时间"`
+	ModifyTime   time.Time `description:"修改时间"`
+	SysUserId    int       `description:"创建人id"`
+	SysUserName  string    `description:"创建人姓名"`
+	Level        int       `description:"层级"`
+	Sort         int       `description:"排序字段,越小越靠前,默认值:10"`
+	Children     []*MaterialClassifyItems
+}
+
+type MaterialClassifyListResp struct {
+	AllNodes []*MaterialClassifyItems
+}
+
+type AddMaterialClassifyReq struct {
+	ClassifyName        string `description:"分类名称"`
+	ParentId            int    `description:"父级id,第一级传0"`
+	Level               int    `description:"层级,第一级传0,其余传上一级的层级"`
+	ChartPermissionId   int    `description:"品种id"`
+	ChartPermissionName string `description:"品种名称"`
+}
+
+func GetMaterialClassifyCount(ClassifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT COUNT(1) AS count FROM material_classify WHERE parent_id=? AND classify_name=? `
+	err = o.Raw(sql, parentId, ClassifyName).QueryRow(&count)
+	return
+}
+
+// GetMaterialClassifyMaxSort 获取沙盘分类下最大的排序数
+func GetMaterialClassifyMaxSort(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT Max(sort) AS sort FROM material_classify WHERE parent_id=? `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type EditMaterialClassifyReq struct {
+	ClassifyName        string `description:"分类名称"`
+	ClassifyId          int    `description:"分类id"`
+	ChartPermissionId   int    `description:"品种id"`
+	ChartPermissionName string `description:"品种名称"`
+}
+
+func GetMaterialClassifyById(classifyId int) (item *MaterialClassify, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM material_classify WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func EditMaterialClassify(classifyId, ChartPermissionId int, ClassifyName, ChartPermissionName string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE material_classify SET classify_name=?,chart_permission_id = ?, chart_permission_name = ?, modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, ClassifyName, ChartPermissionId, ChartPermissionName, classifyId).Exec()
+	return
+}
+
+type MaterialClassifyDeleteCheckReq struct {
+	ClassifyId int `description:"分类id"`
+}
+
+func GetSandboxInfoCountByClassifyId(classifyId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT COUNT(1) AS count FROM sandbox AS a
+				WHERE a.classify_id IN(
+				SELECT t.classify_id FROM 
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM material_classify WHERE parent_id IS NOT NULL) rd,
+					 (SELECT @pid := ?) pd 
+				WHERE FIND_IN_SET(parent_id, @pid) > 0 
+				  AND @pid := CONCAT(@pid, ',', classify_id) 
+				UNION SELECT * FROM material_classify WHERE classify_id = @pid 
+				)AS t
+				) AND a.is_delete = 0 `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+type MaterialClassifyDeleteCheckResp struct {
+	DeleteStatus int    `description:"检测状态:0:默认值,如果为0,继续走其他校验,1:该分类下关联图表不可删除,2:确认删除当前目录及包含的子目录吗"`
+	TipsMsg      string `description:"提示信息"`
+}
+
+type DeleteMaterialClassifyReq struct {
+	ClassifyId int `description:"分类id"`
+	MaterialId int
+	SandboxId  int `description:"指标id"`
+}
+
+func DeleteMaterialClassify(classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` DELETE FROM material_classify
+				WHERE classify_id IN(
+				SELECT t.classify_id FROM
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM material_classify WHERE parent_id IS NOT NULL) rd,
+				(SELECT @pid := ?) pd
+				WHERE FIND_IN_SET(parent_id, @pid) > 0
+				AND @pid := CONCAT(@pid, ',', classify_id)
+				UNION SELECT * FROM material_classify WHERE classify_id = @pid
+				)AS t
+				) `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+// MoveMaterialClassifyReq 移动沙盘分类请求参数
+type MoveMaterialClassifyReq struct {
+	ClassifyId       int `description:"分类id"`
+	SandboxId        int `description:"沙盘ID"`
+	ParentClassifyId int `description:"父级分类id 移动沙盘时为目标分类id"`
+	PrevId           int `description:"上一个兄弟节点分类id"`
+	NextId           int `description:"下一个兄弟节点分类id"`
+	PrevType         int `description:"上一个兄弟节点类型 1分类 2沙盘 "`
+	NextType         int `description:"上一个兄弟节点类型 1分类 2沙盘 "`
+}
+
+// UpdateMaterialClassifySortByParentId 根据沙盘父类id更新排序
+func UpdateMaterialClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` update material_classify set sort = ` + updateSort + ` WHERE parent_id=? and sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( classify_id > ` + fmt.Sprint(classifyId) + ` and sort= ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// GetFirstMaterialClassifyByParentId 获取当前父级沙盘分类下的排序第一条的数据
+func GetFirstMaterialClassifyByParentId(parentId int) (item *MaterialClassify, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM material_classify WHERE parent_id=? order by sort asc,classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+// Update 更新沙盘分类基础信息
+func (MaterialClassify *MaterialClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(MaterialClassify, cols...)
+	return
+}
+
+// GetMaterialClassifyAndInfoByParentId
+func GetMaterialClassifyAndInfoByParentId(parentId int) (items []*MaterialClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT
+	0 AS sandbox_id,
+	classify_id,
+	classify_name,
+	parent_id,
+	create_time,
+	modify_time,
+	sys_user_id,
+	sys_user_real_name AS sys_user_name,
+	sort,
+	level,
+	chart_permission_id,
+	chart_permission_name,
+	0 AS is_delete
+FROM
+	material_classify 
+WHERE
+	parent_id = ? UNION ALL
+SELECT
+	sandbox_id,
+	classify_id,
+	name AS classify_name,
+	0 AS parent_id,
+	create_time,
+	modify_time,
+	sys_user_id,
+	sys_user_name,
+	sort,
+	0 AS level,
+	chart_permission_id,
+	chart_permission_name,
+	is_delete 
+FROM
+	sandbox 
+WHERE
+	classify_id = ? AND is_delete = 0
+ORDER BY
+	sort ASC,
+	classify_id ASC`
+	_, err = o.Raw(sql, parentId, parentId).QueryRows(&items)
+	return
+}
+
+type SandboxLinkCheckReq struct {
+	EdbInfoIdList   []int `description:"指标id列表"`
+	ChartInfoIdList []int `description:"图库id列表"`
+	ReportIdList    []int `description:"报告id列表"`
+}
+
+type SandboxLinkCheckItem struct {
+	Id         int    `description:"id"`
+	Name       string `description:"名称"`
+	UniqueCode string `description:"唯一编码"`
+	ClassifyId int    `description:"分类id"`
+}
+
+type SandboxLinkCheckResp struct {
+	EdbInfoIdList   []*SandboxLinkCheckItem `description:"指标id列表"`
+	ChartInfoIdList []*SandboxLinkCheckItem `description:"图库id列表"`
+	ReportIdList    []*SandboxLinkCheckItem `description:"报告id列表"`
+}
+
+// 获取所有子级分类id
+func GetMaterialClassifySubcategories(classifyId int) (Ids string, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT GROUP_CONCAT(classify_id) AS ids
+FROM (
+SELECT @pv := ? AS classify_id
+UNION ALL
+SELECT sc.classify_id
+FROM material_classify sc
+JOIN (SELECT @pv := ?) initial
+WHERE sc.parent_id = @pv
+) subcategories; `
+	err = o.Raw(sql, classifyId, classifyId).QueryRow(&Ids)
+	return
+}
+
+// UpdateMaterialClassifyChartPermissionById 根据沙盘id更新品种
+func UpdateMaterialClassifyChartPermissionById(ChartPermissionId int, ChartPermissionName, Ids string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` UPDATE material_classify SET chart_permission_id = ?, chart_permission_name = ? WHERE classify_id IN ( ` + Ids + ` ) `
+	_, err = o.Raw(sql, ChartPermissionId, ChartPermissionName).Exec()
+	return
+}

+ 174 - 0
services/material/material.go

@@ -0,0 +1,174 @@
+package sandbox
+
+import (
+	"eta/eta_api/models/material"
+	"eta/eta_api/models/system"
+	"eta/eta_api/utils"
+	"time"
+)
+
+func materialClassifyHaveChild(allNode []*material.MaterialClassifyItems, node *material.MaterialClassifyItems) (childs []*material.MaterialClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.ClassifyId {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}
+
+func MaterialClassifyItemsMakeTree(sysUser *system.Admin, allNode []*material.MaterialClassifyItems, node *material.MaterialClassifyItems) {
+
+	childs, _ := materialClassifyHaveChild(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {                           //查询子节点的子节点,并添加到子节点
+			_, has := materialClassifyHaveChild(allNode, v)
+			if has {
+				MaterialClassifyItemsMakeTree(sysUser, allNode, v) //递归添加节点
+			} else {
+				childrenArr := make([]*material.MaterialClassifyItems, 0)
+				v.Children = childrenArr
+			}
+		}
+	} else {
+		childrenArr := make([]*material.MaterialClassifyItems, 0)
+		node.Children = childrenArr
+	}
+}
+
+// GetMaterialClassifyListForMe 获取我创建的沙盘
+func GetMaterialClassifyListForMe(adminInfo system.Admin, resp *material.MaterialClassifyListResp, classifyId int) (errMsg string, err error) {
+	rootList, err := material.GetMaterialClassifyByParentId(classifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	classifyAll, err := material.GetMaterialClassifyByParentId(classifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	sandboxAll, err := material.GetMaterialInfoByAdminId(adminInfo.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	sandListMap := make(map[int][]*material.MaterialClassifyItems)
+	for _, v := range sandboxAll {
+		if _, ok := sandListMap[v.ClassifyId]; !ok {
+			list := make([]*material.MaterialClassifyItems, 0)
+			list = append(list, v)
+			sandListMap[v.ClassifyId] = list
+		} else {
+			sandListMap[v.ClassifyId] = append(sandListMap[v.ClassifyId], v)
+		}
+	}
+
+	nodeAll := make([]*material.MaterialClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		MaterialClassifyItemsMakeTree(&adminInfo, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+	//for k := range nodeAll {
+	//
+	//}
+	newAll := MaterialItemsMakeTree(nodeAll, sandListMap, classifyId)
+	resp.AllNodes = newAll
+
+	return
+}
+
+// AddMaterial 新增沙盘
+func AddMaterial(req material.AddAndEditMaterial, opUserId int, opUserName string) (resp *material.MaterialSaveResp, err error) {
+	resp = new(material.MaterialSaveResp)
+	//沙盘主表信息
+	materialInfo := &material.Material{
+		MaterialName: utils.TrimStr(req.MaterialName),
+		ImgUrl:       utils.TrimStr(req.ImgUrl),
+		SysUserId:    opUserId,
+		SysUserName:  opUserName,
+		ModifyTime:   time.Now(),
+		CreateTime:   time.Now(),
+		ClassifyId:   req.ClassifyId,
+		Sort:         0,
+	}
+
+	//新增沙盘
+	id, err := material.AddMaterial(materialInfo)
+	if err != nil {
+		return
+	}
+	materialInfo.MaterialId = int(id)
+	resp.Material = materialInfo
+	return
+}
+
+func MaterialItemsMakeTree(allNode []*material.MaterialClassifyItems, sandListMap map[int][]*material.MaterialClassifyItems, classifyId int) (nodeAll []*material.MaterialClassifyItems) {
+	for k := range allNode {
+		if len(allNode[k].Children) > 0 {
+			MaterialItemsMakeTree(allNode[k].Children, sandListMap, classifyId)
+			allNode = append(allNode, sandListMap[allNode[k].ParentId]...)
+			nodeAll = allNode
+		} else if k == len(allNode)-1 {
+			allNode = append(allNode, sandListMap[allNode[k].ParentId]...)
+			nodeAll = allNode
+		}
+	}
+	if len(allNode) == 0 {
+		nodeAll = append(nodeAll, sandListMap[classifyId]...)
+	}
+	return
+}
+
+func MaterialClassifyHaveChild(allNode []*material.MaterialClassifyItems, node *material.MaterialClassifyItems) (childs []*material.MaterialClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.ClassifyId {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}
+
+func MaterialClassifyItemsMakeTreeV2(sysUser *system.Admin, allNode []*material.MaterialClassifyItems, node *material.MaterialClassifyItems) {
+
+	childs, _ := materialClassifyHaveChildV2(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {                           //查询子节点的子节点,并添加到子节点
+			_, has := materialClassifyHaveChildV2(allNode, v)
+			if has {
+				MaterialClassifyItemsMakeTreeV2(sysUser, allNode, v) //递归添加节点
+			} else {
+				//childrenArr := make([]*material.MaterialClassifyItems, 0)
+				//v.Children = childrenArr
+			}
+		}
+	} else {
+		//childrenArr := make([]*material.MaterialClassifyItems, 0)
+		//node.Children = childrenArr
+	}
+}
+
+func materialClassifyHaveChildV2(allNode []*material.MaterialClassifyItems, node *material.MaterialClassifyItems) (childs []*material.MaterialClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.ClassifyId && node.ClassifyId == 0 {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}