Browse Source

Merge branch 'feature/pool276_nh_edb_chart' of eta_server/eta_hub into master

xyxie 4 months ago
parent
commit
aa36a9bfc7

+ 168 - 0
controllers/edb.go

@@ -366,3 +366,171 @@ func (this *EdbInfoController) TraceEdbInfo() {
 	br.Data = resp
 	br.Msg = "刷新成功"
 }
+
+// EdbInfoDataTb
+// @Title 获取指标数据同比值
+// @Description 获取指标数据
+// @Param   EdbInfoId   query   int  true       "指标ID"
+// @Param   DateType   query   int  true       "日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:自定义起始时间至今,7:18年至今,8:19年至今,9:20年至今,10:全部"
+// @Param   StartDate   query   string  true       "自定义开始日期"
+// @Param   EndDate   query   string  true       "自定义结束日期"
+// @Success 200 {object} data_manage.EdbInfoDataResp
+// @router /data/tb [get]
+func (this *EdbController) EdbInfoDataTb() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	edbInfoType, _ := this.GetInt("EdbInfoType")
+	if edbInfoType < 0 {
+		edbInfoType = 1
+	}
+
+	startDate := this.GetString("StartDate")
+	unicode := this.GetString("UniqueCode")
+	edbCode := this.GetString("EdbCode")
+	if unicode == "" && edbCode == "" {
+		br.Msg = "参数有误"
+		return
+	}
+	edbOb := new(data_manage.EdbInfo)
+	edbInfo := new(data_manage.EdbInfo)
+	var err error
+	if unicode != "" {
+		edbInfo, err = edbOb.GetItemByUniCode(unicode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "指标不存在"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "EdbDetail GetItemByUniCode err: " + err.Error()
+			return
+		}
+	} else if edbCode != "" {
+		edbInfo, err = edbOb.GetItemByEdbCode(edbCode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "指标不存在"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "EdbDetail GetItemByEdbCode err: " + err.Error()
+			return
+		}
+	}
+	if edbInfo.EdbInfoType != 0 {
+		br.Msg = "不支持预测指标"
+		return
+	}
+	var startDateTime time.Time
+	if startDate != `` {
+		startDateTime, err = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+		if err != nil {
+			br.Msg = "开始日期异常"
+			br.Msg = "开始日期异常,Err:" + err.Error()
+			return
+		}
+		//同比值的计算方式,是需要与去年同期对比,所以如果用户需要某个时间段的数据的话,获取基础数据时,需要往前面推(1年+兼容的日期频度(目前是35天))
+		startDate = startDateTime.AddDate(-1, 0, -35).Format(utils.FormatDate)
+	}
+	tmpDataList, err := data.GetEdbDataList(edbInfo, startDate)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.Msg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	// 计算同比值
+	dataList, _, _, err := data.GetEdbDataTbz(edbInfo, tmpDataList, startDateTime)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.Msg = "计算同比值失败,Err:" + err.Error()
+		return
+	}
+
+	//edbInfo.MaxValue = maxValue
+	//edbInfo.MinValue = minValue
+	resp := new(data_manage.EdbInfoDataResp)
+	//resp.EdbInfo = edbInfo
+	resp.DataList = dataList
+	br.Ret = 200
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EdbInfoDataSeasonal
+// @Title 获取指标数据(季节性)
+// @Description 获取指标数据
+// @Param   EdbInfoId   query   int  true       "指标ID"
+// @Param   DateType   query   int  true       "日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:自定义起始时间至今,7:18年至今,8:19年至今,9:20年至今,10:全部"
+// @Param   StartDate   query   string  true       "自定义开始日期"
+// @Param   EndDate   query   string  true       "自定义结束日期"
+// @Param   Calendar   query   string  true       "公历/农历"
+// @Success 200 {object} data_manage.EdbInfoDataResp
+// @router /data/seasonal [get]
+func (this *EdbController) EdbInfoDataSeasonal() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	startDate := this.GetString("StartDate")
+
+	calendar := this.GetString("Calendar")
+	if calendar == "" {
+		calendar = "公历"
+	}
+	unicode := this.GetString("UniqueCode")
+	edbCode := this.GetString("EdbCode")
+	if unicode == "" && edbCode == "" {
+		br.Msg = "参数有误"
+		return
+	}
+	edbOb := new(data_manage.EdbInfo)
+	edbInfo := new(data_manage.EdbInfo)
+	var err error
+	if unicode != "" {
+		edbInfo, err = edbOb.GetItemByUniCode(unicode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "指标不存在"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "EdbDetail GetItemByUniCode err: " + err.Error()
+			return
+		}
+	} else if edbCode != "" {
+		edbInfo, err = edbOb.GetItemByEdbCode(edbCode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "指标不存在"
+				return
+			}
+			br.Msg = "获取失败"
+			br.ErrMsg = "EdbDetail GetItemByEdbCode err: " + err.Error()
+			return
+		}
+	}
+	if edbInfo.EdbInfoType != 0 {
+		br.Msg = "不支持预测指标"
+		return
+	}
+
+	dataList, err := data.GetChartEdbSeasonalData(calendar, startDate, edbInfo)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	resp := new(data_manage.EdbInfoDataSeasonalResp)
+	//resp.EdbInfo = edbInfo
+	resp.DataList = dataList
+	br.Ret = 200
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 148 - 0
controllers/edb_refresh.go

@@ -0,0 +1,148 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_hub/models"
+	"eta/eta_hub/models/data_manage"
+	"eta/eta_hub/services/data"
+	"eta/eta_hub/utils"
+)
+
+// SaveRelationEdbRefreshStatus
+// @Title 批量设置被引用的指标刷新状态接口
+// @Description 批量设置被引用的指标刷新状态接口
+// @Param	request	body data_manage.SaveEdbRefreshStatusReq true "type json string"
+// @Success 200 {object} data_manage.RefreshBaseEdbInfoResp
+// @router /refresh/set [post]
+func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	var req data_manage.SaveRelationEdbRefreshStatusReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	condition := ``
+	pars := make([]interface{}, 0)
+
+	edbType := 1 //基础指标
+	switch req.Source {
+	case utils.DATA_SOURCE_THS, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, utils.DATA_SOURCE_WIND: // wind
+		condition += ` AND source = ? `
+		pars = append(pars, req.Source)
+	case 0:
+		req.Source = 0
+		edbType = 2
+		condition += ` AND edb_type = ? AND edb_info_type = 0`
+		pars = append(pars, edbType)
+	default:
+		br.Msg = "暂不支持设置其他来源的指标"
+		return
+	}
+	edbIdList := make([]int, 0)
+	edbCodeList := make([]string, 0)
+	edbIdList = req.EdbSelectIdList
+	if len(edbIdList) <= 0 {
+		br.Msg = "指标不能为空"
+		return
+	}
+	condition += ` AND edb_info_id IN (` + utils.GetOrmInReplace(len(edbIdList)) + `) `
+	pars = append(pars, edbIdList)
+	//查询指标信息
+	edbObj := new(data_manage.EdbInfo)
+	edbList, e := edbObj.GetItemsByCondition(condition, pars, []string{}, "")
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + e.Error()
+		return
+	}
+
+	if len(edbList) == 0 {
+		br.Msg = "未查到对应的指标"
+		return
+	}
+
+	// 如果是钢联化工,那么需要过滤供应商暂停的指标
+	if req.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+		// 获取未被供应商暂停的指标
+		tmpEdbCodeList := make([]string, 0)
+		for _, v := range edbList {
+			tmpEdbCodeList = append(tmpEdbCodeList, v.EdbCode)
+		}
+		notIsSupplierStopIndexList, e := data_manage.GetNotIsSupplierStopIndexByCodeList(tmpEdbCodeList, 0)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + e.Error()
+			return
+		}
+
+		// 未被供应商暂停的指标编码
+		notIsSupplierStopIndexCodeList := make([]string, 0)
+		for _, v := range notIsSupplierStopIndexList {
+			notIsSupplierStopIndexCodeList = append(notIsSupplierStopIndexCodeList, v.IndexCode)
+		}
+
+		//查询未被供应商暂停的指标信息
+		edbList, err = data_manage.GetEdbInfoByEdbCodeList(utils.DATA_SOURCE_MYSTEEL_CHEMICAL, notIsSupplierStopIndexCodeList)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+
+		if len(edbList) <= 0 {
+			br.Ret = 200
+			br.Msg = "保存成功"
+			return
+		}
+	}
+
+	fromEdbIdList := make([]int, 0)
+	for _, v := range edbList {
+		if req.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			edbCodeList = append(edbCodeList, v.EdbCode)
+		}
+		if v.EdbInfoType == 0 {
+			fromEdbIdList = append(fromEdbIdList, v.EdbInfoId)
+		}
+	}
+
+	isStop := 0
+	if req.ModifyStatus == `暂停` {
+		isStop = 1
+	}
+
+	// 查询计算指标ID
+	// 查询相关的计算指标
+	calculateEdbIdList := make([]int, 0)
+	if isStop == 1 {
+		hasFind := make(map[int]struct{})
+		calculateEdbIdList, err = data.GetCalculateEdbByFromEdbInfo(fromEdbIdList, calculateEdbIdList, hasFind)
+		if err != nil {
+			br.Msg = "查询计算指标信息失败"
+			br.ErrMsg = "查询计算指标信息失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	switch req.Source {
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL: // 钢联化工
+		err = data_manage.ModifyMysteelChemicalUpdateStatusByEdbInfoIds(edbIdList, isStop, edbCodeList, calculateEdbIdList)
+	default:
+		err = data_manage.EdbInfoUpdateStatusByEdbInfoId(edbIdList, isStop, calculateEdbIdList)
+	}
+	if err != nil {
+		br.Msg = `操作失败`
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "操作成功"
+}

+ 224 - 0
controllers/sandbox.go

@@ -0,0 +1,224 @@
+package controllers
+
+import (
+	"eta/eta_hub/models"
+	"eta/eta_hub/models/sandbox"
+	sandboxService "eta/eta_hub/services/sandbox"
+	"eta/eta_hub/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+// SandboxController 逻辑导图
+type SandboxController struct {
+	BaseAuthController
+}
+
+// SandboxClassifyItems
+// @Title 获取所有沙盘分类接口-包含沙盘
+// @Description 获取所有沙盘分类接口-包含沙盘
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /classify/list [get]
+func (this *SandboxController) SandboxClassifyItems() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	resp := new(sandbox.SandboxClassifyListResp)
+	sandboxClassifyId, _ := this.GetInt("SandboxClassifyId")
+
+	rootList, err := sandbox.GetSandboxClassifyAndInfoByParentId(sandboxClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := sandbox.GetSandboxClassifyAndInfoByParentId(sandboxClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	nodeAll := make([]*sandbox.SandboxClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		sandboxService.SandboxClassifyItemsMakeTreeV2(classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @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 *SandboxController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sandboxClassifyId, _ := this.GetInt("SandboxClassifyId")
+
+	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 sandboxClassifyId > 0 {
+		sandboxClassifyId, err := sandbox.GetSandboxClassify(sandboxClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取图表信息失败"
+			br.ErrMsg = "获取信息失败,GetChartClassify,Err:" + err.Error()
+			return
+		}
+		condition += " AND sandbox_classify_id IN(" + sandboxClassifyId + ") "
+		//pars = append(pars, chartClassifyId)
+	}
+	if keyWord != "" {
+		condition += ` AND  ( name LIKE '%` + keyWord + `%' )`
+	}
+
+	//获取图表信息
+	condition += ` AND is_delete = 0 `
+	list, err := sandbox.GetSandboxListByCondition(condition, pars, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取沙盘信息失败"
+		br.ErrMsg = "获取沙盘信息失败,Err:" + err.Error()
+		return
+	}
+
+	for i, v := range list {
+		ids, err := sandbox.GetSandboxAllParentByClassifyId(v.SandboxClassifyId)
+		if err != nil {
+			br.Msg = "获取父级信息错误!"
+			br.ErrMsg = "获取父级信息错误,Err:" + err.Error()
+			return
+		}
+		list[i].ParentIds = ids
+	}
+	resp := new(sandbox.SandboxListResp)
+	if list == nil || len(list) <= 0 || (err != nil && err.Error() == utils.ErrNoRow()) {
+		items := make([]*sandbox.SandboxListItems, 0)
+		resp.Paging = page
+		resp.List = items
+		br.Ret = 200
+		br.Msg = "获取成功"
+		return
+	}
+
+	dataCount, err := sandbox.GetSandboxListCountByCondition(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.Msg = "获取成功"
+	br.Data = resp
+}
+
+// GetSandboxVersionDetail
+// @Title 获取沙盘版本数据详情(已保存的)
+// @Description 获取沙盘版本数据详情接口(已保存的)
+// @Param   SandboxVersionCode   query   string  true       "沙盘版本code"
+// @Success 200 {object} sandbox.SandboxVersion
+// @router /detail [get]
+func (this *SandboxController) GetSandboxDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sandboxId, _ := this.GetInt("SandboxId")
+	if sandboxId == 0 {
+		br.Msg = "缺少沙盘Id"
+		return
+	}
+
+	//获取沙盘数据详情(已保存的)
+	sandboxInfo, err := sandbox.GetSandboxById(sandboxId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	msg := "获取成功"
+	br.Ret = 200
+	br.Msg = msg
+	br.Data = sandboxInfo
+}
+
+// SandboxClassifyItems
+// @Title 获取所有沙盘分类接口-不包含沙盘
+// @Description 获取所有沙盘分类接口-不包含沙盘
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /classifyList [get]
+func (this *SandboxController) SandboxClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	resp := new(sandbox.SandboxClassifyListResp)
+
+	rootList, err := sandbox.GetSandboxClassifyByParentId(0)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := sandbox.GetSandboxClassifyAll()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	nodeAll := make([]*sandbox.SandboxClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		sandboxService.SandboxClassifyItemsMakeTree(classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 3 - 2
go.mod

@@ -12,11 +12,13 @@ require (
 	github.com/beego/beego/v2 v2.0.7
 	github.com/go-redis/redis/v8 v8.11.5
 	github.com/go-sql-driver/mysql v1.7.1
-	github.com/h2non/filetype v1.1.3
+	github.com/google/uuid v1.6.0
 	github.com/minio/minio-go/v7 v7.0.77
+	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.32
 	github.com/qiniu/qmgo v1.1.8
 	github.com/rdlucklib/rdluck_tools v1.0.3
+	github.com/shopspring/decimal v1.4.0
 	go.mongodb.org/mongo-driver v1.15.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 )
@@ -33,7 +35,6 @@ require (
 	github.com/goccy/go-json v0.10.3 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/golang/snappy v0.0.1 // indirect
-	github.com/google/uuid v1.6.0 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect

+ 4 - 2
go.sum

@@ -115,8 +115,6 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
-github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -178,6 +176,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6f
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19 h1:LhWT2dBuNkYexwRSsPpYh67e0ikmH1ebBDaVkGHoMts=
+github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19/go.mod h1:LjhyrWzOLJ9l1azMoNr9iCvfNrHEREqvJHzSLQcD0/o=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
@@ -230,6 +230,8 @@ github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
 github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
 github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
 github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=

+ 93 - 0
models/data_manage/base_from_mysteel_chemical_index.go

@@ -0,0 +1,93 @@
+package data_manage
+
+import (
+	"eta/eta_hub/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// BaseFromMysteelChemicalIndex 钢联化工指标表
+type BaseFromMysteelChemicalIndex struct {
+	BaseFromMysteelChemicalIndexId    int       `orm:"column(base_from_mysteel_chemical_index_id);pk"`
+	BaseFromMysteelChemicalClassifyId int       `orm:"column(base_from_mysteel_chemical_classify_id)" description:"钢联化工指标分类id"`
+	IndexCode                         string    `description:"指标编码"`
+	IndexName                         string    `description:"指标名称"`
+	Unit                              string    `description:"单位"`
+	Source                            string    `description:"数据来源"`
+	Frequency                         string    `description:"频度"`
+	StartDate                         string    `description:"开始日期"`
+	EndDate                           string    `description:"结束日期"`
+	Describe                          string    `description:"指标描述"`
+	UpdateWeek                        string    `description:"更新周期"`
+	UpdateTime                        string    `description:"更新时间1"`
+	UpdateTime2                       string    `description:"更新时间2"`
+	SysUserId                         int       `description:"创建人id"`
+	SysUserRealName                   string    `description:"创建人姓名"`
+	ModifyTime                        time.Time `description:"修改时间"`
+	CreateTime                        time.Time `description:"创建时间"`
+	Sort                              int       `description:"排序字段"`
+	MergeFilePath                     string    `description:"合并文件"`
+	TerminalCode                      string    `description:"终端编码"`
+	IsStop                            int       `description:"是否停更:1:停更,0:未停更"`
+	EndValue                          float64   `description:"指标的最新值"`
+	IsSupplierStop                    int       `description:"是否供应商停更:1:停更,0:未停更"`
+}
+
+// GetNotIsSupplierStopIndexByCodeList
+// @Description: 获取未被供应商停更的指标
+// @author: Roc
+// @datetime 2024-08-28 18:15:03
+// @param codeList []string
+// @param isStop int
+// @return items []*BaseFromMysteelChemicalIndex
+// @return err error
+func GetNotIsSupplierStopIndexByCodeList(codeList []string, isStop int) (items []*BaseFromMysteelChemicalIndex, err error) {
+	num := len(codeList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_mysteel_chemical_index WHERE is_supplier_stop = ? AND index_code in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, isStop, codeList).QueryRows(&items)
+
+	return
+}
+
+func ModifyMysteelChemicalUpdateStatusByEdbInfoIds(edbInfoIds []int, isStop int, edbCodes []string, calculateEdbInfoIds []int) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	// 更改数据源的更新状态
+	sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = ? WHERE index_code IN (` + utils.GetOrmInReplace(len(edbCodes)) + `) `
+	_, err = o.Raw(sql, isStop, edbCodes).Exec()
+	if err != nil {
+		return
+	}
+
+	// 更改指标的更新状态
+	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, edbInfoIds).Exec()
+	if err != nil {
+		return
+	}
+	if len(calculateEdbInfoIds) > 0 {
+		// 批量更新相关联的指标ID
+		sql = ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 27 - 27
models/data_manage/chart_info.go

@@ -442,35 +442,35 @@ type ChartViewButton struct {
 //	Value string
 //}
 
-//type QuarterData struct {
-//	Year                 int
-//	DataList             []*EdbDataList
-//	CuttingDataTimestamp int64 `description:"切割的时间戳"`
-//	ChartLegend          string
-//	Years                string
-//}
+type QuarterData struct {
+	Year                 int
+	DataList             []*EdbDataList
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
+	ChartLegend          string
+	Years                string
+}
 
-//type QuarterXDateItem struct {
-//	StartDate            time.Time
-//	EndDate              time.Time
-//	ShowName             string
-//	ChartLegend          string
-//	CuttingDataTimestamp int64 `description:"切割的时间戳"`
-//}
+type QuarterXDateItem struct {
+	StartDate            time.Time
+	EndDate              time.Time
+	ShowName             string
+	ChartLegend          string
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
+}
 
-//type QuarterDataList []*QuarterData
-//
-//func (m QuarterDataList) Len() int {
-//	return len(m)
-//}
-//
-//func (m QuarterDataList) Less(i, j int) bool {
-//	return m[i].Years < m[j].Years
-//}
-//
-//func (m QuarterDataList) Swap(i, j int) {
-//	m[i], m[j] = m[j], m[i]
-//}
+type QuarterDataList []*QuarterData
+
+func (m QuarterDataList) Len() int {
+	return len(m)
+}
+
+func (m QuarterDataList) Less(i, j int) bool {
+	return m[i].Years < m[j].Years
+}
+
+func (m QuarterDataList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}
 
 // ChartDetailApiResponse 图表详情接口响应体
 type ChartDetailApiResponse struct {

+ 34 - 0
models/data_manage/edb_data.go

@@ -338,3 +338,37 @@ func (m *EdbData) getThsHfEdbDataListByMongo(source, subSource int, edbCode, sta
 
 	return
 }
+
+// EdbInfoDataSeasonalResp 指标季节性数据返回
+type EdbInfoDataSeasonalResp struct {
+	//EdbInfo  *EdbInfo
+	DataList interface{}
+}
+
+type EdbInfoDataResp struct {
+	//EdbInfo  *EdbInfo
+	DataList []*EdbDataList
+}
+
+type EdbDataList struct {
+	//EdbDataId     int     `description:" 指标数据ID"`
+	//	EdbInfoId     int     `description:"指标ID"`
+	DataTime      string  //`json:"-" description:"数据日期"`
+	DataTimestamp int64   `description:"数据日期"`
+	UpdateTime    string  `description:"修改时间"`
+	Value         float64 `description:"数据值"`
+}
+
+func FormatEdbData2List(origin *EdbData) (item *EdbDataList) {
+	if origin == nil {
+		return
+	}
+	item = new(EdbDataList)
+	//item.EdbDataId = origin.EdbDataId
+	//item.EdbInfoId = origin.EdbInfoId
+	item.DataTime = utils.TimeTransferString(utils.FormatDate, origin.DataTime)
+	item.Value = origin.Value
+	item.DataTimestamp = origin.DataTimestamp
+	item.UpdateTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}

+ 197 - 0
models/data_manage/edb_data_quarter.go

@@ -0,0 +1,197 @@
+package data_manage
+
+import (
+	"eta/eta_hub/utils"
+	"fmt"
+	"github.com/nosixtools/solarlunar"
+	"strconv"
+	"time"
+)
+
+type EdbDataItems struct {
+	Items                []*EdbDataList
+	Year                 int
+	BetweenDay           int   `json:"-" description:"公历与农历之间相差的天数"`
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
+}
+type EdbDataResult struct {
+	List []*EdbDataItems
+}
+
+// 指标季度数据计算(公历转农历)
+func AddCalculateQuarterV4(dataList []*EdbDataList) (result *EdbDataResult, err error) {
+	var errMsg string
+	defer func() {
+		if errMsg != "" {
+			fmt.Println("errMsg:", errMsg)
+		}
+	}()
+
+	endDate := dataList[len(dataList)-1].DataTime
+	endDateForm, err := time.Parse(utils.FormatDate, endDate)
+	if err != nil {
+		return result, err
+	}
+	thisMonth := int(endDateForm.Month())
+
+	result = new(EdbDataResult)
+	var yearArr []int
+	yearMap := make(map[int]int)
+	var cureentDate time.Time
+	if thisMonth < 11 {
+		for k, v := range dataList {
+			dateTime, err := time.Parse(utils.FormatDate, v.DataTime)
+			if err != nil {
+				errMsg = "time.Parse Err:" + err.Error() + ";DataTime:" + v.DataTime
+				return result, err
+			}
+			if k == len(dataList)-1 {
+				cureentDate = dateTime
+			}
+			year := dateTime.Year()
+			if _, ok := yearMap[year]; !ok {
+				yearArr = append(yearArr, year)
+			}
+			yearMap[year] = year
+		}
+	} else {
+		for k, v := range dataList {
+			dateTime, err := time.Parse(utils.FormatDate, v.DataTime)
+			if err != nil {
+				errMsg = "time.Parse Err:" + err.Error() + ";DataTime:" + v.DataTime
+				return result, err
+			}
+			if k == len(dataList)-1 {
+				cureentDate = dateTime
+			}
+			year := dateTime.Year() + 1
+			if _, ok := yearMap[year]; !ok {
+				yearArr = append(yearArr, year)
+			}
+			yearMap[year] = year
+		}
+	}
+	//排序
+	fmt.Println("yearArr:", yearArr)
+	thisYear := cureentDate.Year()
+	//thisMonth := int(cureentDate.Month())
+
+	fmt.Println("thisMonth:", thisMonth)
+	for ky, vy := range yearArr {
+		fmt.Println("line 432:", ky, vy, thisYear, thisMonth)
+		if thisMonth < 11 {
+			currentYearCjnl := strconv.Itoa(thisYear) + "-01-01"               //当前年份春节农历
+			currentYearCjgl := solarlunar.LunarToSolar(currentYearCjnl, false) //当前年份春节公历
+			currentYearCjglDate, err := time.Parse(utils.FormatDate, currentYearCjgl)
+			if err != nil {
+				errMsg = "生成当前春节失败,Err:" + err.Error()
+				return result, err
+			}
+
+			preYear := vy
+			preYearCjnl := strconv.Itoa(preYear) + "-01-01"            //之前年份春节农历
+			preYearCjgl := solarlunar.LunarToSolar(preYearCjnl, false) //之前年份春节公历
+			preYearCjglDate, err := time.Parse(utils.FormatDate, preYearCjgl)
+			if err != nil {
+				errMsg = "生成历史年份春节失败,Err:" + err.Error()
+				return result, err
+			}
+			day := currentYearCjglDate.Sub(preYearCjglDate).Hours() / float64(24)
+
+			fmt.Println("day:", day, currentYearCjglDate, preYearCjglDate)
+
+			items := new(EdbDataItems)
+			items.BetweenDay = int(day) //公历日期换算成农历,需要减除的天数
+			items.Year = preYear
+			for _, v := range dataList {
+				dateTime, err := time.Parse(utils.FormatDate, v.DataTime)
+				if err != nil {
+					errMsg = "time.Parse Err:" + err.Error() + ";DataTime:" + v.DataTime
+					return result, err
+				}
+				newDate := dateTime.AddDate(0, 0, int(day))
+				selectDateStr := strconv.Itoa(thisYear) + "-11" + "-30"
+				selectDate, _ := time.Parse(utils.FormatDate, selectDateStr)
+				if newDate.Before(selectDate) || newDate == selectDate {
+					timestamp := newDate.UnixNano() / 1e6
+					item := new(EdbDataList)
+					item.DataTime = newDate.Format(utils.FormatDate)
+					//item.EdbInfoId = v.EdbInfoId
+					item.Value = v.Value
+					//item.EdbDataId = v.EdbDataId
+					item.DataTimestamp = timestamp
+					items.Items = append(items.Items, item)
+				}
+			}
+			result.List = append(result.List, items)
+		} else {
+			nextYear := thisYear + 1
+			nextYearCjnl := strconv.Itoa(nextYear) + "-01-01"            //当前年份春节农历
+			nextYearCjgl := solarlunar.LunarToSolar(nextYearCjnl, false) //当前年份春节公历
+
+			nextYearCjglDate, err := time.Parse(utils.FormatDate, nextYearCjgl)
+			if err != nil {
+				errMsg = "生成当前春节失败,Err:" + err.Error()
+				return result, err
+			}
+			preYear := vy
+			preYearCjnl := strconv.Itoa(preYear) + "-01-01"            //之前年份春节农历
+			preYearCjgl := solarlunar.LunarToSolar(preYearCjnl, false) //之前年份春节公历
+			preYearCjglDate, err := time.Parse(utils.FormatDate, preYearCjgl)
+			if err != nil {
+				errMsg = "生成历史年份春节失败,Err:" + err.Error()
+				return result, err
+			}
+			day := nextYearCjglDate.Sub(preYearCjglDate).Hours() / float64(24)
+
+			fmt.Println("day:", day, nextYearCjglDate, preYearCjglDate)
+
+			items := new(EdbDataItems)
+			items.BetweenDay = int(day) //公历日期换算成农历,需要减除的天数
+			items.Year = preYear - 1
+			fmt.Println("preYear:", preYear, "ky:", ky, "yearArrLen:", len(yearArr))
+			if ky+1 < len(yearArr) {
+				for _, v := range dataList {
+					dateTime, err := time.Parse(utils.FormatDate, v.DataTime)
+					if err != nil {
+						errMsg = "time.Parse Err:" + err.Error() + ";DataTime:" + v.DataTime
+						return result, err
+					}
+					newDate := dateTime.AddDate(0, 0, int(day))
+					selectDateStr := strconv.Itoa(nextYear) + "-05" + "-31"
+					selectDate, _ := time.Parse(utils.FormatDate, selectDateStr)
+
+					if newDate.Before(selectDate) || newDate == selectDate {
+						timestamp := newDate.UnixNano() / 1e6
+						item := new(EdbDataList)
+						item.DataTime = newDate.Format(utils.FormatDate)
+						//item.EdbInfoId = v.EdbInfoId
+						item.Value = v.Value
+						//item.EdbDataId = v.EdbDataId
+						item.DataTimestamp = timestamp
+						items.Items = append(items.Items, item)
+					}
+				}
+				result.List = append(result.List, items)
+			} else {
+				for _, v := range dataList {
+					dateTime, err := time.Parse(utils.FormatDate, v.DataTime)
+					if err != nil {
+						errMsg = "time.Parse Err:" + err.Error() + ";DataTime:" + v.DataTime
+						return result, err
+					}
+					timestamp := dateTime.UnixNano() / 1e6
+					item := new(EdbDataList)
+					item.DataTime = dateTime.Format(utils.FormatDate)
+					//item.EdbInfoId = v.EdbInfoId
+					item.Value = v.Value
+					//item.EdbDataId = v.EdbDataId
+					item.DataTimestamp = timestamp
+					items.Items = append(items.Items, item)
+				}
+				result.List = append(result.List, items)
+			}
+		}
+	}
+	return
+}

+ 46 - 0
models/data_manage/edb_data_wind.go

@@ -0,0 +1,46 @@
+package data_manage
+
+import (
+	"eta/eta_hub/utils"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+func EdbInfoUpdateStatusByEdbInfoId(edbInfoIds []int, isStop int, calculateEdbInfoIds []int) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	// 更改指标的更新状态
+	if len(edbInfoIds) == 1 {
+		sql := ` UPDATE edb_info SET no_update = ? WHERE edb_info_id=? `
+		_, err = o.Raw(sql, isStop, edbInfoIds[0]).Exec()
+		if err != nil {
+			return
+		}
+	} else {
+		sql := ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, edbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	if len(calculateEdbInfoIds) > 0 {
+		// 批量更新相关联的指标ID
+		sql := ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 12 - 0
models/data_manage/edb_info.go

@@ -210,3 +210,15 @@ func GetEdbInfoByIdList(edbInfoIdList []int) (items []*EdbInfo, err error) {
 	_, err = o.Raw(sql, edbInfoIdList).QueryRows(&items)
 	return
 }
+
+// GetEdbInfoByEdbCodeList 根据指标code集合获取指标列表
+func GetEdbInfoByEdbCodeList(source int, edbCodeList []string) (items []*EdbInfo, err error) {
+	num := len(edbCodeList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_info WHERE source=? AND edb_code in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, source, edbCodeList).QueryRows(&items)
+	return
+}

+ 11 - 0
models/data_manage/edb_info_calculate_mapping.go

@@ -99,3 +99,14 @@ func GetEdbInfoCalculateMappingListByEdbInfoId(edbInfoId int) (items []*EdbInfoC
 	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
 	return
 }
+
+func GetRelationEdbInfoListMappingByCondition(condition string, pars []interface{}) (item []*ChartEdbInfoMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT a.* FROM edb_info AS a 
+	JOIN edb_info_calculate_mapping AS b on a.edb_info_id = b.edb_info_id WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&item)
+	return
+}

+ 59 - 0
models/data_manage/edb_info_refresh.go

@@ -0,0 +1,59 @@
+package data_manage
+
+// SaveRelationEdbRefreshStatusReq
+// @Description: 设置被引用的指标的刷新状态
+type SaveRelationEdbRefreshStatusReq struct {
+	Source int `description:"来源"`
+	/*ClassifyId      string `description:"分类id,支持多选,用英文,隔开"`
+	SysUserId       string `description:"操作人id,支持多选,用英文,隔开"`
+	Frequency       string `description:"频度,支持多选,用英文,隔开"`
+	Status          string `description:"状态,枚举值:启用、暂停"`
+	EdbInfoType     int    `description:"1计算指标,2预测指标"`
+	Keyword         string `description:"关键字"`
+	IsSelectAll     bool   `description:"是否选择所有指标"`*/
+	EdbSelectIdList []int  `description:"选择的指标id列表"`
+	ModifyStatus    string `description:"需要更改的状态,枚举值:启用、暂停"`
+}
+
+// 查询指标引用列表
+/*func GetEdbInfoRelationList(condition string, pars []interface{}, addFieldStr, joinTableStr, orderBy string, startSize, pageSize int) (total int, items []*BaseRelationEdbInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 数量汇总
+	totalSql := ` SELECT count(1) FROM edb_info e LEFT JOIN (
+SELECT count(edb_info_id) as relation_num, edb_info_id, max(relation_time) as relation_time FROM edb_info_relation GROUP BY edb_info_id) r on e.edb_info_id=r.edb_info_id  `
+
+	if joinTableStr != "" {
+		totalSql += joinTableStr
+	}
+	totalSql += ` WHERE 1=1 `
+	if condition != "" {
+		totalSql += condition
+	}
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	fieldStr := ` e.edb_info_id, e.classify_id,e.edb_code,e.edb_name,e.sys_user_id,e.sys_user_real_name,e.frequency,e.no_update as is_stop, r.relation_num, r.relation_time ` + addFieldStr
+	// 列表数据
+	sql := ` SELECT ` + fieldStr + ` from edb_info e LEFT JOIN (
+SELECT count(edb_info_id) as relation_num, edb_info_id, max(relation_time) as relation_time FROM edb_info_relation GROUP BY edb_info_id) r on e.edb_info_id=r.edb_info_id  `
+	if joinTableStr != "" {
+		sql += joinTableStr
+	}
+	sql += ` WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+
+	if orderBy != "" {
+		sql += ` ORDER BY ` + orderBy
+	} else {
+		sql += ` ORDER BY edb_info_id ASC `
+	}
+	sql += `  LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+
+	return
+}*/

+ 6 - 0
models/report.go

@@ -90,6 +90,8 @@ type Report struct {
 	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
 	IsPublicPublish     int8      `description:"是否公开发布,1:是,2:否"`
 	ReportCreateTime    time.Time `description:"报告时间创建时间"`
+	DetailImgUrl        string    `description:"报告详情长图地址"`
+	DetailPdfUrl        string    `description:"报告详情PDF地址"`
 }
 
 type ReportListResp struct {
@@ -504,6 +506,8 @@ type ReportItem struct {
 	ReportCreateTime string               `description:"报告时间创建时间"`
 	ChapterList      []*ReportChapterItem `description:"章节列表"`
 	HasChapter       int                  `description:"是否有章节 0-否 1-是"`
+	DetailImgUrl     string               `description:"报告详情长图地址"`
+	DetailPdfUrl     string               `description:"报告详情PDF地址"`
 }
 
 func FormatReport2Item(origin *Report, chapterList []*ReportChapterItem) (item *ReportItem) {
@@ -550,6 +554,8 @@ func FormatReport2Item(origin *Report, chapterList []*ReportChapterItem) (item *
 	item.ReportCreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.ReportCreateTime)
 	item.ChapterList = chapterList
 	item.HasChapter = origin.HasChapter
+	item.DetailImgUrl = origin.DetailImgUrl
+	item.DetailPdfUrl = origin.DetailPdfUrl
 
 	return
 }

+ 40 - 0
models/sandbox/request/sandbox.go

@@ -0,0 +1,40 @@
+package request
+
+// AddAndEditSandbox 添加/编辑沙盘的请求数据
+type AddAndEditSandbox struct {
+	SandboxId          int    `description:"沙盘id"`
+	SandboxVersionCode string `description:"沙盘版本code"`
+	Name               string `description:"沙盘名称"`
+	ChartPermissionId  int    `description:"品种权限id"`
+	Content            string `description:"沙盘内容"`
+	PicUrl             string `description:"沙盘图片地址"`
+	SvgData            string `description:"沙盘svg图片数据"`
+}
+
+// MarkEditSandbox 标记编辑沙盘的请求数据
+type MarkEditSandbox struct {
+	SandboxId int `description:"沙盘id"`
+	Status    int `description:"标记状态,1:编辑中,2:编辑完成"`
+}
+
+// DeleteSandbox 删除沙盘的请求数据
+type DeleteSandbox struct {
+	SandboxId int `description:"沙盘id"`
+}
+
+// DeleteSandboxVersion 删除沙盘版本的请求数据
+type DeleteSandboxVersion struct {
+	SandboxVersionCode string `description:"沙盘版本code"`
+}
+
+// AddAndEditSandboxV2 添加/编辑沙盘的请求数据
+type AddAndEditSandboxV2 struct {
+	SandboxId         int    `description:"沙盘id"`
+	Name              string `description:"沙盘名称"`
+	Content           string `description:"沙盘内容"`
+	Style             int    `description:"风格"`
+	MindmapData       string `description:"思维导图内容"`
+	PicUrl            string `description:"沙盘图片地址"`
+	SvgData           string `description:"沙盘svg图片数据"`
+	SandboxClassifyId int    `description:"分类id"`
+}

+ 18 - 0
models/sandbox/response/sandbox.go

@@ -0,0 +1,18 @@
+package response
+
+import (
+	"eta/eta_api/models/sandbox"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+// SandboxListResp 沙盘列表返回数据
+type SandboxListResp struct {
+	Paging *paging.PagingItem
+	List   []*sandbox.Sandbox
+}
+
+// SandboxVersionListResp 沙盘版本列表返回数据
+type SandboxVersionListResp struct {
+	Paging *paging.PagingItem
+	List   []*sandbox.SandboxVersionListItem
+}

+ 187 - 0
models/sandbox/sandbox.go

@@ -0,0 +1,187 @@
+package sandbox
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type Sandbox struct {
+	SandboxId         int       `orm:"column(sandbox_id);pk" description:"沙盘id"`
+	Name              string    `description:"沙盘名称"`
+	Code              string    `description:"沙盘code"`
+	Content           string    `description:"沙盘数据"`
+	MindmapData       string    `description:"思维导图数据"`
+	PicUrl            string    `description:"沙盘图片地址"`
+	SysUserId         int       `description:"作者id"`
+	SysUserName       string    `description:"作者名称"`
+	IsDelete          int8      `description:"是否删除,0:未删除,1:已删除"`
+	ModifyTime        time.Time `description:"修改时间"`
+	CreateTime        time.Time `description:"创建时间"`
+	SandboxClassifyId int       `description:"分类id"`
+	Sort              int       `description:"排序"`
+	Style             int       `description:"风格"`
+}
+
+type SandboxItem struct {
+	Sandbox
+	ModifyTime string `description:"修改时间"`
+	CreateTime string `description:"创建时间"`
+}
+
+// Update 沙盘字段变更
+func (sandbox *Sandbox) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(sandbox, cols...)
+	return
+}
+
+// GetSandboxById 根据沙盘id获取沙盘详情
+func GetSandboxById(sandboxId int) (sandboxInfo *SandboxItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `select * from sandbox where sandbox_id = ? and is_delete = 0`
+	err = o.Raw(sql, sandboxId).QueryRow(&sandboxInfo)
+	return
+}
+
+// SandboxListItem 沙盘推演列表数据
+type SandboxListItem struct {
+	SandboxId int    `description:"沙盘id"`
+	Name      string `description:"沙盘名称"`
+	//ChartPermissionId   int    `description:"品种id"`
+	//ChartPermissionName string `description:"品种名称"`
+	//CurrVersion         int    `description:"当前版本"`
+	Code string `description:"沙盘code"`
+	//VersionCode         string `description:"沙盘版本code"`
+	//Content             string    `description:"沙盘数据"`
+	PicUrl string `description:"沙盘图片地址"`
+	//OpUserId   int    `description:"最近一次编辑操作的用户id"`
+	//OpUserName string `description:"最近一次编辑的用户名称(冗余字段,避免查表)"`
+	IsDelete int8 `description:"是否删除,0:未删除,1:已删除" json:"is_delete"`
+	//	CanEdit      bool      `description:"是否可编辑"`
+	//	Editor       string    `description:"编辑人"`
+	//	VersionTotal int       `description:"历史版本数量"`
+	ModifyTime string `description:"修改时间"`
+	CreateTime string `description:"创建时间"`
+
+	SysUserId         int    `description:"作者id"`
+	SysUserName       string `description:"作者名称"`
+	SandboxClassifyId int    `description:"分类id"`
+	Sort              int    `description:"排序"`
+	Style             int    `description:"风格"`
+}
+
+func GetSandboxClassify(sandboxClassifyId int) (sandbox_classify_id string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT GROUP_CONCAT(t.sandbox_classify_id) AS sandbox_classify_id FROM (
+			SELECT a.sandbox_classify_id FROM sandbox_classify AS a 
+			WHERE a.sandbox_classify_id=?
+			UNION ALL
+			SELECT a.sandbox_classify_id FROM sandbox_classify AS a 
+			WHERE a.parent_id=? UNION ALL
+	SELECT
+		sandbox_classify_id 
+	FROM
+		sandbox_classify 
+WHERE
+	parent_id IN ( SELECT sandbox_classify_id FROM sandbox_classify WHERE parent_id = ? )
+			)AS t`
+	err = o.Raw(sql, sandboxClassifyId, sandboxClassifyId, sandboxClassifyId).QueryRow(&sandbox_classify_id)
+	return
+}
+
+type SandboxListItems struct {
+	SandboxListItem
+	ParentIds string
+}
+
+func GetSandboxListByCondition(condition string, pars []interface{}, startSize, pageSize int) (item []*SandboxListItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM sandbox 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 GetSandboxListCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count FROM sandbox WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type SandboxListResp struct {
+	Paging *paging.PagingItem
+	List   []*SandboxListItems
+}
+
+func GetSandboxAllParentByClassifyId(sandboxClassifyId int) (ids string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT
+	GROUP_CONCAT(DISTINCT m.sandbox_classify_id  ORDER BY m.level) AS ids 
+FROM
+	(
+	SELECT
+		@id AS _id,(
+		SELECT
+			@id := parent_id 
+		FROM
+			sandbox_classify 
+		WHERE
+			sandbox_classify_id = _id 
+		) 
+	FROM
+		(
+		SELECT
+			@id :=(
+			SELECT
+				parent_id 
+			FROM
+				sandbox_classify 
+			WHERE
+				sandbox_classify_id = ? 
+			)) vm,
+		sandbox_classify m 
+	WHERE
+		@id IS NOT NULL 
+	) vm
+	INNER JOIN sandbox_classify m 
+WHERE
+	sandbox_classify_id = vm._id `
+	err = o.Raw(sql, sandboxClassifyId).QueryRow(&ids)
+	return
+}
+
+// ContentDataStruct 沙盘内容结构体
+type ContentDataStruct struct {
+	Cells []struct {
+		Data *NodeData `json:"data,omitempty"`
+	} `json:"cells"`
+}
+
+type NodeData struct {
+	LinkData []*LinkData `json:"linkData"`
+	LinkFold bool        `json:"linkFold"`
+}
+
+type LinkData struct {
+	RId          string       `json:"RId"`
+	Id           int          `json:"Id"`
+	Name         string       `json:"Name"`
+	Type         int          `json:"Type"`
+	Editing      bool         `json:"editing"`
+	DatabaseType int          `json:"databaseType"`
+	DetailParams DetailParams `json:"detailParams"`
+}
+
+type DetailParams struct {
+	Code       string `json:"code"`
+	Id         int    `json:"id"`
+	ClassifyId int    `json:"classifyId"`
+}

+ 285 - 0
models/sandbox/sandbox_classify.go

@@ -0,0 +1,285 @@
+package sandbox
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SandboxClassify struct {
+	SandboxClassifyId   int       `orm:"column(sandbox_classify_id);pk"`
+	SandboxClassifyName string    `description:"分类名称"`
+	ParentId            int       `description:"父级id"`
+	HasData             int       `description:"是否含有指标数据"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+	SysUserId           int       `description:"创建人id"`
+	SysUserRealName     string    `description:"创建人姓名"`
+	Level               int       `description:"层级"`
+	ChartPermissionId   int       `description:"品种id"`
+	ChartPermissionName string    `description:"品种名称"`
+	Sort                int       `description:"排序字段,越小越靠前,默认值:10"`
+}
+
+func AddSandboxClassify(item *SandboxClassify) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	lastId, err = o.Insert(item)
+	return
+}
+
+// GetSandboxClassifyByParentId
+func GetSandboxClassifyByParentId(parentId int) (items []*SandboxClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM sandbox_classify WHERE parent_id=? order by sort asc,sandbox_classify_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetSandboxClassifyAll
+func GetSandboxClassifyAll() (items []*SandboxClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM sandbox_classify WHERE parent_id<>0 order by sort asc,sandbox_classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type SandboxClassifyItems struct {
+	SandboxClassifyId   int       `orm:"column(sandbox_classify_id);pk"`
+	SandboxClassifyName string    `description:"分类名称"`
+	ParentId            int       `description:"父级id"`
+	HasData             int       `description:"是否含有指标数据"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+	SysUserId           int       `description:"创建人id"`
+	SysUserName         string    `description:"创建人姓名"`
+	Level               int       `description:"层级"`
+	Sort                int       `description:"排序字段,越小越靠前,默认值:10"`
+	SandboxId           int       `description:"沙盘id"`
+	ChartPermissionId   int       `description:"品种id"`
+	ChartPermissionName string    `description:"品种名称"`
+	Children            []*SandboxClassifyItems
+}
+
+type SandboxClassifyListResp struct {
+	AllNodes []*SandboxClassifyItems
+}
+
+type AddSandboxClassifyReq struct {
+	SandboxClassifyName string `description:"分类名称"`
+	ParentId            int    `description:"父级id,第一级传0"`
+	Level               int    `description:"层级,第一级传0,其余传上一级的层级"`
+	ChartPermissionId   int    `description:"品种id"`
+	ChartPermissionName string `description:"品种名称"`
+}
+
+func GetSandboxClassifyCount(sandboxClassifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM sandbox_classify WHERE parent_id=? AND sandbox_classify_name=? `
+	err = o.Raw(sql, parentId, sandboxClassifyName).QueryRow(&count)
+	return
+}
+
+// GetSandboxClassifyMaxSort 获取沙盘分类下最大的排序数
+func GetSandboxClassifyMaxSort(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT Max(sort) AS sort FROM sandbox_classify WHERE parent_id=? `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type EditSandboxClassifyReq struct {
+	SandboxClassifyName string `description:"分类名称"`
+	SandboxClassifyId   int    `description:"分类id"`
+	ChartPermissionId   int    `description:"品种id"`
+	ChartPermissionName string `description:"品种名称"`
+}
+
+func GetSandboxClassifyById(classifyId int) (item *SandboxClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM sandbox_classify WHERE sandbox_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func EditSandboxClassify(classifyId, ChartPermissionId int, sandboxClassifyName, ChartPermissionName string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE sandbox_classify SET sandbox_classify_name=?,chart_permission_id = ?, chart_permission_name = ?, modify_time=NOW() WHERE sandbox_classify_id=? `
+	_, err = o.Raw(sql, sandboxClassifyName, ChartPermissionId, ChartPermissionName, classifyId).Exec()
+	return
+}
+
+type SandboxClassifyDeleteCheckReq struct {
+	SandboxClassifyId int `description:"分类id"`
+}
+
+func GetSandboxInfoCountByClassifyId(classifyId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count FROM sandbox AS a
+				WHERE a.sandbox_classify_id IN(
+				SELECT t.sandbox_classify_id FROM 
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM sandbox_classify WHERE parent_id IS NOT NULL) rd,
+					 (SELECT @pid := ?) pd 
+				WHERE FIND_IN_SET(parent_id, @pid) > 0 
+				  AND @pid := CONCAT(@pid, ',', sandbox_classify_id) 
+				UNION SELECT * FROM sandbox_classify WHERE sandbox_classify_id = @pid 
+				)AS t
+				) AND a.is_delete = 0 `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+type SandboxClassifyDeleteCheckResp struct {
+	DeleteStatus int    `description:"检测状态:0:默认值,如果为0,继续走其他校验,1:该分类下关联图表不可删除,2:确认删除当前目录及包含的子目录吗"`
+	TipsMsg      string `description:"提示信息"`
+}
+
+type DeleteSandboxClassifyReq struct {
+	SandboxClassifyId int `description:"分类id"`
+	SandboxId         int `description:"指标id"`
+}
+
+func DeleteSandboxClassify(classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` DELETE FROM sandbox_classify
+				WHERE sandbox_classify_id IN(
+				SELECT t.sandbox_classify_id FROM
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM sandbox_classify WHERE parent_id IS NOT NULL) rd,
+				(SELECT @pid := ?) pd
+				WHERE FIND_IN_SET(parent_id, @pid) > 0
+				AND @pid := CONCAT(@pid, ',', sandbox_classify_id)
+				UNION SELECT * FROM sandbox_classify WHERE sandbox_classify_id = @pid
+				)AS t
+				) `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+// MoveSandboxClassifyReq 移动沙盘分类请求参数
+type MoveSandboxClassifyReq 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沙盘 "`
+}
+
+// UpdateSandboxClassifySortByParentId 根据沙盘父类id更新排序
+func UpdateSandboxClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update sandbox_classify set sort = ` + updateSort + ` WHERE parent_id=? and sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( sandbox_classify_id > ` + fmt.Sprint(classifyId) + ` and sort= ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// GetFirstSandboxClassifyByParentId 获取当前父级沙盘分类下的排序第一条的数据
+func GetFirstSandboxClassifyByParentId(parentId int) (item *SandboxClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM sandbox_classify WHERE parent_id=? order by sort asc,sandbox_classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+// Update 更新沙盘分类基础信息
+func (sandboxClassify *SandboxClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(sandboxClassify, cols...)
+	return
+}
+
+// GetSandboxClassifyAndInfoByParentId
+func GetSandboxClassifyAndInfoByParentId(parentId int) (items []*SandboxClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT
+	0 AS sandbox_id,
+	sandbox_classify_id,
+	sandbox_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
+	sandbox_classify 
+WHERE
+	parent_id = ? UNION ALL
+SELECT
+	sandbox_id,
+	sandbox_classify_id,
+	name AS sandbox_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
+	sandbox_classify_id = ? AND is_delete = 0
+ORDER BY
+	sort ASC,
+	sandbox_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 GetSandboxClassifySubcategories(classifyId int) (Ids string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT GROUP_CONCAT(sandbox_classify_id) AS ids
+FROM (
+SELECT @pv := ? AS sandbox_classify_id
+UNION ALL
+SELECT sc.sandbox_classify_id
+FROM sandbox_classify sc
+JOIN (SELECT @pv := ?) initial
+WHERE sc.parent_id = @pv
+) subcategories; `
+	err = o.Raw(sql, classifyId, classifyId).QueryRow(&Ids)
+	return
+}
+
+// UpdateSandboxClassifyChartPermissionById 根据沙盘id更新品种
+func UpdateSandboxClassifyChartPermissionById(ChartPermissionId int, ChartPermissionName, Ids string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` UPDATE sandbox_classify SET chart_permission_id = ?, chart_permission_name = ? WHERE sandbox_classify_id IN ( ` + Ids + ` ) `
+	_, err = o.Raw(sql, ChartPermissionId, ChartPermissionName).Exec()
+	return
+}

+ 63 - 0
routers/commentsRouter.go

@@ -97,6 +97,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbController"],
+        beego.ControllerComments{
+            Method: "EdbInfoDataSeasonal",
+            Router: `/data/seasonal`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbController"],
+        beego.ControllerComments{
+            Method: "EdbInfoDataTb",
+            Router: `/data/tb`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbController"],
         beego.ControllerComments{
             Method: "Detail",
@@ -187,6 +205,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "SaveRelationEdbRefreshStatus",
+            Router: `/refresh/set`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_hub/controllers:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:EnglishReportController"],
         beego.ControllerComments{
             Method: "Approve",
@@ -286,6 +313,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"],
+        beego.ControllerComments{
+            Method: "SandboxClassifyItems",
+            Router: `/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"],
+        beego.ControllerComments{
+            Method: "SandboxClassifyList",
+            Router: `/classifyList`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"],
+        beego.ControllerComments{
+            Method: "GetSandboxDetail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:SandboxController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_hub/controllers:SmartReportController"] = append(beego.GlobalControllerRouter["eta/eta_hub/controllers:SmartReportController"],
         beego.ControllerComments{
             Method: "Approve",

+ 5 - 0
routers/router.go

@@ -83,6 +83,11 @@ func init() {
 				&controllers.ResourceDealWithController{},
 			),
 		),
+		web.NSNamespace("/sandbox",
+			web.NSInclude(
+				&controllers.SandboxController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 289 - 0
services/data/edb_data.go

@@ -0,0 +1,289 @@
+package data
+
+import (
+	"errors"
+	"eta/eta_hub/models/data_manage"
+	"eta/eta_hub/utils"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"time"
+)
+
+// GetEdbDataTbz 获取指标的同比值数据
+func GetEdbDataTbz(edbInfo *data_manage.EdbInfo, tmpDataList []*data_manage.EdbDataList, startDateTime time.Time) (dataList []*data_manage.EdbDataList, minValue, maxValue float64, err error) {
+	dataList = make([]*data_manage.EdbDataList, 0)
+
+	// 数据处理
+	var dateArr []string
+	dataMap := make(map[string]*data_manage.EdbDataList)
+	for _, v := range tmpDataList {
+		dateArr = append(dateArr, v.DataTime)
+		dataMap[v.DataTime] = v
+	}
+	for _, av := range dateArr {
+		currentItem, ok := dataMap[av]
+		// 如果找不到当前日期的数据,那么终止当前循环,进入下一循环
+		if !ok {
+			continue
+		}
+		tmpItem := *currentItem
+		var isOk bool //是否计算出来结果
+
+		//当前日期
+		currentDate, tmpErr := time.ParseInLocation(utils.FormatDate, av, time.Local)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		// 如果存在开始日期,同时,当前日期早于开始日期,那么终止当前循环,进入下一循环
+		if !startDateTime.IsZero() && currentDate.Before(startDateTime) {
+			continue
+		}
+		//上一年的日期
+		preDate := currentDate.AddDate(-1, 0, 0)
+		preDateStr := preDate.Format(utils.FormatDate)
+		if findItem, ok := dataMap[preDateStr]; ok { //上一年同期找到
+			tmpItem.Value = TbzDiv(currentItem.Value, findItem.Value)
+			isOk = true
+		} else {
+			if edbInfo.Frequency == "月度" { //向上和向下,各找一个月
+				for i := 0; i <= 35; i++ {
+					nextDateDay := preDate.AddDate(0, 0, i)
+					nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+					if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						tmpItem.Value = TbzDiv(currentItem.Value, findItem.Value)
+						isOk = true
+						break
+					} else {
+						preDateDay := preDate.AddDate(0, 0, -i)
+						preDateDayStr := preDateDay.Format(utils.FormatDate)
+						if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							tmpItem.Value = TbzDiv(currentItem.Value, findItem.Value)
+							isOk = true
+							break
+						}
+					}
+				}
+			} else if edbInfo.Frequency == "季度" || edbInfo.Frequency == "年度" {
+				if findItem, ok := dataMap[preDateStr]; ok { //上一年同期->下一个月找到
+					tmpItem.Value = TbzDiv(currentItem.Value, findItem.Value)
+					isOk = true
+					break
+				}
+			} else {
+				nextDateDay := preDate.AddDate(0, 0, 1)
+				nextDateDayStr := nextDateDay.Format(utils.FormatDate)
+
+				preDateDay := preDate.AddDate(0, 0, -1)
+				preDateDayStr := preDateDay.Format(utils.FormatDate)
+
+				for i := 0; i < 35; i++ {
+					if i >= 1 {
+						nextDateDay = nextDateDay.AddDate(0, 0, i)
+						nextDateDayStr = nextDateDay.Format(utils.FormatDate)
+					}
+					if findItem, ok := dataMap[nextDateDayStr]; ok { //上一年同期->下一个月找到
+						tmpItem.Value = TbzDiv(currentItem.Value, findItem.Value)
+						isOk = true
+						break
+					} else {
+						if i >= 1 {
+							preDateDay = preDate.AddDate(0, 0, -i)
+							preDateDayStr = nextDateDay.Format(utils.FormatDate)
+						}
+						if findItem, ok := dataMap[preDateDayStr]; ok { //上一年同期->上一个月找到
+							tmpItem.Value = TbzDiv(currentItem.Value, findItem.Value)
+							isOk = true
+							break
+						}
+					}
+				}
+			}
+		}
+
+		if isOk {
+			if tmpItem.Value > maxValue {
+				maxValue = tmpItem.Value
+			}
+			if tmpItem.Value < minValue {
+				minValue = tmpItem.Value
+			}
+			dataList = append(dataList, &tmpItem)
+		}
+	}
+
+	return
+}
+
+// GetChartEdbSeasonalData 获取指标的季节性数据
+func GetChartEdbSeasonalData(calendar, startDate string, edbInfo *data_manage.EdbInfo) (resultData interface{}, err error) {
+	var startDateReal string
+	startDateReal = startDate
+	calendarPreYear := 0
+	if calendar == "农历" {
+		newStartDateReal, tmpErr := time.Parse(utils.FormatDate, startDateReal)
+		if tmpErr != nil {
+			err = tmpErr
+			fmt.Println("time.Parse:" + err.Error())
+			return
+		}
+		calendarPreYear = newStartDateReal.Year() - 1
+		newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
+		startDateReal = newStartDateReal.Format(utils.FormatDate)
+	}
+	dataList, err := GetEdbDataList(edbInfo, startDateReal)
+	if err != nil {
+		err = fmt.Errorf("获取指标数据出错 Err:%s", err.Error())
+		return
+	}
+
+	latestDateStr := edbInfo.LatestDate //实际数据的截止日期
+	latestDate, tmpErr := time.Parse(utils.FormatDate, edbInfo.LatestDate)
+	if tmpErr != nil {
+		err = errors.New(fmt.Sprint("获取最后实际数据的日期失败,Err:" + tmpErr.Error() + ";LatestDate:" + edbInfo.LatestDate))
+		return
+	}
+	latestDateYear := latestDate.Year() //实际数据截止年份
+
+	if calendar == "农历" {
+		if len(dataList) <= 0 {
+			resultData = new(data_manage.EdbDataResult)
+		} else {
+			result, tmpErr := data_manage.AddCalculateQuarterV4(dataList)
+			if tmpErr != nil {
+				err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			// 处理季节图的截止日期
+			for k, edbDataItems := range result.List {
+				var cuttingDataTimestamp int64
+
+				// 切割的日期时间字符串
+				cuttingDataTimeStr := latestDate.AddDate(0, 0, edbDataItems.BetweenDay).Format(utils.FormatDate)
+				//如果等于最后的实际日期,那么遍历找到该日期对应的时间戳,并将其赋值为 切割时间戳
+				if edbDataItems.Year >= latestDateYear {
+					for _, tmpData := range edbDataItems.Items {
+						if tmpData.DataTime == cuttingDataTimeStr {
+							cuttingDataTimestamp = tmpData.DataTimestamp
+							break
+						}
+					}
+				}
+				edbDataItems.CuttingDataTimestamp = cuttingDataTimestamp
+				result.List[k] = edbDataItems
+			}
+			//fmt.Println("result", result.List[0].Items[0].DataTime)
+			//fmt.Println("calendarPreYear:", calendarPreYear)
+			//fmt.Println("result.List[0].Year", result.List[0].Year)
+			if result.List[0].Year != calendarPreYear {
+				itemList := make([]*data_manage.EdbDataList, 0)
+				items := new(data_manage.EdbDataItems)
+				//items.Year = calendarPreYear
+				items.Items = itemList
+
+				newResult := new(data_manage.EdbDataResult)
+				newResult.List = append(newResult.List, items)
+				newResult.List = append(newResult.List, result.List...)
+				resultData = newResult
+			} else {
+				resultData = result
+			}
+		}
+
+	} else {
+		currentYear := time.Now().Year()
+
+		quarterDataList := make([]*data_manage.QuarterData, 0)
+		quarterMap := make(map[int][]*data_manage.EdbDataList)
+		var quarterArr []int
+
+		for _, v := range dataList {
+			itemDate, tmpErr := time.Parse(utils.FormatDate, v.DataTime)
+			if tmpErr != nil {
+				err = errors.New("季度指标日期转换,Err:" + tmpErr.Error() + ";DataTime:" + v.DataTime)
+				return
+			}
+			year := itemDate.Year()
+			newItemDate := itemDate.AddDate(currentYear-year, 0, 0)
+			timestamp := newItemDate.UnixNano() / 1e6
+			v.DataTimestamp = timestamp
+			if findVal, ok := quarterMap[year]; !ok {
+				quarterArr = append(quarterArr, year)
+				findVal = append(findVal, v)
+				quarterMap[year] = findVal
+			} else {
+				findVal = append(findVal, v)
+				quarterMap[year] = findVal
+			}
+		}
+		for _, v := range quarterArr {
+			itemList := quarterMap[v]
+			quarterItem := new(data_manage.QuarterData)
+			quarterItem.Year = v
+			quarterItem.DataList = itemList
+
+			//如果等于最后的实际日期,那么将切割时间戳记录
+			if v == latestDateYear {
+				var cuttingDataTimestamp int64
+				for _, tmpData := range itemList {
+					if tmpData.DataTime == latestDateStr {
+						cuttingDataTimestamp = tmpData.DataTimestamp
+						break
+					}
+				}
+				quarterItem.CuttingDataTimestamp = cuttingDataTimestamp
+			} else if v > latestDateYear {
+				//如果大于最后的实际日期,那么第一个点就是切割的时间戳
+				if len(itemList) > 0 {
+					quarterItem.CuttingDataTimestamp = itemList[0].DataTimestamp - 100
+				}
+			}
+			quarterDataList = append(quarterDataList, quarterItem)
+		}
+		resultData = quarterDataList
+	}
+	return
+}
+
+// TbzDiv 同比值计算
+func TbzDiv(a, b float64) float64 {
+	var valFloat float64
+	if b != 0 {
+		af := decimal.NewFromFloat(a)
+		bf := decimal.NewFromFloat(b)
+		val, _ := af.Div(bf).Float64()
+		val = val - 1
+		valFloat, _ = decimal.NewFromFloat(val).Round(4).Float64()
+	} else {
+		valFloat = 0
+	}
+	return valFloat
+}
+
+// GetEdbDataList 获取指标数据
+func GetEdbDataList(edbInfo *data_manage.EdbInfo, startDate string) (dataList []*data_manage.EdbDataList, err error) {
+	var list []*data_manage.EdbData
+	switch edbInfo.EdbInfoType {
+	case 0:
+		dataOb := new(data_manage.EdbData)
+		list, err = dataOb.GetItemsBySourceAndCode(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, startDate, []string{}, "data_time ASC")
+	//case 1:
+	//_, dataList, _, _, err, _ = GetPredictDataListByPredictEdbInfoId(edbInfo.EdbInfoId, startDate, endDate, false)
+	default:
+		err = errors.New(fmt.Sprint("获取失败,不支持预测指标", edbInfo.EdbInfoType))
+
+	}
+	if err != nil {
+		return
+	}
+
+	if len(list) <= 0 {
+		return
+	}
+
+	for _, v := range list {
+		dataList = append(dataList, data_manage.FormatEdbData2List(v))
+	}
+	return
+}

+ 161 - 0
services/data/edb_refresh.go

@@ -0,0 +1,161 @@
+package data
+
+import (
+	"eta/eta_hub/models/data_manage"
+	"eta/eta_hub/utils"
+	"fmt"
+)
+
+/*// GetEdbRelationList 获取指标引用列表
+func GetEdbRelationList(source, edbType int, classifyId, sysUserId, frequency, keyword, status string, startSize, pageSize int, sortParam, sortType string) (total int, list []*data_manage.BaseRelationEdbInfo, err error) {
+	var pars []interface{}
+	var condition string
+
+	list = make([]*data_manage.BaseRelationEdbInfo, 0)
+
+	isStop := -1
+	switch status {
+	case `暂停`:
+		isStop = 1
+	case `启用`:
+		isStop = 0
+	case `供应商停用`:
+		isStop = 3
+	}
+
+	// 关联表语句
+	var addFieldStr, joinTableStr string
+
+	switch source {
+	case 0: // 计算指标,不校验source
+	default:
+		condition += ` AND e.source = ? `
+		pars = append(pars, source)
+	}
+
+	if edbType == 2 { //计算指标
+		condition += ` AND e.edb_type = ? AND e.edb_info_type = 0`
+		pars = append(pars, edbType)
+	}
+	switch isStop {
+	case -1:
+		// 供应商停用
+		if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			joinTableStr = ` LEFT JOIN base_from_mysteel_chemical_index z ON e.edb_code = z.index_code `
+			addFieldStr = ` ,z.is_supplier_stop `
+		}
+	case 0, 1:
+		condition += " AND e.no_update = ? "
+		pars = append(pars, isStop)
+
+		// 供应商停用
+		if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			condition += " AND z.is_supplier_stop = ? "
+			pars = append(pars, 0)
+			joinTableStr = ` LEFT JOIN base_from_mysteel_chemical_index z ON e.edb_code = z.index_code `
+			addFieldStr = ` ,z.is_supplier_stop `
+		}
+	case 3:
+		// 供应商停用
+		if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			condition += " AND z.is_supplier_stop = ? "
+			pars = append(pars, 1)
+			joinTableStr = ` LEFT JOIN base_from_mysteel_chemical_index z ON e.edb_code = z.index_code `
+			addFieldStr = ` ,z.is_supplier_stop `
+		}
+	}
+
+	if classifyId != `` {
+		classifyIdSlice := strings.Split(classifyId, ",")
+		condition += ` AND e.classify_id IN (` + utils.GetOrmInReplace(len(classifyIdSlice)) + `)`
+		pars = append(pars, classifyIdSlice)
+	}
+	if sysUserId != `` {
+		sysUserIdSlice := strings.Split(sysUserId, ",")
+		condition += ` AND e.sys_user_id IN (` + utils.GetOrmInReplace(len(sysUserIdSlice)) + `)`
+		pars = append(pars, sysUserIdSlice)
+	}
+	if frequency != `` {
+		frequencySlice := strings.Split(frequency, ",")
+		condition += ` AND e.frequency IN (` + utils.GetOrmInReplace(len(frequencySlice)) + `)`
+		pars = append(pars, frequencySlice)
+	}
+	if keyword != `` {
+		keywordSlice := strings.Split(keyword, " ")
+		if len(keywordSlice) > 0 {
+			tmpConditionSlice := make([]string, 0)
+			tmpConditionSlice = append(tmpConditionSlice, ` e.edb_name like ? or e.edb_code like ? `)
+			pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+
+			for _, v := range keywordSlice {
+				if v == ` ` || v == `` {
+					continue
+				}
+				tmpConditionSlice = append(tmpConditionSlice, ` e.edb_name like ? or e.edb_code like ? `)
+				pars = utils.GetLikeKeywordPars(pars, v, 2)
+			}
+			condition += ` AND (` + strings.Join(tmpConditionSlice, " or ") + `)`
+
+		} else {
+			condition += ` AND (e.edb_name like ? or e.edb_code like ? )`
+			pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+		}
+	}
+
+	sortStr := ``
+	if sortParam != `` {
+		sortStr = fmt.Sprintf("%s %s,e.edb_info_id desc ", sortParam, sortType)
+	}
+
+	total, list, err = data_manage.GetEdbInfoRelationList(condition, pars, addFieldStr, joinTableStr, sortStr, startSize, pageSize)
+
+	return
+}
+*/
+// GetCalculateEdbByFromEdbInfo 找到依赖于该基础指标的所有计算指标
+func GetCalculateEdbByFromEdbInfo(edbInfoIds []int, calculateEdbIds []int, hasFind map[int]struct{}) (newCalculateEdbIds []int, err error) {
+	if len(edbInfoIds) == 0 {
+		return
+	}
+	newCalculateEdbIds = calculateEdbIds
+	newEdbInfoIds := make([]int, 0)
+	for _, v := range edbInfoIds {
+		if _, ok := hasFind[v]; ok {
+			continue
+		}
+		newEdbInfoIds = append(newEdbInfoIds, v)
+	}
+	if len(newEdbInfoIds) == 0 {
+		return
+	}
+	var condition string
+	var pars []interface{}
+	// 关联指标
+	condition += ` AND b.from_edb_info_id in (` + utils.GetOrmInReplace(len(newEdbInfoIds)) + `)`
+	pars = append(pars, newEdbInfoIds)
+
+	//获取关联图表列表
+	list, err := data_manage.GetRelationEdbInfoListMappingByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("获取关联指标信息失败,Err:%s", err.Error())
+		return
+	}
+	calculateEdbIdsTmp := make([]int, 0)
+	for _, mapping := range list {
+		if mapping.EdbType == 2 && mapping.EdbInfoType == 0 { // 如果指标库里的计算指标,则加入,否则继续找
+			newCalculateEdbIds = append(newCalculateEdbIds, mapping.EdbInfoId)
+			calculateEdbIdsTmp = append(calculateEdbIdsTmp, mapping.EdbInfoId)
+		}
+	}
+	for _, v := range newEdbInfoIds {
+		hasFind[v] = struct{}{}
+	}
+	if len(calculateEdbIdsTmp) > 0 {
+		newCalculateEdbIds, err = GetCalculateEdbByFromEdbInfo(calculateEdbIdsTmp, newCalculateEdbIds, hasFind)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 198 - 0
services/sandbox/sandbox.go

@@ -0,0 +1,198 @@
+package sandbox
+
+import (
+	"eta/eta_hub/models/sandbox"
+)
+
+// ContentStruct 沙盘内容结构体
+type ContentStruct struct {
+	Cells []struct {
+		Attrs struct {
+			Line struct {
+				SourceMarker    bool   `json:"sourceMarker"`
+				Stroke          string `json:"stroke"`
+				StrokeDasharray string `json:"strokeDasharray"`
+			} `json:"line"`
+			Rect struct {
+				Fill            string      `json:"fill"`
+				Stroke          string      `json:"stroke"`
+				StrokeDasharray interface{} `json:"strokeDasharray"`
+				StrokeWidth     int64       `json:"strokeWidth"`
+			} `json:"rect"`
+			Text struct {
+				Fill       string  `json:"fill"`
+				FontSize   float64 `json:"fontSize"`
+				FontWeight string  `json:"fontWeight"`
+				LineHeight float64 `json:"lineHeight"`
+				Text       string  `json:"text"`
+				TextWrap   struct {
+					Text  string `json:"text"`
+					Width int64  `json:"width"`
+				} `json:"textWrap"`
+			} `json:"text"`
+		} `json:"attrs"`
+		Data struct {
+			Key string `json:"key"`
+		} `json:"data"`
+		ID    string `json:"id"`
+		Ports struct {
+			Groups struct {
+				Port_bottom struct {
+					Attrs struct {
+						Circle struct {
+							Fill        string `json:"fill"`
+							Magnet      bool   `json:"magnet"`
+							R           int64  `json:"r"`
+							Stroke      string `json:"stroke"`
+							StrokeWidth int64  `json:"strokeWidth"`
+						} `json:"circle"`
+					} `json:"attrs"`
+					Position string `json:"position"`
+					ZIndex   int64  `json:"zIndex"`
+				} `json:"port-bottom"`
+				Port_left struct {
+					Attrs struct {
+						Circle struct {
+							Fill        string `json:"fill"`
+							Magnet      bool   `json:"magnet"`
+							R           int64  `json:"r"`
+							Stroke      string `json:"stroke"`
+							StrokeWidth int64  `json:"strokeWidth"`
+						} `json:"circle"`
+					} `json:"attrs"`
+					Position string `json:"position"`
+					ZIndex   int64  `json:"zIndex"`
+				} `json:"port-left"`
+				Port_right struct {
+					Attrs struct {
+						Circle struct {
+							Fill        string `json:"fill"`
+							Magnet      bool   `json:"magnet"`
+							R           int64  `json:"r"`
+							Stroke      string `json:"stroke"`
+							StrokeWidth int64  `json:"strokeWidth"`
+						} `json:"circle"`
+					} `json:"attrs"`
+					Position string `json:"position"`
+					ZIndex   int64  `json:"zIndex"`
+				} `json:"port-right"`
+				Port_top struct {
+					Attrs struct {
+						Circle struct {
+							Fill        string `json:"fill"`
+							Magnet      bool   `json:"magnet"`
+							R           int64  `json:"r"`
+							Stroke      string `json:"stroke"`
+							StrokeWidth int64  `json:"strokeWidth"`
+						} `json:"circle"`
+					} `json:"attrs"`
+					Position string `json:"position"`
+					ZIndex   int64  `json:"zIndex"`
+				} `json:"port-top"`
+			} `json:"groups"`
+			Items []struct {
+				Group string `json:"group"`
+				ID    string `json:"id"`
+			} `json:"items"`
+		} `json:"ports"`
+		Position struct {
+			X float64 `json:"x"`
+			Y float64 `json:"y"`
+		} `json:"position"`
+		Shape string `json:"shape"`
+		Size  struct {
+			Height float64 `json:"height"`
+			Width  float64 `json:"width"`
+		} `json:"size"`
+		Source struct {
+			Cell string `json:"cell"`
+			Port string `json:"port"`
+		} `json:"source"`
+		Target struct {
+			Cell string `json:"cell"`
+			Port string `json:"port"`
+		} `json:"target"`
+		ZIndex int64 `json:"zIndex"`
+	} `json:"cells"`
+}
+
+type SendBoxNodeData struct {
+	linkData []SandBoxLinkData `json:"linkData"`
+	linkFold bool              `json:"linkFold"`
+}
+
+type SandBoxLinkData struct {
+	RId          string              `json:"RId"`
+	Id           int                 `json:"Id"`
+	Name         string              `json:"Name"`
+	Type         int                 `json:"Type"`
+	Editing      bool                `json:"editing"`
+	DatabaseType int                 `json:"databaseType"`
+	DetailParams SandBoxDetailParams `json:"detailParams"`
+}
+
+type SandBoxDetailParams struct {
+	Code       string `json:"code"`
+	Id         int    `json:"id"`
+	ClassifyId int    `json:"classifyId"`
+}
+
+func sandboxClassifyHaveChild(allNode []*sandbox.SandboxClassifyItems, node *sandbox.SandboxClassifyItems) (childs []*sandbox.SandboxClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.SandboxClassifyId {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}
+
+func SandboxClassifyItemsMakeTree(allNode []*sandbox.SandboxClassifyItems, node *sandbox.SandboxClassifyItems) {
+
+	childs, _ := sandboxClassifyHaveChild(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {                           //查询子节点的子节点,并添加到子节点
+			_, has := sandboxClassifyHaveChild(allNode, v)
+			if has {
+				SandboxClassifyItemsMakeTree(allNode, v) //递归添加节点
+			} else {
+				childrenArr := make([]*sandbox.SandboxClassifyItems, 0)
+				v.Children = childrenArr
+			}
+		}
+	} else {
+		childrenArr := make([]*sandbox.SandboxClassifyItems, 0)
+		node.Children = childrenArr
+	}
+}
+
+func SandboxClassifyItemsMakeTreeV2(allNode []*sandbox.SandboxClassifyItems, node *sandbox.SandboxClassifyItems) {
+
+	childs, _ := sandboxClassifyHaveChildV2(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {                           //查询子节点的子节点,并添加到子节点
+			_, has := sandboxClassifyHaveChildV2(allNode, v)
+			if has {
+				SandboxClassifyItemsMakeTreeV2(allNode, v) //递归添加节点
+			}
+		}
+	}
+}
+
+func sandboxClassifyHaveChildV2(allNode []*sandbox.SandboxClassifyItems, node *sandbox.SandboxClassifyItems) (childs []*sandbox.SandboxClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.SandboxClassifyId && node.SandboxId == 0 {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}