浏览代码

萝卜投研接口

hsun 3 周之前
父节点
当前提交
6f70667c32

+ 1089 - 0
controllers/data_manage/base_from_radish_research.go

@@ -0,0 +1,1089 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	dataSourceModel "eta/eta_api/models/data_source"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	"eta/eta_api/services/elastic"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// BaseFromRadishResearchController 萝卜投研
+type BaseFromRadishResearchController struct {
+	controllers.BaseAuthController
+}
+
+// IndexPageList
+// @Title 指标列表-分页
+// @Description 指标列表-分页
+// @Success 200 {object} data_manage.RadishResearchIndexPageListResp
+// @router /radish_research/index/page_list [get]
+func (this *BaseFromRadishResearchController) IndexPageList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var params data_manage.RadishResearchIndexListForm
+	if e := this.ParseForm(&params); e != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, err: %v", e)
+		return
+	}
+	resp := new(data_manage.RadishResearchIndexPageListResp)
+	resp.List = make([]*data_manage.BaseFromRadishResearchIndexItem, 0)
+
+	// 分页查询
+	var startSize int
+	if params.PageSize <= 0 {
+		params.PageSize = utils.PageSize20
+	}
+	if params.CurrentIndex <= 0 {
+		params.CurrentIndex = 1
+	}
+	startSize = utils.StartIndex(params.CurrentIndex, params.PageSize)
+
+	// 筛选项
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	var (
+		cond        string
+		pars        []interface{}
+		classifyIds []int
+	)
+	params.ClassifyIds = strings.TrimSpace(params.ClassifyIds)
+	params.Frequencies = strings.TrimSpace(params.Frequencies)
+	params.Keyword = strings.TrimSpace(params.Keyword)
+	if params.ClassifyIds != "" {
+		idsArr := strings.Split(params.ClassifyIds, ",")
+		for _, v := range idsArr {
+			id, _ := strconv.Atoi(v)
+			if id > 0 {
+				classifyIds = append(classifyIds, id)
+			}
+		}
+		if len(classifyIds) > 0 {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().ClassifyId)
+			pars = append(pars, classifyIds)
+		}
+	}
+	if params.Frequencies != "" {
+		freArr := strings.Split(params.Frequencies, ",")
+		if len(freArr) > 0 {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().Frequency)
+			pars = append(pars, freArr)
+		}
+	}
+	if params.Keyword != "" {
+		kw := fmt.Sprint("%", params.Keyword, "%")
+		cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, indexOb.Cols().IndexName, indexOb.Cols().IndexCode)
+		pars = append(pars, kw, kw)
+	}
+	if params.IgnoreEdbExist {
+		cond += fmt.Sprintf(` AND %s = 0`, indexOb.Cols().EdbExist)
+	}
+
+	// 列表合计
+	total, e := indexOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标总数失败, %v", e)
+		return
+	}
+	if total <= 0 {
+		page := paging.GetPaging(params.CurrentIndex, params.PageSize, 0)
+		resp.Paging = page
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	items, e := indexOb.GetPageItemsByCondition(cond, pars, []string{}, "", startSize, params.PageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标列表失败, %v", e)
+		return
+	}
+	for _, v := range items {
+		t := v.Format2Item()
+		resp.List = append(resp.List, t)
+	}
+
+	page := paging.GetPaging(params.CurrentIndex, params.PageSize, total)
+	resp.Paging = page
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// IndexDetail
+// @Title 指标详情
+// @Description 指标详情
+// @Param   IndexId  query  string  true  "指标ID"
+// @Success 200 {object} data_manage.BaseFromRadishResearchIndexDetail
+// @router /radish_research/index/detail [get]
+func (this *BaseFromRadishResearchController) IndexDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	indexId, _ := this.GetInt("IndexId")
+	if indexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", indexId)
+		return
+	}
+	resp := new(data_manage.BaseFromRadishResearchIndexDetail)
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	item, e := indexOb.GetItemById(indexId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if item != nil && item.BaseFromRadishResearchIndexId <= 0 {
+		br.Msg = "指标不存在, 请刷新页面"
+		return
+	}
+	resp.BaseFromRadishResearchIndexItem = item.Format2Item()
+	resp.DataList = make([]*data_manage.BaseFromRadishResearchDataItem, 0)
+
+	dataOb := new(data_manage.BaseFromRadishResearchData)
+	cond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
+	pars := make([]interface{}, 0)
+	pars = append(pars, item.IndexCode)
+	dataList, e := dataOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标数据失败, %v", e)
+		return
+	}
+	for _, v := range dataList {
+		resp.DataList = append(resp.DataList, v.Format2Item())
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// IndexEdit
+// @Title 编辑指标
+// @Description 编辑指标
+// @Success 200 {object} data_manage.RadishResearchIndexEditReq
+// @router /radish_research/index/edit [post]
+func (this *BaseFromRadishResearchController) IndexEdit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 data_manage.RadishResearchIndexEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", req.IndexId)
+		return
+	}
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	item, e := indexOb.GetItemById(req.IndexId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if item != nil && item.BaseFromRadishResearchIndexId <= 0 {
+		br.Msg = "指标不存在, 请刷新页面"
+		return
+	}
+
+	// 更新指标(这里不多限制,允许移到未分类)
+	item.ClassifyId = req.ClassifyId
+	item.ModifyTime = time.Now().Local()
+	updateCols := []string{indexOb.Cols().ClassifyId, indexOb.Cols().ModifyTime}
+	if e = item.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新指标分类失败, %v", e)
+		return
+	}
+
+	// 更新ES
+	go func() {
+		indexItem := new(dataSourceModel.SearchDataSource)
+		indexItem.PrimaryId = item.BaseFromRadishResearchIndexId
+		indexItem.IndexCode = item.IndexCode
+		indexItem.IndexName = item.IndexName
+		indexItem.ClassifyId = item.ClassifyId
+		indexItem.Unit = item.Unit
+		indexItem.Frequency = item.Frequency
+		indexItem.StartDate = item.StartDate.Format(utils.FormatDate)
+		indexItem.EndDate = item.EndDate.Format(utils.FormatDate)
+		indexItem.LatestValue = fmt.Sprint(item.LatestValue)
+		indexItem.Source = utils.DATA_SOURCE_RADISH_RESEARCH
+		indexItem.SourceName = utils.DATA_SOURCE_NAME_RADISH_RESEARCH
+		indexItem.IsDeleted = 0
+		indexItem.CreateTime = item.CreateTime.Format(utils.FormatDateTime)
+		indexItem.ModifyTime = item.ModifyTime.Format(utils.FormatDateTime)
+
+		docId := fmt.Sprintf("%d-%d", utils.DATA_SOURCE_RADISH_RESEARCH, item.BaseFromRadishResearchIndexId)
+		if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+			utils.FileLog.Warning("RadishResearch-写入指标ES失败, %v", e)
+			return
+		}
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// IndexRemove
+// @Title 删除指标
+// @Description 删除指标
+// @Success 200 {object} data_manage.RadishResearchIndexRemoveReq
+// @router /radish_research/index/remove [post]
+func (this *BaseFromRadishResearchController) IndexRemove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 data_manage.RadishResearchIndexRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", req.IndexId)
+		return
+	}
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	item, e := indexOb.GetItemById(req.IndexId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if item != nil && item.BaseFromRadishResearchIndexId <= 0 {
+		br.Msg = "指标不存在, 请刷新页面"
+		return
+	}
+	if item.EdbExist == 1 {
+		br.Msg = "指标已被引用, 不允许删除"
+		return
+	}
+	if e = indexOb.RemoveIndexAndData(item.IndexCode); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("删除指标和数据失败, %v", e)
+		return
+	}
+
+	// 更新ES
+	go func() {
+		indexItem := new(dataSourceModel.SearchDataSource)
+		indexItem.PrimaryId = item.BaseFromRadishResearchIndexId
+		indexItem.IndexCode = item.IndexCode
+		indexItem.IndexName = item.IndexName
+		indexItem.ClassifyId = item.ClassifyId
+		indexItem.Unit = item.Unit
+		indexItem.Frequency = item.Frequency
+		indexItem.StartDate = item.StartDate.Format(utils.FormatDate)
+		indexItem.EndDate = item.EndDate.Format(utils.FormatDate)
+		indexItem.LatestValue = fmt.Sprint(item.LatestValue)
+		indexItem.Source = utils.DATA_SOURCE_RADISH_RESEARCH
+		indexItem.SourceName = utils.DATA_SOURCE_NAME_RADISH_RESEARCH
+		indexItem.IsDeleted = 1 // 标记已删除
+		indexItem.CreateTime = item.CreateTime.Format(utils.FormatDateTime)
+		indexItem.ModifyTime = item.ModifyTime.Format(utils.FormatDateTime)
+
+		docId := fmt.Sprintf("%d-%d", utils.DATA_SOURCE_RADISH_RESEARCH, item.BaseFromRadishResearchIndexId)
+		if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+			utils.FileLog.Warning("RadishResearch-写入指标ES失败, %v", e)
+			return
+		}
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// IndexExport
+// @Title 导出指标数据
+// @Description 导出指标数据
+// @Param   ClassifyId  query  int  false  "分类Id"
+// @Success 200  导出成功
+// @router /radish_research/index/export [get]
+func (this *BaseFromRadishResearchController) IndexExport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	classifyId, _ := this.GetInt("ClassifyId")
+
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downFile := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	// 获取指标数据
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	cond := fmt.Sprintf(` AND %s = ?`, indexOb.Cols().ClassifyId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, classifyId)
+	indexes, e := indexOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC, %s ASC", indexOb.Cols().Sort, indexOb.Cols().PrimaryId))
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if len(indexes) == 0 {
+		// 无数据返回空文件
+		if e := xlsxFile.Save(downFile); e != nil {
+			sheet, e := xlsxFile.AddSheet("无数据")
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("新增Sheet失败, %v", e)
+				return
+			}
+			rowSecName := sheet.AddRow()
+			celSecName := rowSecName.AddCell()
+			celSecName.SetValue("")
+			if e = xlsxFile.Save(downFile); e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("保存文件失败, %v", e)
+				return
+			}
+		}
+		return
+	}
+
+	// 划分指标频度
+	frequencyIndex := make(map[string][]*data_manage.BaseFromRadishResearchIndex)
+	frequencyIndexIds := make(map[string][]int)
+	for _, v := range indexes {
+		if frequencyIndex[v.Frequency] == nil {
+			frequencyIndex[v.Frequency] = make([]*data_manage.BaseFromRadishResearchIndex, 0)
+		}
+		if frequencyIndexIds[v.Frequency] == nil {
+			frequencyIndexIds[v.Frequency] = make([]int, 0)
+		}
+		frequencyIndexIds[v.Frequency] = append(frequencyIndexIds[v.Frequency], v.BaseFromRadishResearchIndexId)
+		frequencyIndex[v.Frequency] = append(frequencyIndex[v.Frequency], v)
+	}
+
+	frequencyArr := []string{"日度", "周度", "旬度", "月度", "季度", "半年度", "年度"}
+	//frequencyMap := map[string]string{
+	//	"日度":  "Daily",
+	//	"周度":  "Weekly",
+	//	"旬度":  "ten-day",
+	//	"月度":  "Monthly",
+	//	"季度":  "Quarterly",
+	//	"半年度": "Semi-annual",
+	//	"年度":  "Annual",
+	//}
+	dataOb := new(data_manage.BaseFromRadishResearchData)
+	for _, frequency := range frequencyArr {
+		// 获取对应频度指标
+		secNameList := frequencyIndex[frequency]
+		if len(secNameList) == 0 {
+			continue
+		}
+
+		//sheetName := fmt.Sprintf("%s(%s)", frequency, frequencyMap[frequency])
+		sheetNew, e := xlsxFile.AddSheet(frequency)
+		if e != nil {
+			utils.FileLog.Warning(fmt.Sprintf("萝卜投研导出-AddSheet err: %v", e))
+			continue
+		}
+		secNameRow := sheetNew.AddRow()
+		frequencyRow := sheetNew.AddRow()
+		unitRow := sheetNew.AddRow()
+		updateTimeRow := sheetNew.AddRow()
+
+		// 指标日期序列
+		indexIds := frequencyIndexIds[frequency]
+		dataTimeList, e := dataOb.GetDataTimeByIndexIds(indexIds)
+		if e != nil {
+			utils.FileLog.Warning(fmt.Sprintf("萝卜投研导出-GetDataTimeByIndexIds err: %v", e))
+			continue
+		}
+
+		// 添加excel左侧指标日期
+		setRowIndex := 4
+		for rk, dv := range dataTimeList {
+			rowIndex := setRowIndex + rk
+			row := sheetNew.Row(rowIndex)
+			displayDate, _ := time.Parse(utils.FormatDate, dv)
+			displayDateCell := row.AddCell()
+			style := new(xlsx.Style)
+			style.ApplyAlignment = true
+			style.Alignment.WrapText = true
+			displayDateCell.SetStyle(style)
+			displayDateCell.SetDate(displayDate)
+		}
+		for k, sv := range secNameList {
+			// 获取数据
+			dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
+			dataPars := make([]interface{}, 0)
+			dataPars = append(dataPars, sv.IndexCode)
+			dataList, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
+			if e != nil {
+				utils.FileLog.Warning(fmt.Sprintf("萝卜投研导出-GetIndexDataByCondition err: %v", e))
+				continue
+			}
+
+			if k == 0 {
+				secNameRow.AddCell().SetValue("指标名称")
+				frequencyRow.AddCell().SetValue("频度")
+				unitRow.AddCell().SetValue("单位")
+				updateTimeRow.AddCell().SetValue("更新时间")
+				minCol := k * 3
+				sheetNew.SetColWidth(minCol, minCol, 15)
+			}
+			if len(dataList) == 0 {
+				continue
+			}
+			secNameRow.AddCell().SetValue(sv.IndexName)
+			frequencyRow.AddCell().SetValue(sv.Frequency)
+			unitRow.AddCell().SetValue(sv.Unit)
+
+			updateTimeRow.AddCell().SetValue(sv.ModifyTime)
+			dataInfoMap := make(map[string]*data_manage.BaseFromRadishResearchData)
+			for _, v := range dataList {
+				dt := v.DataTime.Format(utils.FormatDate)
+				dataInfoMap[dt] = v
+			}
+
+			for rk, dtv := range dataTimeList {
+				rowIndex := setRowIndex + rk
+				row := sheetNew.Row(rowIndex)
+				displayDateCell := row.AddCell()
+				tmpData, ok := dataInfoMap[dtv]
+				if ok {
+					displayDateCell.SetValue(tmpData.Value)
+				}
+			}
+		}
+	}
+
+	// 保存文件出错返回空文件
+	if e := xlsxFile.Save(downFile); e != nil {
+		sheet, e := xlsxFile.AddSheet("无数据")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("新增Sheet失败, %v", e)
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		if e = xlsxFile.Save(downFile); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("保存文件失败, %v", e)
+			return
+		}
+	}
+	fileName := fmt.Sprintf("%s%s%s", utils.DATA_SOURCE_NAME_RADISH_RESEARCH, time.Now().Format("06.01.02"), ".xlsx")
+	this.Ctx.Output.Download(downFile, fileName)
+	defer func() {
+		_ = os.Remove(downFile)
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+
+}
+
+// IndexSelect
+// @Title 批量加入指标库-选择指标
+// @Description 批量加入指标库-选择指标
+// @Success 200 {object} data_manage.BaseFromRadishResearchIndexItem
+// @router /radish_research/index/select [post]
+func (this *BaseFromRadishResearchController) IndexSelect() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 data_manage.RadishResearchIndexSelectReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	resp := make([]*data_manage.BaseFromRadishResearchIndexItem, 0)
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	var (
+		cond string
+		pars []interface{}
+	)
+	// 忽略已加入指标库的
+	cond += fmt.Sprintf(` AND %s = 0`, indexOb.Cols().EdbExist)
+	if len(req.ClassifyIds) > 0 {
+		cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().ClassifyId)
+		pars = append(pars, req.ClassifyIds)
+	}
+	if len(req.Frequencies) > 0 {
+		cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().Frequency)
+		pars = append(pars, req.Frequencies)
+	}
+	req.Keyword = strings.TrimSpace(req.Keyword)
+	if req.Keyword != "" {
+		kw := fmt.Sprint("%", req.Keyword, "%")
+		cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, indexOb.Cols().IndexName, indexOb.Cols().IndexCode)
+		pars = append(pars, kw, kw)
+	}
+	// 列表全选-SelectAll-true: IndexCodes为排除的指标, SelectAll-false: IndexCodes为选择的指标
+	if len(req.IndexCodes) > 0 {
+		if req.SelectAll {
+			cond += fmt.Sprintf(` AND %s NOT IN ?`, indexOb.Cols().IndexCode)
+		} else {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().IndexCode)
+		}
+		pars = append(pars, req.IndexCodes)
+	}
+
+	items, e := indexOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标列表失败, %v", e)
+		return
+	}
+	if len(items) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	for _, v := range items {
+		t := v.Format2Item()
+		resp = append(resp, t)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// EdbAdd
+// @Title 加入指标库
+// @Description 加入指标库
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /radish_research/edb/add [post]
+func (this *BaseFromRadishResearchController) EdbAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := fmt.Sprintf("CACHE_EDB_INFO_ADD_%d_%d", utils.DATA_SOURCE_RADISH_RESEARCH, sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	var req data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, %v", e)
+		return
+	}
+	req.EdbCode = strings.TrimSpace(req.EdbCode)
+	req.EdbName = strings.TrimSpace(req.EdbName)
+	if req.EdbCode == "" {
+		br.Msg = "指标编码不能为空"
+		return
+	}
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	// 是否加入过指标库
+	exist, e := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_RADISH_RESEARCH, req.EdbCode)
+	if e != nil && !utils.IsErrNoRow(e) {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("校验是否加入过指标库失败, %v", e)
+		return
+	}
+	if exist != nil && exist.EdbInfoId > 0 {
+		br.Msg = "指标库已存在,请刷新页面"
+		return
+	}
+
+	// 指标入库
+	edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(utils.DATA_SOURCE_RADISH_RESEARCH, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, req.StartDate, req.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	// 刷新指标数据
+	refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+		return
+	}
+	if refreshRes != nil && refreshRes.Ret != 200 {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+		return
+	}
+
+	// 更新指标EdbExist
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	if e := indexOb.UpdateEdbExists(1, []string{req.EdbCode}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新数据源EdbExist失败, %v", e)
+		return
+	}
+
+	//新增操作日志
+	{
+		edbLog := new(data_manage.EdbInfoLog)
+		edbLog.EdbInfoId = edbInfo.EdbInfoId
+		edbLog.SourceName = edbInfo.SourceName
+		edbLog.Source = edbInfo.Source
+		edbLog.EdbCode = edbInfo.EdbCode
+		edbLog.EdbName = edbInfo.EdbName
+		edbLog.ClassifyId = edbInfo.ClassifyId
+		edbLog.SysUserId = sysUser.AdminId
+		edbLog.SysUserRealName = sysUser.RealName
+		edbLog.CreateTime = time.Now()
+		edbLog.Content = string(this.Ctx.Input.RequestBody)
+		edbLog.Status = "新增指标"
+		edbLog.Method = this.Ctx.Input.URI()
+		go data_manage.AddEdbInfoLog(edbLog)
+	}
+
+	// 更新es
+	go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+
+	// 试用平台更新用户累计新增指标数
+	if utils.BusinessCode == utils.BusinessCodeSandbox {
+		go func() {
+			adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+			if e != nil {
+				return
+			}
+			if adminItem != nil && adminItem.AdminId <= 0 {
+				return
+			}
+			if adminItem.DepartmentName != "ETA试用客户" {
+				return
+			}
+
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserIndexNum(r)
+		}()
+	}
+
+	resp := new(data_manage.AddEdbInfoResp)
+	resp.EdbInfoId = edbInfo.EdbInfoId
+	resp.UniqueCode = edbInfo.UniqueCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// EdbNameCheck
+// @Title 批量加入指标库-重名校验
+// @Description 批量加入指标库-重名校验
+// @Success 200 {object} data_manage.EdbNameCheckResult
+// @router /radish_research/edb/name_check [post]
+func (this *BaseFromRadishResearchController) EdbNameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 []*data_manage.NameCheckEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req)
+	if codeLen > codeMaxT {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+
+	indexNames := make([]string, 0)
+	resp := make([]*data_manage.EdbNameCheckResult, 0)
+	nameCount := make(map[string]int)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		nameCount[v.EdbName] += 1
+
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &data_manage.EdbNameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+	}
+	// 本次提交的名称中也不允许重复
+	for _, v := range resp {
+		if nameCount[v.EdbName] > 1 {
+			v.Exist = true
+		}
+	}
+
+	// 重名校验
+	edbList, e := data_manage.GetEdbInfoByNameArr(indexNames, utils.EDB_INFO_TYPE)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名指标失败, Err: " + e.Error()
+		return
+	}
+	nameExists := make(map[string]bool)
+	for _, v := range edbList {
+		nameExists[v.EdbName] = true
+	}
+	if len(nameExists) > 0 {
+		for _, v := range resp {
+			v.Exist = nameExists[v.EdbName]
+		}
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// EdbMultiAdd
+// @Title 批量加入指标库
+// @Description 批量加入指标库
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/edb/multi_add [post]
+func (this *BaseFromRadishResearchController) EdbMultiAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := fmt.Sprintf("CACHE_EDB_INFO_BATCH_ADD_%d_%d", utils.DATA_SOURCE_RADISH_RESEARCH, sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		v.Frequency = strings.TrimSpace(v.Frequency)
+		if v.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		v.Unit = strings.TrimSpace(v.Unit)
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+	}
+
+	// 限定同一时间最多批量新增30个指标
+	var successCodes []string
+	for _, v := range req {
+		// 是否加入过指标库
+		exist, e := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_RADISH_RESEARCH, v.EdbCode)
+		if e != nil && !utils.IsErrNoRow(e) {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("校验是否加入过指标库失败, %v", e)
+			return
+		}
+		if exist != nil && exist.EdbInfoId > 0 {
+			// 加入过指标库这里直接忽略掉
+			continue
+		}
+
+		// 指标入库
+		edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(utils.DATA_SOURCE_RADISH_RESEARCH, utils.DATA_SUB_SOURCE_EDB, v.ClassifyId, v.EdbCode, v.EdbName, v.Frequency, v.Unit, v.StartDate, v.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+		if err != nil {
+			br.Msg = "保存失败"
+			if errMsg != `` {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = err.Error()
+			br.IsSendEmail = isSendEmail
+			return
+		}
+		successCodes = append(successCodes, v.EdbCode)
+
+		// 刷新指标数据
+		refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+			return
+		}
+		if refreshRes != nil && refreshRes.Ret != 200 {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+			return
+		}
+
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(this.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = this.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+
+		// 更新es
+		go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+	}
+
+	// 更新指标EdbExist
+	if len(successCodes) > 0 {
+		indexOb := new(data_manage.BaseFromRadishResearchIndex)
+		if e := indexOb.UpdateEdbExists(1, successCodes); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("更新数据源EdbExist失败, %v", e)
+			return
+		}
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}

+ 518 - 0
controllers/data_manage/base_from_radish_research_classify.go

@@ -0,0 +1,518 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/data"
+	"eta/eta_api/utils"
+	"fmt"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ClassifyList
+// @Title 分类列表-含指标
+// @Description 分类列表-含指标
+// @Param   ParentId  query  int  false  "父级ID"
+// @Success 200 {object} data_manage.RadishResearchSearchEdbResp
+// @router /radish_research/classify/list [get]
+func (this *BaseFromRadishResearchController) ClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	parentId, _ := this.GetInt("ParentId", -1)
+
+	resp := make([]*data_manage.BaseFromRadishResearchClassifyListItem, 0)
+
+	// 默认查询未分类及一级分类
+	var classifyId int
+	if parentId == -1 {
+		resp = append(resp, &data_manage.BaseFromRadishResearchClassifyListItem{
+			ClassifyName: "未分类",
+			Level:        1,
+			Sort:         -100,
+			UniqueCode:   "unclassified",
+		})
+	}
+	// 查询未分类下的指标
+	if parentId == 0 {
+		classifyId = -1
+	}
+	if parentId > 0 {
+		classifyId = parentId
+	}
+
+	// 查询分类
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, classifyId)
+		list, e := classifyOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", classifyOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取子分类失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			resp = append(resp, &data_manage.BaseFromRadishResearchClassifyListItem{
+				ClassifyId:   v.BaseFromRadishResearchClassifyId,
+				ClassifyName: v.ClassifyName,
+				ParentId:     v.ParentId,
+				Level:        v.Level,
+				Sort:         v.Sort,
+				UniqueCode:   v.UniqueCode,
+			})
+		}
+	}
+
+	// 查询指标
+	if parentId > -1 {
+		indexOb := new(data_manage.BaseFromRadishResearchIndex)
+		{
+			cond := fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
+			pars := make([]interface{}, 0)
+			pars = append(pars, parentId)
+			list, e := indexOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", indexOb.Cols().Sort))
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取分类下指标失败, %v", e)
+				return
+			}
+			for _, v := range list {
+				resp = append(resp, &data_manage.BaseFromRadishResearchClassifyListItem{
+					NodeType:   1,
+					IndexId:    v.BaseFromRadishResearchIndexId,
+					IndexCode:  v.IndexCode,
+					IndexName:  v.IndexName,
+					ParentId:   parentId,
+					Sort:       v.Sort,
+					UniqueCode: v.IndexCode,
+				})
+			}
+		}
+	}
+	sort.Slice(resp, func(i, j int) bool {
+		return resp[i].Sort < resp[j].Sort
+	})
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ClassifyTree
+// @Title 分类树
+// @Description 分类树
+// @Success 200 {object} data_manage.RadishResearchSearchEdbResp
+// @router /radish_research/classify/tree [get]
+func (this *BaseFromRadishResearchController) ClassifyTree() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// 获取所有分类
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	list, e := classifyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC, %s ASC", classifyOb.Cols().ParentId, classifyOb.Cols().Sort))
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取分类列表失败, %v", e)
+		return
+	}
+	items := make([]*data_manage.BaseFromRadishResearchClassifyItem, 0)
+	for _, v := range list {
+		items = append(items, v.Format2Item())
+	}
+	tree := data.GetRadishResearchClassifyTreeRecursive(items, 0)
+
+	// 未分类置顶
+	resp := make([]*data_manage.BaseFromRadishResearchClassifyItem, 0)
+	resp = append(resp, &data_manage.BaseFromRadishResearchClassifyItem{
+		ClassifyName: "未分类",
+		Level:        1,
+		Sort:         -100,
+		UniqueCode:   "unclassified",
+	})
+	resp = append(resp, tree...)
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ClassifyAdd
+// @Title 新增分类
+// @Description 新增分类
+// @Param	request	body data_manage.RadishResearchClassifyAddReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/add [post]
+func (this *BaseFromRadishResearchController) ClassifyAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 data_manage.RadishResearchClassifyAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "请选择上级分类"
+		return
+	}
+	if req.Level > 6 {
+		br.Msg = "目前只支持6级目录"
+		return
+	}
+
+	// 校验分类名称
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ? AND %s = ?", classifyOb.Cols().ParentId, classifyOb.Cols().ClassifyName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ParentId, req.ClassifyName)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类名称重复数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在"
+			return
+		}
+	}
+
+	// 层级路径
+	var levelPath string
+	var rootId int
+	if req.ParentId > 0 {
+		parent, e := classifyOb.GetItemById(req.ParentId)
+		if e != nil {
+			br.Msg = "上级分类有误"
+			br.ErrMsg = fmt.Sprintf("获取上级分类失败, %v", e)
+			return
+		}
+		levelPath = parent.LevelPath
+		rootId = parent.RootId
+	}
+
+	sortMax, e := classifyOb.GetSortMax(req.ParentId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类最大排序失败, %v", e)
+		return
+	}
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	classifyOb.ParentId = req.ParentId
+	classifyOb.ClassifyName = req.ClassifyName
+	classifyOb.Level = req.Level + 1
+	classifyOb.Sort = sortMax + 1
+	classifyOb.SysUserId = sysUser.AdminId
+	classifyOb.SysUserRealName = sysUser.RealName
+	classifyOb.UniqueCode = utils.MD5(classifyOb.TableName() + "_" + timestamp)
+	classifyOb.CreateTime = time.Now().Local()
+	classifyOb.ModifyTime = time.Now().Local()
+	if e = classifyOb.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("新增分类失败, %v", e)
+		return
+	}
+	if req.ParentId > 0 {
+		// 用英文逗号拼接方便查询
+		classifyOb.LevelPath = fmt.Sprintf("%s,%d", levelPath, classifyOb.BaseFromRadishResearchClassifyId)
+		classifyOb.RootId = rootId
+	} else {
+		classifyOb.LevelPath = fmt.Sprint(classifyOb.BaseFromRadishResearchClassifyId)
+		classifyOb.RootId = classifyOb.BaseFromRadishResearchClassifyId
+	}
+	if e = classifyOb.Update([]string{classifyOb.Cols().LevelPath, classifyOb.Cols().RootId}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新分类失败, %v", e)
+		return
+	}
+
+	br.Data = classifyOb.BaseFromRadishResearchClassifyId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ClassifyEdit
+// @Title 编辑分类
+// @Description 编辑分类
+// @Param	request	body data_manage.RadishResearchClassifyEditReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/edit [post]
+func (this *BaseFromRadishResearchController) ClassifyEdit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 data_manage.RadishResearchClassifyEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId < 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	classifyItem, e := classifyOb.GetItemById(req.ClassifyId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "分类不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+		return
+	}
+	if classifyItem != nil && classifyItem.BaseFromRadishResearchClassifyId <= 0 {
+		br.Msg = "分类不存在, 请刷新页面"
+		return
+	}
+
+	// 校验分类名称
+	{
+		cond := fmt.Sprintf(" AND %s <> ? AND %s = ?", classifyOb.Cols().PrimaryId, classifyOb.Cols().ClassifyName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ClassifyId, req.ClassifyName)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类名称重复数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在"
+			return
+		}
+	}
+	classifyItem.ClassifyName = req.ClassifyName
+	classifyItem.ModifyTime = time.Now().Local()
+	updateCols := []string{classifyOb.Cols().ClassifyName, classifyOb.Cols().ModifyTime}
+	if e = classifyItem.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新分类失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ClassifyRemove
+// @Title 删除分类
+// @Description 删除分类
+// @Param	request	body data_manage.RadishResearchClassifyRemoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/remove [post]
+func (this *BaseFromRadishResearchController) ClassifyRemove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 data_manage.RadishResearchClassifyRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId < 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	classifyItem, e := classifyOb.GetItemById(req.ClassifyId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+		return
+	}
+	if classifyItem != nil && classifyItem.BaseFromRadishResearchClassifyId <= 0 {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 查询子分类
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ClassifyId)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取子分类数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "该分类下含有子分类, 不允许删除"
+			return
+		}
+	}
+
+	// 查询指标
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ClassifyId)
+		count, e := indexOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类下指标数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "该分类下含有指标, 不允许删除"
+			return
+		}
+	}
+
+	if e := classifyItem.Remove(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("删除分类失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ClassifyMove
+// @Title 移动分类
+// @Description 移动分类
+// @Param	request	body data_manage.BaseFromRadishResearchClassifyMoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/move [post]
+func (this *BaseFromRadishResearchController) ClassifyMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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 data_manage.BaseFromRadishResearchClassifyMoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId <= 0 && req.ItemId <= 0 {
+		br.Msg = "请选择分类或指标"
+		return
+	}
+
+	err, errMsg := data.RadishResearchMoveClassify(req, sysUser)
+	if errMsg != `` {
+		br.Msg = errMsg
+		br.ErrMsg = errMsg
+		if err != nil {
+			br.ErrMsg = err.Error()
+		} else {
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 1 - 1
controllers/data_manage/edb_info_refresh.go

@@ -368,7 +368,7 @@ func (c *EdbInfoController) GetEdbRefreshDefaultConfig() {
 	}
 
 	// 非有色的来源,频度不能为空
-	if source != utils.DATA_SOURCE_YS && frequency == `` {
+	if source != utils.DATA_SOURCE_YS && source != utils.DATA_SOURCE_RADISH_RESEARCH && frequency == `` {
 		br.Msg = "频度不能为空"
 		br.IsSendEmail = false
 		return

+ 24 - 0
controllers/data_source/data_source.go

@@ -260,6 +260,21 @@ func (c *DataSourceController) SearchByEs() {
 	//	}
 	//}
 
+	// 萝卜投研-LevelPath用作前端定位
+	levelPathMap := make(map[int]string)
+	if source == utils.DATA_SOURCE_RADISH_RESEARCH {
+		classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+		classifies, e := classifyOb.GetItemsByCondition("", make([]interface{}, 0), []string{classifyOb.Cols().PrimaryId, classifyOb.Cols().LevelPath}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取萝卜投研分类失败, %v", e)
+			return
+		}
+		for _, v := range classifies {
+			levelPathMap[v.BaseFromRadishResearchClassifyId] = v.LevelPath
+		}
+	}
+
 	for _, v := range listMap {
 		classifyId, ok := v[classifyIdKey].(int)
 		if !ok {
@@ -271,6 +286,15 @@ func (c *DataSourceController) SearchByEs() {
 		v["StartDate"] = utils.GormDateStrToDateStr(startDate)
 		endDate := v["EndDate"].(string)
 		v["EndDate"] = utils.GormDateStrToDateStr(endDate)
+
+		if source == utils.DATA_SOURCE_RADISH_RESEARCH {
+			// 未分类
+			if classifyId == 0 {
+				v["ClassifyLevelPath"] = "0"
+				continue
+			}
+			v["ClassifyLevelPath"] = levelPathMap[classifyId]
+		}
 	}
 
 	page := paging.GetPaging(currentIndex, pageSize, total)

+ 302 - 0
models/data_manage/base_from_radish_research_classify.go

@@ -0,0 +1,302 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// BaseFromRadishResearchClassify 萝卜投研原始数据分类表
+type BaseFromRadishResearchClassify struct {
+	BaseFromRadishResearchClassifyId int       `orm:"column(base_from_radish_research_classify_id);pk" gorm:"primaryKey"`
+	ClassifyName                     string    `description:"分类名称"`
+	ParentId                         int       `description:"父级id"`
+	Level                            int       `description:"层级"`
+	Sort                             int       `description:"排序字段,越小越靠前,默认值:10"`
+	SysUserId                        int       `description:"创建人id"`
+	SysUserRealName                  string    `description:"创建人姓名"`
+	LevelPath                        string    `description:"层级路径"`
+	RootId                           int       `description:"顶级分类ID"`
+	UniqueCode                       string    `description:"唯一编码"`
+	ModifyTime                       time.Time `description:"修改时间"`
+	CreateTime                       time.Time `description:"创建时间"`
+}
+
+func (m *BaseFromRadishResearchClassify) TableName() string {
+	return "base_from_radish_research_classify"
+}
+
+type BaseFromRadishResearchClassifyCols struct {
+	PrimaryId       string
+	ClassifyName    string
+	ParentId        string
+	SysUserId       string
+	SysUserRealName string
+	Level           string
+	Sort            string
+	RootId          string
+	LevelPath       string
+	UniqueCode      string
+	CreateTime      string
+	ModifyTime      string
+}
+
+func (m *BaseFromRadishResearchClassify) Cols() BaseFromRadishResearchClassifyCols {
+	return BaseFromRadishResearchClassifyCols{
+		PrimaryId:       "base_from_radish_research_classify_id",
+		ClassifyName:    "classify_name",
+		ParentId:        "parent_id",
+		SysUserId:       "sys_user_id",
+		SysUserRealName: "sys_user_real_name",
+		Level:           "level",
+		Sort:            "sort",
+		RootId:          "root_id",
+		LevelPath:       "level_path",
+		UniqueCode:      "unique_code",
+		CreateTime:      "create_time",
+		ModifyTime:      "modify_time",
+	}
+}
+
+func (m *BaseFromRadishResearchClassify) Create() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Create(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) CreateMulti(items []*BaseFromRadishResearchClassify) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) Update(cols []string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) Remove() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Exec(sql, m.BaseFromRadishResearchClassifyId).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, ids).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetItemById(id int) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// GetBaseFromRadishResearchClassify 获取所有分类
+func GetBaseFromRadishResearchClassify() (items []*BaseFromRadishResearchClassify, err error) {
+	sql := ` SELECT * FROM base_from_radish_research_classify ORDER BY parent_id ASC, sort ASC, base_from_radish_research_classify_id ASC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql).Find(&items).Error
+	return
+}
+
+// BaseFromRadishResearchClassifyItem 萝卜投研数据分类信息
+type BaseFromRadishResearchClassifyItem struct {
+	ClassifyId   int                                   `description:"分类ID"`
+	ClassifyName string                                `description:"分类名称"`
+	ParentId     int                                   `description:"父级id"`
+	Level        int                                   `description:"层级"`
+	Sort         int                                   `description:"排序字段"`
+	LevelPath    string                                `description:"层级路径"`
+	UniqueCode   string                                `description:"唯一编码"`
+	CreateTime   string                                `description:"创建时间"`
+	ModifyTime   string                                `description:"修改时间"`
+	Children     []*BaseFromRadishResearchClassifyItem `description:"子分类"`
+}
+
+func (m *BaseFromRadishResearchClassify) Format2Item() (item *BaseFromRadishResearchClassifyItem) {
+	item = new(BaseFromRadishResearchClassifyItem)
+	item.ClassifyId = m.BaseFromRadishResearchClassifyId
+	item.ClassifyName = m.ClassifyName
+	item.ParentId = m.ParentId
+	item.Level = m.Level
+	item.Sort = m.Sort
+	item.LevelPath = m.LevelPath
+	item.UniqueCode = m.UniqueCode
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+type RadishResearchClassifyAddReq struct {
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级ID, 第一级传0"`
+	Level        int    `description:"层级, 第一级传0, 其余传上一级的层级"`
+}
+
+type RadishResearchClassifyEditReq struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+}
+
+func (m *BaseFromRadishResearchClassify) GetSortMax(parentId int) (sort int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COALESCE(Max(%s), 0) AS sort FROM %s WHERE %s = ?`, m.Cols().Sort, m.TableName(), m.Cols().ParentId)
+	err = o.Raw(sql, parentId).Scan(&sort).Error
+	return
+}
+
+type RadishResearchClassifyRemoveReq struct {
+	ClassifyId int `description:"分类ID"`
+}
+
+type BaseFromRadishResearchClassifyListItem struct {
+	NodeType     int                                       `description:"节点类型: 0-分类; 1-指标"`
+	ClassifyId   int                                       `description:"分类ID"`
+	ClassifyName string                                    `description:"分类名称"`
+	IndexId      int                                       `description:"指标ID"`
+	IndexCode    string                                    `description:"指标编码"`
+	IndexName    string                                    `description:"指标名称"`
+	ParentId     int                                       `description:"父级ID"`
+	Level        int                                       `description:"层级"`
+	Sort         int                                       `description:"排序"`
+	UniqueCode   string                                    `description:"唯一编码, 指标的话用indexCode"`
+	Children     []*BaseFromRadishResearchClassifyListItem `description:"子分类"`
+}
+
+type BaseFromRadishResearchClassifyMoveReq struct {
+	ClassifyId       int `description:"分类ID"`
+	ParentClassifyId int `description:"父级分类ID"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类ID"`
+	NextClassifyId   int `description:"下一个兄弟节点分类ID"`
+	ItemId           int `description:"指标ID, 如果指标ID有值,则移动对象为指标,否则认为移动对象为分类"`
+	PrevItemId       int `description:"上一个指标ID"`
+	NextItemId       int `description:"下一个指标ID"`
+}
+
+func GetRadishResearchClassifyById(classifyId int) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT * FROM base_from_radish_research_classify WHERE base_from_radish_research_classify_id = ?`
+	err = o.Raw(sql, classifyId).First(&item).Error
+	return
+}
+
+func GetRadishResearchClassifyByRootIdLevel(rootId int, orderStr string) (items []*BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_classify WHERE root_id = ? `
+	if orderStr != "" {
+		sql += orderStr
+	} else {
+		sql += ` order by level desc, sort asc, base_from_radish_research_classify_id asc`
+	}
+	err = o.Raw(sql, rootId).Find(&items).Error
+	return
+}
+
+// UpdateRadishResearchClassifySortByParentId 根据父类id更新排序
+func UpdateRadishResearchClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` update base_from_radish_research_classify set sort = ` + updateSort + ` WHERE parent_id = ? AND sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( base_from_radish_research_classify_id > ` + fmt.Sprint(classifyId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
+	}
+	err = o.Exec(sql, parentId, nowSort).Error
+	return
+}
+
+// GetFirstRadishResearchClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func GetFirstRadishResearchClassifyByParentId(parentId int) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_classify WHERE parent_id = ? ORDER BY sort ASC,base_from_radish_research_classify_id ASC LIMIT 1`
+	err = o.Raw(sql, parentId).First(&item).Error
+	return
+}
+
+func UpdateRadishResearchClassifyChildByParentClassifyId(classifyIds []int, rootId int, levelStep int) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	var pars []interface{}
+	pars = append(pars, rootId, levelStep)
+	pars = append(pars, classifyIds)
+	// 更新相关联的二级分类的parentId,和classify_name_second
+	sql := `update base_from_radish_research_classify SET root_id = ?, level = level+? where base_from_radish_research_classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+// UpdateLevelPath 更新层级路径
+func (m *BaseFromRadishResearchClassify) UpdateLevelPath(classifyId int, levelPath string) (err error) {
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ? WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().LevelPath, m.Cols().PrimaryId)
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, levelPath, classifyId).Error
+	return
+}

+ 192 - 0
models/data_manage/base_from_radish_research_data.go

@@ -0,0 +1,192 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// BaseFromRadishResearchData 萝卜投研-指标数据
+type BaseFromRadishResearchData struct {
+	BaseFromRadishResearchDataId  int       `orm:"column(base_from_radish_research_data_id);pk" gorm:"primaryKey"`
+	BaseFromRadishResearchIndexId int       `description:"指标ID"`
+	IndexCode                     string    `description:"指标编码"`
+	DataTime                      time.Time `description:"数据日期"`
+	Value                         float64   `description:"数据值"`
+	CreateTime                    time.Time `description:"创建时间"`
+	ModifyTime                    time.Time `description:"修改时间"`
+	DataTimestamp                 int64     `description:"数据日期时间戳"`
+}
+
+func (m *BaseFromRadishResearchData) TableName() string {
+	return "base_from_radish_research_data"
+}
+
+type BaseFromRadishResearchDataCols struct {
+	PrimaryId                     string
+	BaseFromRadishResearchIndexId string
+	IndexCode                     string
+	DataTime                      string
+	Value                         string
+	UniqueCode                    string
+	CreateTime                    string
+	ModifyTime                    string
+	DataTimestamp                 string
+}
+
+func (m *BaseFromRadishResearchData) Cols() BaseFromRadishResearchDataCols {
+	return BaseFromRadishResearchDataCols{
+		PrimaryId:                     "base_from_radish_research_data_id",
+		BaseFromRadishResearchIndexId: "base_from_radish_research_index_id",
+		IndexCode:                     "index_code",
+		DataTime:                      "data_time",
+		Value:                         "value",
+		UniqueCode:                    "unique_code",
+		CreateTime:                    "create_time",
+		ModifyTime:                    "modify_time",
+		DataTimestamp:                 "data_timestamp",
+	}
+}
+
+func (m *BaseFromRadishResearchData) Create() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Create(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) CreateMulti(items []*BaseFromRadishResearchData) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) Update(cols []string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) Remove() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Exec(sql, m.BaseFromRadishResearchDataId).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, ids).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetItemById(id int) (item *BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// BaseFromRadishResearchDataItem 萝卜投研数据信息
+type BaseFromRadishResearchDataItem struct {
+	DataId        int     `description:"数据ID"`
+	IndexId       int     `description:"指标ID"`
+	IndexCode     string  `description:"指标编码"`
+	DataTime      string  `description:"数据日期"`
+	Value         float64 `description:"数据值"`
+	DataTimestamp int64   `description:"数据日期时间戳"`
+}
+
+func (m *BaseFromRadishResearchData) Format2Item() (item *BaseFromRadishResearchDataItem) {
+	item = new(BaseFromRadishResearchDataItem)
+	item.DataId = m.BaseFromRadishResearchDataId
+	item.IndexId = m.BaseFromRadishResearchIndexId
+	item.IndexCode = m.IndexCode
+	item.DataTime = utils.TimeTransferString(utils.FormatDate, m.DataTime)
+	item.Value = m.Value
+	item.DataTimestamp = m.DataTimestamp
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetDataTimeByIndexIds(indexIds []int) (items []string, err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT DISTINCT %s FROM %s WHERE %s IN ? ORDER BY %s DESC`, m.Cols().DataTime, m.TableName(), m.Cols().BaseFromRadishResearchIndexId, m.Cols().DataTime)
+	err = o.Raw(sql, indexIds).Find(&items).Error
+	if err != nil {
+		return
+	}
+	for i, v := range items {
+		items[i] = utils.GormDateStrToDateStr(v)
+	}
+	return
+}

+ 530 - 0
models/data_manage/base_from_radish_research_index.go

@@ -0,0 +1,530 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+// BaseFromRadishResearchIndex 萝卜投研数据
+type BaseFromRadishResearchIndex struct {
+	BaseFromRadishResearchIndexId int       `orm:"column(base_from_radish_research_index_id);pk" gorm:"primaryKey"`
+	IndexCode                     string    `description:"指标编码"`
+	IndexName                     string    `description:"指标名称"`
+	ClassifyId                    int       `description:"分类ID"`
+	Unit                          string    `description:"单位"`
+	Source                        string    `description:"数据来源"`
+	Frequency                     string    `description:"频度"`
+	StartDate                     time.Time `description:"开始日期"`
+	EndDate                       time.Time `description:"结束日期"`
+	Sort                          int       `description:"排序"`
+	IsStop                        int       `description:"是否停更:0-否;1-停更"`
+	TerminalCode                  string    `description:"所属终端编码"`
+	EdbExist                      int       `description:"指标库是否已添加指标:0-否;1-是"`
+	FilePath                      string    `description:"文件存储路径"`
+	LatestValue                   float64   `description:"数据最新值"`
+	CreateTime                    time.Time `description:"创建时间"`
+	ModifyTime                    time.Time `description:"修改时间"`
+}
+
+func (m *BaseFromRadishResearchIndex) TableName() string {
+	return "base_from_radish_research_index"
+}
+
+type BaseFromRadishResearchIndexCols struct {
+	PrimaryId    string
+	IndexCode    string
+	IndexName    string
+	ClassifyId   string
+	Unit         string
+	Source       string
+	Frequency    string
+	StartDate    string
+	EndDate      string
+	Sort         string
+	IsStop       string
+	TerminalCode string
+	EdbExist     string
+	FilePath     string
+	LatestValue  string
+	CreateTime   string
+	ModifyTime   string
+}
+
+func (m *BaseFromRadishResearchIndex) Cols() BaseFromRadishResearchIndexCols {
+	return BaseFromRadishResearchIndexCols{
+		PrimaryId:    "base_from_radish_research_index_id",
+		IndexCode:    "index_code",
+		IndexName:    "index_name",
+		ClassifyId:   "classify_id",
+		Unit:         "unit",
+		Source:       "source",
+		Frequency:    "frequency",
+		StartDate:    "start_date",
+		EndDate:      "end_date",
+		Sort:         "sort",
+		IsStop:       "is_stop",
+		EdbExist:     "edb_exist",
+		TerminalCode: "terminal_code",
+		FilePath:     "file_path",
+		LatestValue:  "latest_value",
+		CreateTime:   "create_time",
+		ModifyTime:   "modify_time",
+	}
+}
+
+func (m *BaseFromRadishResearchIndex) Create() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Create(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) CreateMulti(items []*BaseFromRadishResearchIndex) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) Update(cols []string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) Remove() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Exec(sql, m.BaseFromRadishResearchIndexId).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, ids).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetItemById(id int) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// BaseFromRadishResearchIndexItem 萝卜投研数据信息
+type BaseFromRadishResearchIndexItem struct {
+	IndexId     int     `description:"指标ID"`
+	ClassifyId  int     `description:"分类ID"`
+	IndexCode   string  `description:"指标编码"`
+	IndexName   string  `description:"指标名称"`
+	Unit        string  `description:"单位"`
+	Source      string  `description:"数据来源"`
+	Frequency   string  `description:"频度"`
+	StartDate   string  `description:"开始日期"`
+	EndDate     string  `description:"结束日期"`
+	Sort        int     `description:"排序"`
+	LatestValue float64 `description:"最新值"`
+	EdbExist    int     `description:"是否加入指标:0-未加入;1-已加入"`
+	CreateTime  string  `description:"创建时间"`
+	ModifyTime  string  `description:"修改时间"`
+}
+
+func (m *BaseFromRadishResearchIndex) Format2Item() (item *BaseFromRadishResearchIndexItem) {
+	item = new(BaseFromRadishResearchIndexItem)
+	item.ClassifyId = m.ClassifyId
+	item.IndexId = m.BaseFromRadishResearchIndexId
+	item.IndexCode = m.IndexCode
+	item.IndexName = m.IndexName
+	item.Unit = m.Unit
+	item.Source = m.Source
+	item.Frequency = m.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, m.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, m.EndDate)
+	item.Sort = m.Sort
+	item.LatestValue = m.LatestValue
+	item.EdbExist = m.EdbExist
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+// BaseFromRadishResearchIndexDetail 指标详情
+type BaseFromRadishResearchIndexDetail struct {
+	*BaseFromRadishResearchIndexItem
+	DataList []*BaseFromRadishResearchDataItem
+}
+
+func (m *BaseFromRadishResearchIndex) UpdateClassifyMulti(ids []int, classifyId int) (err error) {
+	if len(ids) == 0 || classifyId <= 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ?, %s = NOW() WHERE %s IN (%s)`, m.TableName(), m.Cols().ClassifyId, m.Cols().ModifyTime, m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, classifyId, ids).Error
+	return
+}
+
+// RadishResearchSearchEdbReq 搜索指标请求体
+//type RadishResearchSearchEdbReq struct {
+//	StockCode string `form:"StockCode" description:"证券代码" `
+//	EdbCode   string `form:"EdbCode" description:"指标代码"`
+//	StartTime string `form:"StartTime" description:"每日数据开始时间"`
+//	EndTime   string `form:"EndTime" description:"每日数据结束时间"`
+//	Interval  int    `form:"Interval" description:"时间周期"`
+//	Fill      string `form:"Fill" description:"非交易间隔处理"`
+//	CPS       string `form:"CPS" description:"复权方式"`
+//	BaseDate  string `form:"BaseDate" description:"复权基点"`
+//}
+
+// RadishResearchExistCheckResp 指标存在校验响应
+//type RadishResearchExistCheckResp struct {
+//	ExistAll   bool                            `description:"指标是否全部存在: true-是; false-否"`
+//	ExistIndex []RadishResearchExistCheckIndex `description:"已存在的指标信息"`
+//}
+
+// RadishResearchExistCheckIndex 指标存在校验-指标信息
+//type RadishResearchExistCheckIndex struct {
+//	IndexId   int    `description:"指标ID"`
+//	IndexCode string `description:"指标编码"`
+//	IndexName string `description:"指标名称"`
+//}
+
+// RadishResearchSearchEdbResp 响应体
+//type RadishResearchSearchEdbResp struct {
+//	StockCode string                        `description:"证券代码" `
+//	EdbCode   string                        `description:"指标代码"`
+//	IndexName string                        `description:"指标名称"`
+//	Frequency int                           `description:"频度: 1-60"`
+//	IndexData []RadishResearchSearchEdbData `description:"指标数据"`
+//}
+
+//type RadishResearchSearchEdbData struct {
+//	DataTime string
+//	Value    string
+//}
+
+// RadishResearchIndexWithData 萝卜投研指标
+//type RadishResearchIndexWithData struct {
+//	StockCode string                     `description:"证券代码"`
+//	EdbCode   string                     `description:"指标代码"`
+//	IndexData []*RadishResearchIndexData `description:"指标数据"`
+//}
+
+// RadishResearchIndexData 萝卜投研指标数据
+//type RadishResearchIndexData struct {
+//	DataTime time.Time `description:"数据时间"`
+//	Value    float64   `description:"数据值"`
+//}
+
+// RadishResearchIndexDataLibResp 萝卜投研指标-指标库响应
+//type RadishResearchIndexDataLibResp struct {
+//	Ret     int
+//	Msg     string
+//	ErrMsg  string
+//	ErrCode string
+//	Data    []*RadishResearchIndexWithData
+//	Success bool `description:"true 执行成功,false 执行失败"`
+//}
+
+// RadishResearchAddEdbReq 新增指标请求体
+//type RadishResearchAddEdbReq struct {
+//	StartTime string                            `description:"每日数据开始时间"`
+//	EndTime   string                            `description:"每日数据结束时间"`
+//	Interval  int                               `description:"时间周期"`
+//	Fill      string                            `description:"非交易间隔处理"`
+//	CPS       string                            `description:"复权方式"`
+//	BaseDate  string                            `description:"复权基点"`
+//	IndexList []*RadishResearchBaseAddIndexItem `description:"指标信息"`
+//}
+
+//type RadishResearchBaseAddIndexItem struct {
+//	ClassifyId int    `description:"分类ID"`
+//	Unit       string `description:"单位"`
+//	IndexName  string `description:"指标名称"`
+//	Frequency  string `description:"频度"`
+//	StockCode  string `description:"证券代码"`
+//	EdbCode    string `description:"指标代码"`
+//}
+
+//type RadishResearchBaseAddReq struct {
+//	StartTime                      string `description:"每日数据开始时间"`
+//	EndTime                        string `description:"每日数据结束时间"`
+//	Interval                       int    `description:"时间周期"`
+//	Fill                           string `description:"非交易间隔处理"`
+//	CPS                            string `description:"复权方式"`
+//	BaseDate                       string `description:"复权基点"`
+//	SysAdminId                     int    `description:"创建人ID"`
+//	SysAdminName                   string `description:"创建人姓名"`
+//	RadishResearchBaseAddIndexItem `description:"指标信息"`
+//}
+
+// RadishResearchIndexEditReq 编辑指标请求
+type RadishResearchIndexEditReq struct {
+	IndexId    int `description:"指标ID"`
+	ClassifyId int `description:"分类ID"`
+}
+
+// RadishResearchIndexRemoveReq 删除指标请求
+type RadishResearchIndexRemoveReq struct {
+	IndexId int `description:"指标ID"`
+}
+
+// RadishResearchIndexListChoiceReq 指标列表选择请求
+type RadishResearchIndexListChoiceReq struct {
+	ClassifyId   string `form:"ClassifyId" description:"分类ID(多选)"`
+	IncludeChild bool   `form:"IncludeChild" description:"是否包含子分类"`
+	Frequency    string `form:"Frequency" description:"频度(多选)"`
+	SysAdminId   string `form:"SysAdminId" description:"创建人ID(多选)"`
+	Keywords     string `form:"Keywords" description:"关键词: 指标ID/指标名称"`
+	ListIds      string `form:"ListIds" description:"列表选择项/排除项(全选为true时为排除项)"`
+	SelectAll    bool   `form:"SelectAll" description:"是否全选: true/false"`
+}
+
+// RadishResearchIndexListChoiceItem 指标列表选择响应
+type RadishResearchIndexListChoiceItem struct {
+	IndexId   int    `description:"指标ID"`
+	IndexCode string `description:"指标编码"`
+	IndexName string `description:"指标名称"`
+}
+
+// RadishResearchIndexMultiOptReq 指标批量操作请求
+type RadishResearchIndexMultiOptReq struct {
+	IndexIds       []int `description:"指标IDs"`
+	OptType        int   `description:"操作类型: 1-移动分类; 2-删除; 3-刷新"`
+	MoveClassifyId int   `description:"移动至分类ID"`
+	RefreshType    int   `description:"刷新类型: 1-最近6小时; 2-全部刷新"`
+}
+
+// RadishResearchIndexListForm 指标列表表单
+type RadishResearchIndexListForm struct {
+	PageSize       int    `form:"PageSize" description:"每页数据量"`
+	CurrentIndex   int    `form:"CurrentIndex" description:"页码"`
+	ClassifyIds    string `form:"ClassifyIds" description:"分类IDs"`
+	IgnoreEdbExist bool   `form:"IgnoreEdbExist" description:"忽略已加入指标库的"`
+	Frequencies    string `form:"Frequencies" description:"频度(多选)"`
+	Keyword        string `form:"Keyword" description:"关键词-指标ID/指标名称"`
+}
+
+type RadishResearchIndexPageListResp struct {
+	List   []*BaseFromRadishResearchIndexItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+func GetRadishResearchIndexById(indexId int) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_index WHERE base_from_radish_research_index_id = ?`
+	err = o.Raw(sql, indexId).First(&item).Error
+	return
+}
+
+// UpdateRadishResearchIndexSortByClassifyId 根据分类id更新排序
+func UpdateRadishResearchIndexSortByClassifyId(classifyId, nowSort int, prevEdbInfoId int, updateSort string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` update base_from_radish_research_index set sort = ` + updateSort + ` WHERE classify_id=?`
+	if prevEdbInfoId > 0 {
+		sql += ` AND ( sort > ? or ( base_from_radish_research_index_id > ` + fmt.Sprint(prevEdbInfoId) + ` and sort=` + fmt.Sprint(nowSort) + ` )) `
+	} else {
+		sql += ` AND ( sort > ? )`
+	}
+	err = o.Exec(sql, classifyId, nowSort).Error
+	return
+}
+
+// GetRadishResearchIndexMaxSortByClassifyId 获取分类下指标的最大的排序数
+func GetRadishResearchIndexMaxSortByClassifyId(classifyId int) (sort int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT COALESCE(MAX(sort), 0) AS sort FROM base_from_radish_research_index WHERE classify_id = ?`
+	err = o.Raw(sql, classifyId).Scan(&sort).Error
+	return
+}
+
+// GetFirstRadishResearchIndexByClassifyId 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstRadishResearchIndexByClassifyId(classifyId int) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_index WHERE classify_id = ? ORDER BY sort ASC,base_from_radish_research_index_id ASC LIMIT 1`
+	err = o.Raw(sql, classifyId).First(&item).Error
+	return
+}
+
+type RadishResearchIndexConvert2EdbRule struct {
+	ConvertType  int `description:"转换类型: 1-指定时间值; 2-区间计算值"`
+	ConvertFixed struct {
+		FixedDay  int    `description:"指定时间值日期: 1-当日; 2-前一日"`
+		FixedTime string `description:"指定时间值时点(HH:mm:ss)"`
+	} `description:"指定时间值"`
+	ConvertArea struct {
+		StartDay      int    `description:"起始时间日期: 1-当日; 2-前一日"`
+		StartTime     string `description:"起始时间时点(HH:mm:ss)"`
+		EndDay        int    `description:"截止时间日期: 1-当日; 2-前一日"`
+		EndTime       string `description:"截止时间时点(HH:mm:ss)"`
+		CalculateType int    `description:"计算类型: 1-区间均值; 2-最大值; 3-最小值"`
+	} `description:"区间计算值"`
+}
+
+// RadishResearchIndexMultiSave2EdbPreReq 批量添加指标库请求
+type RadishResearchIndexMultiSave2EdbPreReq struct {
+	ConvertRule RadishResearchIndexConvert2EdbRule
+	IndexIds    []int `description:"指标IDs"`
+}
+
+type RadishResearchIndexMultiSave2EdbReq struct {
+	ConvertRule RadishResearchIndexConvert2EdbRule
+	NewIndexes  []*RadishResearchIndexMultiSave2EdbPreItem `description:"新增指标"`
+}
+
+type RadishResearchIndexMultiSave2EdbLibReq struct {
+	ConvertRule RadishResearchIndexConvert2EdbRule
+	NewIndex    *RadishResearchIndexMultiSave2EdbPreItem `description:"新增指标"`
+}
+
+// RadishResearchIndexMultiSave2EdbPreItem 批量新增指标库信息
+type RadishResearchIndexMultiSave2EdbPreItem struct {
+	IndexId      int    `description:"指标ID"`
+	IndexCode    string `description:"指标编码"`
+	IndexName    string `description:"原指标名称"`
+	NewIndexName string `description:"新指标名称"`
+	StockCode    string `description:"证券代码"`
+	EdbCode      string `description:"指标代码"`
+	Unit         string `description:"单位"`
+	Frequency    string `description:"原频度"`
+	NewFrequency string `description:"新频度(固定日度)"`
+	ClassifyId   int    `description:"指标库分类ID"`
+	SysAdminId   int    `description:"创建人ID"`
+	SysAdminName string `description:"创建人姓名"`
+	Tips         string `description:"提示信息"`
+	ErrMsg       string `description:"错误信息"`
+}
+
+type RadishResearchIndexMultiSave2EdbResp struct {
+	Exist   []*RadishResearchIndexMultiSave2EdbPreItem `description:"已存在的指标"`
+	Success []*RadishResearchIndexMultiSave2EdbPreItem `description:"添加成功的指标"`
+	Fail    []*RadishResearchIndexMultiSave2EdbPreItem `description:"添加失败的指标"`
+}
+
+type RadishResearchIndexMultiOptResp struct {
+	Success []*RadishResearchIndexBaseInfo `description:"操作成功的指标"`
+	Fail    []*RadishResearchIndexBaseInfo `description:"操作失败的指标"`
+}
+
+type RadishResearchIndexBaseInfo struct {
+	IndexId   int    `description:"指标ID"`
+	IndexCode string `description:"指标编码"`
+	IndexName string `description:"指标名称"`
+}
+
+func (m *BaseFromRadishResearchIndex) RemoveIndexAndData(indexCode string) (err error) {
+	tx := global.DbMap[utils.DbNameIndex].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().IndexCode)
+	e := tx.Exec(sql, indexCode).Error
+	if e != nil {
+		err = fmt.Errorf("delete index err: %s", e.Error())
+		return
+	}
+
+	dataOb := new(BaseFromRadishResearchData)
+	sql = fmt.Sprintf(`DELETE FROM %s WHERE %s = ?`, dataOb.TableName(), dataOb.Cols().IndexCode)
+	e = tx.Exec(sql, indexCode).Error
+	if e != nil {
+		err = fmt.Errorf("delete index data err: %s", e.Error())
+		return
+	}
+	return
+}
+
+// RadishResearchIndexSelectReq 批量加入指标-选择指标
+type RadishResearchIndexSelectReq struct {
+	ClassifyIds []int    `description:"分类IDs"`
+	Frequencies []string `description:"频度"`
+	Keyword     string   `description:"关键词-指标ID/指标名称"`
+	IndexCodes  []string `description:"SelectAll-true: IndexCodes为排除的指标, SelectAll-false: IndexCodes为选择的指标"`
+	SelectAll   bool     `description:"列表全选"`
+}
+
+// UpdateEdbExists 更新是否加入指标库字段
+func (m *BaseFromRadishResearchIndex) UpdateEdbExists(exist int, indexCodes []string) (err error) {
+	if len(indexCodes) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ? WHERE %s IN ?`, m.TableName(), m.Cols().EdbExist, m.Cols().IndexCode)
+	err = o.Exec(sql, exist, indexCodes).Error
+	return
+}

+ 135 - 0
routers/commentsRouter.go

@@ -2914,6 +2914,141 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyAdd",
+            Router: `/radish_research/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyEdit",
+            Router: `/radish_research/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyList",
+            Router: `/radish_research/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyMove",
+            Router: `/radish_research/classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyRemove",
+            Router: `/radish_research/classify/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyTree",
+            Router: `/radish_research/classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "EdbAdd",
+            Router: `/radish_research/edb/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "EdbMultiAdd",
+            Router: `/radish_research/edb/multi_add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "EdbNameCheck",
+            Router: `/radish_research/edb/name_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexDetail",
+            Router: `/radish_research/index/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexEdit",
+            Router: `/radish_research/index/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexExport",
+            Router: `/radish_research/index/export`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexPageList",
+            Router: `/radish_research/index/page_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexRemove",
+            Router: `/radish_research/index/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexSelect",
+            Router: `/radish_research/index/select`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRzdIndexController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRzdIndexController"],
         beego.ControllerComments{
             Method: "RzdClassify",

+ 1 - 0
routers/router.go

@@ -189,6 +189,7 @@ func init() {
 				&data_manage.BaseFromRzdIndexController{},
 				&data_manage.ClarksonsDataController{},
 				&data_manage.BaseFromGprRiskController{},
+				&data_manage.BaseFromRadishResearchController{},
 			),
 		),
 		web.NSNamespace("/my_chart",

+ 611 - 0
services/data/base_from_radish_research_classify.go

@@ -0,0 +1,611 @@
+package data
+
+import (
+	"errors"
+	"eta/eta_api/models/data_manage"
+	dataSourceModel "eta/eta_api/models/data_source"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/elastic"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// GetRadishResearchClassifyTreeRecursive 递归获取分类树形结构
+func GetRadishResearchClassifyTreeRecursive(list []*data_manage.BaseFromRadishResearchClassifyItem, parentId int) []*data_manage.BaseFromRadishResearchClassifyItem {
+	res := make([]*data_manage.BaseFromRadishResearchClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			t := GetRadishResearchClassifyTreeRecursive(list, v.ClassifyId)
+			v.Children = nil // 这一步是方便前端组件判断null...
+			if len(t) > 0 {
+				v.Children = t
+			}
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+// RadishResearchMoveClassify 移动指标分类
+func RadishResearchMoveClassify(req data_manage.BaseFromRadishResearchClassifyMoveReq, sysUser *system.Admin) (err error, errMsg string) {
+	// req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId
+	classifyId := req.ClassifyId
+	parentClassifyId := req.ParentClassifyId
+	prevClassifyId := req.PrevClassifyId
+	nextClassifyId := req.NextClassifyId
+
+	itemId := req.ItemId
+	prevItemId := req.PrevItemId
+	nextItemId := req.NextItemId
+
+	//首先确定移动的对象是分类还是指标
+	//判断上一个节点是分类还是指标
+	//判断下一个节点是分类还是指标
+	//同时更新分类目录下的分类sort和指标sort
+	//更新当前移动的分类或者指标sort
+
+	var parentEdbClassifyInfo *data_manage.BaseFromRadishResearchClassify
+	if parentClassifyId > 0 {
+		parentEdbClassifyInfo, err = data_manage.GetRadishResearchClassifyById(parentClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上级分类信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		edbClassifyInfo *data_manage.BaseFromRadishResearchClassify
+		prevClassify    *data_manage.BaseFromRadishResearchClassify
+		nextClassify    *data_manage.BaseFromRadishResearchClassify
+
+		edbInfo     *data_manage.BaseFromRadishResearchIndex
+		prevEdbInfo *data_manage.BaseFromRadishResearchIndex
+		nextEdbInfo *data_manage.BaseFromRadishResearchIndex
+		prevSort    int
+		nextSort    int
+	)
+
+	// 移动对象为分类, 判断权限
+	if itemId == 0 {
+		edbClassifyInfo, err = data_manage.GetRadishResearchClassifyById(classifyId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				errMsg = "当前分类不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if parentClassifyId > 0 && parentEdbClassifyInfo.Level == 6 {
+			errMsg = "最高只支持添加6级分类"
+			err = errors.New(errMsg)
+			return
+		}
+		// 如果是移动目录, 那么校验一下父级目录下是否有重名目录
+		exists, e := data_manage.GetEdbClassifyByParentIdAndName(parentClassifyId, edbClassifyInfo.ClassifyName, classifyId)
+		if e != nil && !utils.IsErrNoRow(e) {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取父级分类下的同名分类失败, Err: %s", e.Error())
+			return
+		}
+		if exists != nil && exists.ClassifyId > 0 {
+			errMsg = "移动失败,分类名称已存在"
+			return
+		}
+
+	} else {
+		edbInfo, err = data_manage.GetRadishResearchIndexById(req.ItemId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				errMsg = "当前指标不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if edbInfo != nil && edbInfo.BaseFromRadishResearchIndexId <= 0 {
+			errMsg = "当前指标不存在"
+			err = errors.New("获取指标信息失败, Err:" + err.Error())
+			return
+		}
+		if parentClassifyId == 0 {
+			errMsg = "移动失败,指标必须挂在分类下"
+			err = errors.New(errMsg)
+			return
+		}
+
+		//// 移动权限校验
+		//button := GetEdbOpButton(sysUser, edbInfo.SysUserId, edbInfo.EdbType, edbInfo.EdbInfoType, haveOperaAuth)
+		//if !button.MoveButton {
+		//	errMsg = "无操作权限"
+		//	err = errors.New(errMsg)
+		//	return
+		//}
+	}
+
+	if prevClassifyId > 0 {
+		prevClassify, err = data_manage.GetRadishResearchClassifyById(prevClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevClassify.Sort
+	} else if prevItemId > 0 {
+		prevEdbInfo, err = data_manage.GetRadishResearchIndexById(prevItemId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevEdbInfo.Sort
+	}
+
+	if nextClassifyId > 0 {
+		//下一个兄弟节点
+		nextClassify, err = data_manage.GetRadishResearchClassifyById(nextClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextClassify.Sort
+	} else if nextItemId > 0 {
+		//下一个兄弟节点
+		nextEdbInfo, err = data_manage.GetRadishResearchIndexById(nextItemId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextEdbInfo.Sort
+	}
+
+	err, errMsg = radishResearchMoveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify, edbInfo, prevEdbInfo, nextEdbInfo, parentClassifyId, prevSort, nextSort)
+	return
+}
+
+// radishResearchMoveEdbClassify 移动指标分类
+func radishResearchMoveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify *data_manage.BaseFromRadishResearchClassify, edbInfo, prevEdbInfo, nextEdbInfo *data_manage.BaseFromRadishResearchIndex, parentClassifyId int, prevSort, nextSort int) (err error, errMsg string) {
+	updateCol := make([]string, 0)
+	var moveParent bool  // 是否移动分类的父级目录
+	var moveParentId int // 被改变父级分类的目录ID
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+
+	// 移动对象为分类, 判断分类是否存在
+	if edbClassifyInfo != nil {
+		oldParentId := edbClassifyInfo.ParentId
+		oldLevel := edbClassifyInfo.Level
+		var classifyIds []int
+		if oldParentId != parentClassifyId {
+			//更新子分类对应的level
+			childList, e, m := GetRadishResearchChildClassifyByClassifyId(edbClassifyInfo.BaseFromRadishResearchClassifyId)
+			if e != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询子分类失败,Err:" + e.Error() + m)
+				return
+			}
+
+			if len(childList) > 0 {
+				for _, v := range childList {
+					if v.BaseFromRadishResearchClassifyId == edbClassifyInfo.BaseFromRadishResearchClassifyId {
+						continue
+					}
+					classifyIds = append(classifyIds, v.BaseFromRadishResearchClassifyId)
+				}
+			}
+		}
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId != 0 {
+			if edbClassifyInfo.Level != parentEdbClassifyInfo.Level+1 { //禁止层级调整
+				errMsg = "移动失败"
+				err = errors.New("不支持目录层级变更")
+				return
+			}
+			edbClassifyInfo.ParentId = parentEdbClassifyInfo.BaseFromRadishResearchClassifyId
+			edbClassifyInfo.RootId = parentEdbClassifyInfo.RootId
+			edbClassifyInfo.Level = parentEdbClassifyInfo.Level + 1
+			edbClassifyInfo.LevelPath = fmt.Sprintf("%s,%d", parentEdbClassifyInfo.LevelPath, edbClassifyInfo.BaseFromRadishResearchClassifyId) // 注意更新层级路径
+			edbClassifyInfo.ModifyTime = time.Now()
+			// 更改层级路径
+			edbClassifyInfo.LevelPath = fmt.Sprintf("%s,%d", parentEdbClassifyInfo.LevelPath, edbClassifyInfo.BaseFromRadishResearchClassifyId)
+			updateCol = append(updateCol, "ParentId", "RootId", "Level", "LevelPath", "ModifyTime", "LevelPath")
+			moveParent = true
+			moveParentId = edbClassifyInfo.BaseFromRadishResearchClassifyId
+		} else if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId == 0 {
+			errMsg = "移动失败"
+			err = errors.New("不支持目录层级变更")
+			return
+		}
+
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbClassifyInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+
+					}
+				}
+			}
+
+			edbClassifyInfo.Sort = prevSort + 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevEdbInfo == nil && nextEdbInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = GetRadishResearchClassifyMaxSort(parentClassifyId)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			edbClassifyInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstRadishResearchClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && !utils.IsErrNoRow(tmpErr) {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, firstClassify.BaseFromRadishResearchClassifyId-1, 0, updateSortStr)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstRadishResearchIndexByClassifyId(parentClassifyId)
+				if tErr != nil && !utils.IsErrNoRow(tErr) {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, firstEdb.BaseFromRadishResearchIndexId-1, updateSortStr)
+					_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
+				}
+			}
+
+			edbClassifyInfo.Sort = 0 //那就是排在第一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = edbClassifyInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+			//更新对应分类的root_id和层级
+			if oldParentId != parentClassifyId {
+				if len(classifyIds) > 0 {
+					levelStep := edbClassifyInfo.Level - oldLevel
+					err = data_manage.UpdateRadishResearchClassifyChildByParentClassifyId(classifyIds, edbClassifyInfo.RootId, levelStep)
+					if err != nil {
+						errMsg = "移动失败"
+						err = errors.New("更新子分类失败,Err:" + err.Error())
+						return
+					}
+				}
+			}
+		}
+	} else {
+		if edbInfo == nil {
+			errMsg = "当前指标不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		//如果改变了分类,那么移动该指标数据
+		if edbInfo.ClassifyId != parentClassifyId {
+			edbInfo.ClassifyId = parentClassifyId
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().ClassifyId, indexOb.Cols().ModifyTime)
+		}
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+					}
+				}
+			}
+
+			edbInfo.Sort = prevSort + 1
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().Sort, indexOb.Cols().ModifyTime)
+		} else if prevClassify == nil && nextClassify == nil && prevEdbInfo == nil && nextEdbInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+			maxSort, err = classifyOb.GetSortMax(parentClassifyId)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			edbInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().Sort, indexOb.Cols().ModifyTime)
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstRadishResearchClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && !utils.IsErrNoRow(tmpErr) {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, firstClassify.BaseFromRadishResearchClassifyId-1, 0, updateSortStr)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstRadishResearchIndexByClassifyId(parentClassifyId)
+				if tErr != nil && !utils.IsErrNoRow(tErr) {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, firstEdb.BaseFromRadishResearchIndexId-1, updateSortStr)
+					_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
+				}
+			}
+
+			edbInfo.Sort = 0 //那就是排在第一位
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().Sort, indexOb.Cols().ModifyTime)
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = edbInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+
+			// 更新ES
+			go func() {
+				indexItem := new(dataSourceModel.SearchDataSource)
+				indexItem.PrimaryId = edbInfo.BaseFromRadishResearchIndexId
+				indexItem.IndexCode = edbInfo.IndexCode
+				indexItem.IndexName = edbInfo.IndexName
+				indexItem.ClassifyId = edbInfo.ClassifyId
+				indexItem.Unit = edbInfo.Unit
+				indexItem.Frequency = edbInfo.Frequency
+				indexItem.StartDate = edbInfo.StartDate.Format(utils.FormatDate)
+				indexItem.EndDate = edbInfo.EndDate.Format(utils.FormatDate)
+				indexItem.LatestValue = fmt.Sprint(edbInfo.LatestValue)
+				indexItem.Source = utils.DATA_SOURCE_RADISH_RESEARCH
+				indexItem.SourceName = utils.DATA_SOURCE_NAME_RADISH_RESEARCH
+				indexItem.IsDeleted = 0
+				indexItem.CreateTime = edbInfo.CreateTime.Format(utils.FormatDateTime)
+				indexItem.ModifyTime = edbInfo.ModifyTime.Format(utils.FormatDateTime)
+
+				docId := fmt.Sprintf("%d-%d", utils.DATA_SOURCE_RADISH_RESEARCH, edbInfo.BaseFromRadishResearchIndexId)
+				if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+					utils.FileLog.Warning("RadishResearch-写入指标ES失败, %v", e)
+					return
+				}
+			}()
+		}
+	}
+
+	// 更新子分类的LevelPath
+	if moveParent {
+		UpdateRadishResearchClassifyLevelPathRecursive(moveParentId)
+	}
+	return
+}
+
+func GetRadishResearchChildClassifyByClassifyId(targetClassifyId int) (targetList []*data_manage.BaseFromRadishResearchClassify, err error, errMsg string) {
+	//判断是否是挂在顶级目录下
+	targetClassify, err := data_manage.GetRadishResearchClassifyById(targetClassifyId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			errMsg = "当前分类不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		errMsg = "获取失败"
+		err = errors.New("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	orderStr := ` order by level asc, sort asc, base_from_radish_research_classify_id asc`
+	tmpList, err := data_manage.GetRadishResearchClassifyByRootIdLevel(targetClassify.RootId, orderStr)
+	if err != nil && !utils.IsErrNoRow(err) {
+		errMsg = "获取失败"
+		err = errors.New("获取数据失败,Err:" + err.Error())
+		return
+	}
+	idMap := make(map[int]struct{})
+	if len(tmpList) > 0 {
+		for _, v := range tmpList {
+			if v.BaseFromRadishResearchClassifyId == targetClassify.BaseFromRadishResearchClassifyId {
+				idMap[v.BaseFromRadishResearchClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ParentId]; ok {
+				idMap[v.BaseFromRadishResearchClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.BaseFromRadishResearchClassifyId]; ok {
+				targetItem := new(data_manage.BaseFromRadishResearchClassify)
+				targetItem.BaseFromRadishResearchClassifyId = v.BaseFromRadishResearchClassifyId
+				targetItem.ParentId = v.ParentId
+				targetItem.RootId = v.RootId
+				//targetItem.UniqueCode = v.UniqueCode
+				targetItem.Level = v.Level
+				targetItem.ClassifyName = v.ClassifyName
+				//targetItem.IsJoinPermission = v.IsJoinPermission
+				targetList = append(targetList, targetItem)
+			}
+		}
+	}
+
+	return
+}
+
+func GetRadishResearchClassifyMaxSort(parentId int) (maxSort int, err error) {
+	//获取该层级下最大的排序数
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	classifyMaxSort, err := classifyOb.GetSortMax(parentId)
+	if err != nil {
+		return
+	}
+	maxSort = classifyMaxSort
+	edbMaxSort, err := data_manage.GetRadishResearchIndexMaxSortByClassifyId(parentId)
+	if err != nil {
+		return
+	}
+	if maxSort < edbMaxSort {
+		maxSort = edbMaxSort
+	}
+	return
+}
+
+// UpdateRadishResearchClassifyLevelPathRecursive 分类父级ID变更后, 递归更新分类LevelPath
+func UpdateRadishResearchClassifyLevelPathRecursive(parentId int) {
+	fmt.Println("ParentId", parentId)
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	cond := fmt.Sprintf(` AND %s = ?`, classifyOb.Cols().ParentId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, parentId)
+	classifies, e := classifyOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		utils.FileLog.Warning("UpdateRadishResearchClassifyLevelPathRecursive, errMsg: %v", e)
+		return
+	}
+	if len(classifies) == 0 {
+		return
+	}
+
+	// 批量更新子分类的LevelPath
+	parentClassify, e := classifyOb.GetItemById(parentId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			return
+		}
+		utils.FileLog.Warning("UpdateRadishResearchClassifyLevelPathRecursive, parent errMsg: %v", e)
+		return
+	}
+	if parentClassify != nil && parentClassify.BaseFromRadishResearchClassifyId <= 0 {
+		return
+	}
+
+	// 继续递归处理
+	for _, v := range classifies {
+		e = classifyOb.UpdateLevelPath(v.BaseFromRadishResearchClassifyId, fmt.Sprintf("%s,%d", parentClassify.LevelPath, v.BaseFromRadishResearchClassifyId))
+		if e != nil {
+			utils.FileLog.Warning("UpdateRadishResearchClassifyLevelPathRecursive, update errMsg: %v", e)
+			continue
+		}
+		UpdateRadishResearchClassifyLevelPathRecursive(v.BaseFromRadishResearchClassifyId)
+	}
+	return
+}

+ 23 - 0
services/data/edb_info.go

@@ -3045,6 +3045,9 @@ func handleByDelEdbInfo(edbInfo *data_manage.EdbInfo) {
 		// 删除图表中的指标引用
 		_ = data_manage.DeleteEdbRelationByObjectId(edbInfo.EdbInfoId, utils.EDB_RELATION_PREDICT_EDB)
 	}
+
+	// 如果数据源表中有EdbExist字段,删除指标后字段要置为0
+	_ = ResetBaseFromIndexEdbExist(edbInfo.Source, edbInfo.EdbCode)
 }
 
 func EditBaseEdbInfo(req data_manage.EditEdbInfoReq, sysUser *system.Admin, lang, requestBody, uri string) (isSendEmail bool, err error, errMsg string) {
@@ -3268,3 +3271,23 @@ func GetMySteelSourceByEdbCode(edbCode string) (source int, item *data_manage.Ed
 
 	return
 }
+
+// ResetBaseFromIndexEdbExist 重置数据源指标表EdbExist
+func ResetBaseFromIndexEdbExist(source int, indexCode string) (err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("ResetBaseFromIndexEdbExist, Source: %d, IndexCode: %s, ErrMsg: %v", source, indexCode, err)
+			utils.FileLog.Warning(tips)
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}
+	}()
+	switch source {
+	case utils.DATA_SOURCE_RADISH_RESEARCH:
+		indexOb := new(data_manage.BaseFromRadishResearchIndex)
+		if e := indexOb.UpdateEdbExists(0, []string{indexCode}); e != nil {
+			err = fmt.Errorf("更新EdbExist失败, %v", e)
+			return
+		}
+	}
+	return
+}

+ 1 - 1
services/data/edb_info_refresh.go

@@ -72,7 +72,7 @@ func SaveEdbRefreshDefaultConfig(source, subSource int, frequency string, list [
 	}
 
 	// 非有色的来源,频度不能为空
-	if source != utils.DATA_SOURCE_YS && frequency == `` {
+	if source != utils.DATA_SOURCE_YS && source != utils.DATA_SOURCE_RADISH_RESEARCH && frequency == `` {
 		errMsg = "频度不能为空"
 		err = errors.New(errMsg)
 		isSendEmail = false

+ 6 - 1
utils/constants.go

@@ -193,7 +193,8 @@ const (
 	DATA_SOURCE_RESIDUAL_ANALYSIS                    = 99       // 残差分析
 	DATA_SOURCE_CLARKSONS                            = 101      // 克拉克森数据
 	DATA_SOURCE_GPR_RISK                             = 102      //GPR地缘风险指数
-	DATA_SOURCE_AI_PREDICT_MODEL                     = 103      // AI预测模型
+	DATA_SOURCE_AI_PREDICT_MODEL                     = 103      // AI预测模型(edb_source里实际不存在这个,ES里面的为103)
+	DATA_SOURCE_RADISH_RESEARCH                      = 105      // 萝卜投研
 )
 
 // 数据刷新频率
@@ -569,3 +570,7 @@ const (
 	DbNameAI          = "eta_ai"
 	DbNameWeekly      = "weekly_report"
 )
+
+const (
+	DATA_SOURCE_NAME_RADISH_RESEARCH = "萝卜投研" // 萝卜投研 -> 105
+)