Browse Source

Merge branch 'feature/eta_1.9.3' into custom

hsun 8 months ago
parent
commit
af0fb5b421
64 changed files with 9228 additions and 67 deletions
  1. 2 0
      cache/replace_edb_info.go
  2. 151 0
      controllers/business_conf.go
  3. 1705 0
      controllers/data_manage/base_from_ths_hf.go
  4. 490 0
      controllers/data_manage/base_from_ths_hf_classify.go
  5. 2 0
      controllers/data_manage/chart_classify.go
  6. 21 9
      controllers/data_manage/edb_info.go
  7. 1 1
      controllers/data_manage/edb_info_calculate.go
  8. 232 2
      controllers/data_manage/edb_info_refresh.go
  9. 298 0
      controllers/data_manage/edb_info_relation.go
  10. 6 1
      controllers/data_manage/excel/excel_info.go
  11. 6 0
      controllers/data_manage/future_good/future_good_chart_info.go
  12. 13 1
      controllers/data_manage/future_good/future_good_profit_chart_info.go
  13. 331 0
      controllers/data_manage/wind_data.go
  14. 67 3
      controllers/english_report/email.go
  15. 2 0
      controllers/fe_calendar/fe_calendar_matter.go
  16. 9 1
      controllers/sandbox/sandbox.go
  17. 33 0
      models/business_conf.go
  18. 231 0
      models/data_manage/base_from_business_data.go
  19. 162 0
      models/data_manage/base_from_edb_mapping.go
  20. 509 0
      models/data_manage/base_from_ths_hf.go
  21. 297 0
      models/data_manage/base_from_ths_hf_classify.go
  22. 180 0
      models/data_manage/base_from_ths_hf_data.go
  23. 11 0
      models/data_manage/chart_edb_mapping.go
  24. 77 2
      models/data_manage/chart_info.go
  25. 8 0
      models/data_manage/edb_classify.go
  26. 66 4
      models/data_manage/edb_data_base.go
  27. 102 1
      models/data_manage/edb_data_insert_config.go
  28. 8 0
      models/data_manage/edb_data_mysteel_chemical.go
  29. 41 0
      models/data_manage/edb_data_wind.go
  30. 158 12
      models/data_manage/edb_info.go
  31. 11 0
      models/data_manage/edb_info_calculate_mapping.go
  32. 518 0
      models/data_manage/edb_info_relation.go
  33. 22 0
      models/data_manage/edb_refresh/request/edb_info_refresh.go
  34. 9 0
      models/data_manage/excel/excel_edb_mapping.go
  35. 85 0
      models/data_manage/mysteel_chemical_index.go
  36. 6 0
      models/data_manage/response/edb_info.go
  37. 18 1
      models/db.go
  38. 7 4
      models/english_report.go
  39. 38 1
      models/english_report_email_pv.go
  40. 5 2
      models/english_video.go
  41. 423 0
      models/mgo/base_from_ths_hf_data.go
  42. 510 0
      models/mgo/edb_data_ths_hf.go
  43. 36 0
      models/sandbox/sandbox.go
  44. 243 0
      routers/commentsRouter.go
  45. 2 0
      routers/router.go
  46. 12 9
      services/data/base_edb_lib.go
  47. 66 0
      services/data/base_from_business.go
  48. 302 0
      services/data/base_from_ths_hf.go
  49. 526 0
      services/data/base_from_ths_hf_classify.go
  50. 4 1
      services/data/chart_info.go
  51. 4 0
      services/data/correlation/chart_info.go
  52. 9 3
      services/data/cross_variety/chart.go
  53. 59 0
      services/data/edb_classify.go
  54. 48 1
      services/data/edb_data.go
  55. 17 3
      services/data/edb_info.go
  56. 680 0
      services/data/edb_info_relation.go
  57. 4 1
      services/data/excel/excel_op.go
  58. 5 0
      services/data/line_equation/chart_info.go
  59. 5 1
      services/data/line_feature/chart_info.go
  60. 274 0
      services/edb_info_replace.go
  61. 3 1
      services/eta_forum/eta_forum_hub.go
  62. 41 0
      services/sandbox/sandbox.go
  63. 4 0
      utils/config.go
  64. 13 2
      utils/constants.go

+ 2 - 0
cache/replace_edb_info.go

@@ -13,6 +13,8 @@ func AddReplaceEdbInfo(oldEdbInfo, newEdbInfo *data_manage.EdbInfo) bool {
 	record.NewEdbInfo = newEdbInfo
 	if utils.Re == nil {
 		err := utils.Rc.LPush(utils.CACHE_KEY_REPLACE_EDB, record)
+		
+		utils.FileLog.Info(fmt.Sprintf("指标替换 加入缓存 AddReplaceEdbInfo LPush: 旧指标ID:%d,新指标ID:%d", oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId))
 		if err != nil {
 			fmt.Println("AddReplaceEdbInfo LPush Err:" + err.Error())
 		}

+ 151 - 0
controllers/business_conf.go

@@ -279,3 +279,154 @@ func (this *BusinessConfOpenController) CodeEncrypt() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// SingleSave
+// @Title 保存单项配置
+// @Description 保存配置
+// @Param	request	body map[string]interface{} true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /single/save [post]
+func (this *BusinessConfController) SingleSave() {
+	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 models.BusinessConfSingleSaveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	if req.ConfKey == "" {
+		br.Msg = "参数异常!"
+		return
+	}
+
+	// 设置白名单,只有白名单里的配置才允许保存
+	writeList := []string{models.BusinessConfEdbStopRefreshRule}
+	if !utils.InArrayByStr(writeList, req.ConfKey) {
+		br.Msg = "不支持该项配置"
+		return
+	}
+	// 获取配置信息
+	confOb, e := models.GetBusinessConfByKey(req.ConfKey)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "配置不存在"
+			return
+		}
+		br.Msg = "保存失败"
+		br.ErrMsg = "获取配置失败, Err: " + e.Error()
+		return
+	}
+
+	switch confOb.ValType {
+	case 1: // 字符串
+		req.ConfVal = strings.TrimSpace(req.ConfVal)
+		if confOb.Necessary == 1 && req.ConfVal == "" {
+			br.Msg = confOb.Remark + "不可为空"
+			return
+		}
+	}
+	if req.ConfKey == models.BusinessConfEdbStopRefreshRule {
+		//将json转为结构体
+		rule := new(models.EdbStopRefreshRule)
+		err := json.Unmarshal([]byte(req.ConfVal), rule)
+		if err != nil {
+			br.Msg = confOb.Remark + "格式错误"
+			return
+		}
+		if rule.IsOpen == 1 && (rule.EdbStopDays == 0 || rule.BaseIndexStopDays == 0) {
+			br.Msg = confOb.Remark + "天数不可设置为0"
+			return
+		}
+	}
+	if confOb.ConfVal != req.ConfVal {
+		confOb.ConfVal = req.ConfVal
+		if e = confOb.Update([]string{"ConfVal"}); e != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存商家配置失败, Err: " + e.Error()
+			return
+		}
+		// 操作日志
+		go func() {
+			b, e := json.Marshal(req)
+			if e != nil {
+				return
+			}
+			recordOb := new(models.BusinessConfOperationRecord)
+			recordOb.SysUserId = sysUser.AdminId
+			recordOb.SysRealName = sysUser.RealName
+			recordOb.Content = string(b)
+			recordOb.CreateTime = time.Now().Local()
+			_ = recordOb.Create()
+		}()
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// GetSingle
+// @Title 获取单项配置
+// @Description 保存配置
+// @Param	request	body map[string]interface{} true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /single [get]
+func (this *BusinessConfController) GetSingle() {
+	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
+	}
+	confKey := this.GetString("ConfKey")
+	if confKey == "" {
+		br.Msg = "参数异常!"
+		return
+	}
+
+	// 设置白名单,只有白名单里的配置才允许保存
+	writeList := []string{models.BusinessConfEdbStopRefreshRule}
+	if !utils.InArrayByStr(writeList, confKey) {
+		br.Msg = "不支持该项配置"
+		return
+	}
+	// 获取配置信息
+	confOb, e := models.GetBusinessConfByKey(confKey)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "配置不存在"
+			return
+		}
+		br.Msg = "保存失败"
+		br.ErrMsg = "获取配置失败, Err: " + e.Error()
+		return
+	}
+	resp := models.BusinessConfSingleResp{ConfVal: confOb.ConfVal}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.Data = resp
+}

+ 1705 - 0
controllers/data_manage/base_from_ths_hf.go

@@ -0,0 +1,1705 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/mgo"
+	"eta/eta_api/services/data"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/shopspring/decimal"
+	"go.mongodb.org/mongo-driver/bson"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// BaseFromThsHfController 同花顺高频数据
+type BaseFromThsHfController struct {
+	controllers.BaseAuthController
+}
+
+// Search
+// @Title 新增指标-查询指标信息接口
+// @Description 新增指标-查询指标信息接口
+// @Param	request	body data_manage.ThsHfSearchEdbReq true "type json string"
+// @Success 200 {object} data_manage.ThsHfSearchEdbResp
+// @router /ths_hf/index/search [get]
+func (this *BaseFromThsHfController) Search() {
+	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.ThsHfSearchEdbReq
+	if e := this.ParseForm(&params); e != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, err: %v", e)
+		return
+	}
+	params.StockCode = strings.TrimSpace(params.StockCode)
+	if params.StockCode == "" {
+		br.Msg = "请输入证券代码"
+		return
+	}
+	stockCodes := strings.Split(params.StockCode, ",")
+	if len(stockCodes) == 0 {
+		br.Msg = "请输入证券代码"
+		return
+	}
+	if len(stockCodes) > 10 {
+		br.Msg = "最多输入10个证券代码"
+		return
+	}
+	params.EdbCode = strings.TrimSpace(params.EdbCode)
+	if params.EdbCode == "" {
+		br.Msg = "请输入指标代码"
+		return
+	}
+	edbCodes := strings.Split(params.EdbCode, ",")
+	if len(edbCodes) == 0 {
+		br.Msg = "请输入指标代码"
+		return
+	}
+	if len(edbCodes) > 20 {
+		br.Msg = "最多选择/输入20个指标代码"
+		return
+	}
+	if params.StartTime == "" {
+		br.Msg = "请选择起始时间"
+		return
+	}
+	_, e := time.ParseInLocation(utils.FormatDateTime, params.StartTime, time.Local)
+	if e != nil {
+		br.Msg = "起始时间格式有误"
+		br.ErrMsg = fmt.Sprintf("起始时间格式有误, %v", e)
+		return
+	}
+	// 结束时间选填, 不填则为当前时间
+	if params.EndTime != "" {
+		_, e := time.ParseInLocation(utils.FormatDateTime, params.EndTime, time.Local)
+		if e != nil {
+			br.Msg = "截止时间格式有误"
+			br.ErrMsg = fmt.Sprintf("截止时间格式有误, %v", e)
+			return
+		}
+	}
+	if params.EndTime == "" {
+		params.EndTime = time.Now().Local().Format(utils.FormatDateTime)
+	}
+	if !utils.InArrayByInt(data_manage.ThsHfPeriodArr, params.Interval) {
+		br.Msg = "时间周期有误"
+		br.ErrMsg = fmt.Sprintf("时间周期有误, Interval: %d", params.Interval)
+		return
+	}
+	if params.CPS != "" && !utils.InArrayByStr(data_manage.ThsHfCPSArr, params.CPS) {
+		br.Msg = "复权方式有误"
+		br.ErrMsg = fmt.Sprintf("复权方式有误, CPS: %s", params.CPS)
+		return
+	}
+	if params.BaseDate != "" {
+		_, e = time.ParseInLocation(utils.FormatDate, params.BaseDate, time.Local)
+		if e != nil {
+			br.Msg = "复权基点格式有误"
+			br.ErrMsg = fmt.Sprintf("复权基点格式有误, %v", e)
+			return
+		}
+	}
+	if params.Fill != "" && !utils.InArrayByStr(data_manage.ThsHfFillArr, params.Fill) {
+		br.Msg = "非交易间隔处理有误"
+		br.ErrMsg = fmt.Sprintf("非交易间隔处理有误, Fill: %s", params.Fill)
+		return
+	}
+
+	// 校验已入库的指标
+	checkResp, indexExists, e := data.CheckExistThsHfEdb(stockCodes, edbCodes)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("校验已存在的高频指标失败, %v", e)
+		return
+	}
+	if len(checkResp.ExistIndex) > 0 && checkResp.ExistAll {
+		br.Msg = "指标均已存在"
+		return
+	}
+
+	resp := make([]*data_manage.ThsHfSearchEdbResp, 0)
+
+	// 请求API获取数据
+	indexes, e := data.GetEdbDataThsHf(params)
+	if e != nil {
+		br.Msg = "未搜索到指标"
+		br.ErrMsg = fmt.Sprintf("获取高频指标失败, %v", e)
+		return
+	}
+	if len(indexes) == 0 {
+		br.Msg = "未搜索到指标"
+		return
+	}
+	for _, v := range indexes {
+		// 忽略掉校验出来的已入库指标
+		k := fmt.Sprintf("%s-%s", v.StockCode, v.EdbCode)
+		if indexExists[k] {
+			continue
+		}
+
+		// 默认指标名称
+		suffix := data_manage.ThsHfEdbCodeCn[v.EdbCode]
+		if suffix == "" {
+			suffix = v.EdbCode
+		}
+		indexName := fmt.Sprintf("%s%dm%s", v.StockCode, params.Interval, suffix)
+
+		item := new(data_manage.ThsHfSearchEdbResp)
+		item.StockCode = v.StockCode
+		item.EdbCode = v.EdbCode
+		item.IndexName = indexName
+		item.Frequency = params.Interval
+
+		// 搜索只展示100条数据
+		var limit int
+		sort.Slice(v.IndexData, func(i, j int) bool {
+			return v.IndexData[i].DataTime.After(v.IndexData[j].DataTime)
+		})
+		for _, d := range v.IndexData {
+			if limit > 100 {
+				break
+			}
+			limit += 1
+			strVal := decimal.NewFromFloat(d.Value).Round(4).String()
+			item.IndexData = append(item.IndexData, data_manage.ThsHfSearchEdbData{
+				DataTime: d.DataTime.Format(utils.FormatDateTime),
+				Value:    strVal,
+			})
+		}
+		resp = append(resp, item)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ExistCheck
+// @Title 新增指标-指标存在校验
+// @Description 新增指标-指标存在校验
+// @Param	request	body data_manage.ThsHfSearchEdbReq true "type json string"
+// @Success 200 {object} data_manage.ThsHfExistCheckResp
+// @router /ths_hf/index/exist_check [get]
+func (this *BaseFromThsHfController) ExistCheck() {
+	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.ThsHfSearchEdbReq
+	if e := this.ParseForm(&params); e != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, err: %v", e)
+		return
+	}
+	params.StockCode = strings.TrimSpace(params.StockCode)
+	if params.StockCode == "" {
+		br.Msg = "请输入证券代码"
+		return
+	}
+	stockCodes := strings.Split(params.StockCode, ",")
+	if len(stockCodes) == 0 {
+		br.Msg = "请输入证券代码"
+		return
+	}
+	params.EdbCode = strings.TrimSpace(params.EdbCode)
+	if params.EdbCode == "" {
+		br.Msg = "请输入指标代码"
+		return
+	}
+	edbCodes := strings.Split(params.EdbCode, ",")
+	if len(edbCodes) == 0 {
+		br.Msg = "请输入指标代码"
+		return
+	}
+
+	// 校验已存在的高频指标
+	resp, _, e := data.CheckExistThsHfEdb(stockCodes, edbCodes)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("校验已存在的高频指标失败, %v", e)
+		return
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// List
+// @Title 列表
+// @Description 列表
+// @Param	request	body data_manage.ThsHfIndexListForm true "type json string"
+// @Success 200 {object} data_manage.ThsHfIndexPageListResp
+// @router /ths_hf/index/list [get]
+func (this *BaseFromThsHfController) List() {
+	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.ThsHfIndexListForm
+	if e := this.ParseForm(&params); e != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, err: %v", e)
+		return
+	}
+	if params.SortField > 0 && !utils.InArrayByInt([]int{1, 2, 3, 4}, params.SortField) {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, SortField: %d", params.SortField)
+		return
+	}
+	if params.SortType > 0 && !utils.InArrayByInt([]int{1, 2}, params.SortType) {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, SortType: %d", params.SortType)
+		return
+	}
+	resp := new(data_manage.ThsHfIndexPageListResp)
+	resp.List = make([]*data_manage.BaseFromThsHfIndexItem, 0)
+
+	// 查询所有分类-分类查询/分类完整路径用
+	classifyOb := new(data_manage.BaseFromThsHfClassify)
+	//classifies := make([]*data_manage.BaseFromThsHfClassify, 0)
+	classifyIdItem := make(map[int]*data_manage.BaseFromThsHfClassify)
+	{
+		list, e := classifyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{classifyOb.Cols().PrimaryId, classifyOb.Cols().ClassifyName, classifyOb.Cols().ClassifyNameEn, classifyOb.Cols().LevelPath}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取分类列表失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			classifyIdItem[v.BaseFromThsHfClassifyId] = v
+		}
+		//classifies = list
+	}
+
+	// 筛选项
+	var (
+		cond      string
+		pars      []interface{}
+		listOrder string
+	)
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	{
+		// 分类
+		if params.ClassifyId != "" {
+			classifyIdArr := strings.Split(params.ClassifyId, ",")
+			classifyIds := make([]int, 0)
+			for _, v := range classifyIdArr {
+				t, _ := strconv.Atoi(v)
+				if t > 0 {
+					classifyIds = append(classifyIds, t)
+				}
+			}
+			if len(classifyIds) == 0 {
+				page := paging.GetPaging(params.CurrentIndex, params.PageSize, 0)
+				resp.Paging = page
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				return
+			}
+			cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().BaseFromThsHfClassifyId, utils.GetOrmInReplace(len(classifyIds)))
+			pars = append(pars, classifyIds)
+
+			//// 不包含子分类
+			//if len(classifyIds) > 0 && !params.IncludeChild {
+			//	cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().BaseFromThsHfClassifyId, utils.GetOrmInReplace(len(classifyIds)))
+			//	pars = append(pars, classifyIds)
+			//}
+			//
+			//// 包含子分类
+			//if len(classifyIds) > 0 && params.IncludeChild {
+			//	queryClassifyIds := make([]int, 0)
+			//	queryClassifyExist := make(map[int]bool)
+			//
+			//	for _, v := range classifyIds {
+			//		// 遍历所有分类从LevelPath中找含有查询分类ID的...=_=!
+			//		for _, cv := range classifies {
+			//			if queryClassifyExist[cv.BaseFromThsHfClassifyId] {
+			//				continue
+			//			}
+			//			if cv.LevelPath == "" {
+			//				continue
+			//			}
+			//			strArr := strings.Split(cv.LevelPath, ",")
+			//			if len(strArr) == 0 {
+			//				continue
+			//			}
+			//			for _, sv := range strArr {
+			//				tv, _ := strconv.Atoi(sv)
+			//				if tv == v {
+			//					queryClassifyIds = append(queryClassifyIds, cv.BaseFromThsHfClassifyId)
+			//					queryClassifyExist[cv.BaseFromThsHfClassifyId] = true
+			//					break
+			//				}
+			//			}
+			//		}
+			//	}
+			//
+			//	if len(queryClassifyIds) == 0 {
+			//		page := paging.GetPaging(params.CurrentIndex, params.PageSize, 0)
+			//		resp.Paging = page
+			//		br.Ret = 200
+			//		br.Success = true
+			//		br.Msg = "获取成功"
+			//		return
+			//	}
+			//	cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().BaseFromThsHfClassifyId, utils.GetOrmInReplace(len(queryClassifyIds)))
+			//	pars = append(pars, queryClassifyIds)
+			//}
+		}
+
+		if params.Frequency != "" {
+			frequencyArr := strings.Split(params.Frequency, ",")
+			if len(frequencyArr) > 0 {
+				cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().Frequency, utils.GetOrmInReplace(len(frequencyArr)))
+				pars = append(pars, frequencyArr)
+			}
+		}
+		if params.SysAdminId != "" {
+			adminIdArr := strings.Split(params.SysAdminId, ",")
+			adminIds := make([]int, 0)
+			for _, v := range adminIdArr {
+				t, _ := strconv.Atoi(v)
+				if t > 0 {
+					adminIds = append(adminIds, t)
+				}
+			}
+			if len(adminIds) > 0 {
+				cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().SysUserId, utils.GetOrmInReplace(len(adminIds)))
+				pars = append(pars, adminIds)
+			}
+		}
+		params.Keywords = strings.TrimSpace(params.Keywords)
+		if params.Keywords != "" {
+			cond += fmt.Sprintf(" AND (%s LIKE ? OR %s LIKE ?)", indexOb.Cols().IndexCode, indexOb.Cols().IndexName)
+			kw := fmt.Sprint("%", params.Keywords, "%")
+			pars = append(pars, kw, kw)
+		}
+
+		// 排序
+		if params.SortField > 0 && params.SortType > 0 {
+			fieldMap := map[int]string{1: indexOb.Cols().StartDate, 2: indexOb.Cols().EndDate, 3: indexOb.Cols().ModifyTime, 4: indexOb.Cols().LatestValue}
+			typeMap := map[int]string{1: "ASC", 2: "DESC"}
+			listOrder = fmt.Sprintf("%s %s", fieldMap[params.SortField], typeMap[params.SortType])
+		}
+	}
+
+	// 列表总计
+	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
+	}
+
+	// 分页查询
+	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)
+	items, e := indexOb.GetPageItemsByCondition(cond, pars, []string{}, listOrder, startSize, params.PageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标列表失败, %v", e)
+		return
+	}
+	for _, v := range items {
+		t := v.Format2Item()
+		// 分类完整路径
+		classify := classifyIdItem[v.BaseFromThsHfClassifyId]
+		levelArr := make([]string, 0)
+		if classify != nil && classify.LevelPath != "" {
+			arr := strings.Split(classify.LevelPath, ",")
+			for _, a := range arr {
+				i, _ := strconv.Atoi(a)
+				if classifyIdItem[i] != nil {
+					if this.Lang == utils.EnLangVersion {
+						levelArr = append(levelArr, classifyIdItem[i].ClassifyNameEn)
+					} else {
+						levelArr = append(levelArr, classifyIdItem[i].ClassifyName)
+					}
+				}
+			}
+		}
+		t.ClassifyPath = strings.Join(levelArr, "/")
+		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 = "获取成功"
+}
+
+// Add
+// @Title 新增指标
+// @Description 新增指标
+// @Param	request	body data_manage.ThsHfAddEdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/index/add [post]
+func (this *BaseFromThsHfController) Add() {
+	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.ThsHfAddEdbReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &params); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if params.StartTime == "" {
+		br.Msg = "请选择起始时间"
+		return
+	}
+	_, e := time.ParseInLocation(utils.FormatDateTime, params.StartTime, time.Local)
+	if e != nil {
+		br.Msg = "起始时间格式有误"
+		br.ErrMsg = fmt.Sprintf("起始时间格式有误, %v", e)
+		return
+	}
+	if params.EndTime != "" {
+		_, e := time.ParseInLocation(utils.FormatDateTime, params.EndTime, time.Local)
+		if e != nil {
+			br.Msg = "截止时间格式有误"
+			br.ErrMsg = fmt.Sprintf("截止时间格式有误, %v", e)
+			return
+		}
+	}
+	if !utils.InArrayByInt(data_manage.ThsHfPeriodArr, params.Interval) {
+		br.Msg = "时间周期有误"
+		br.ErrMsg = fmt.Sprintf("时间周期有误, Interval: %d", params.Interval)
+		return
+	}
+	if params.CPS != "" && !utils.InArrayByStr(data_manage.ThsHfCPSArr, params.CPS) {
+		br.Msg = "复权方式有误"
+		br.ErrMsg = fmt.Sprintf("复权方式有误, CPS: %s", params.CPS)
+		return
+	}
+	if params.BaseDate != "" {
+		_, e = time.ParseInLocation(utils.FormatDate, params.BaseDate, time.Local)
+		if e != nil {
+			br.Msg = "复权基点格式有误"
+			br.ErrMsg = fmt.Sprintf("复权基点格式有误, %v", e)
+			return
+		}
+	}
+	if params.Fill != "" && !utils.InArrayByStr(data_manage.ThsHfFillArr, params.Fill) {
+		br.Msg = "非交易间隔处理有误"
+		br.ErrMsg = fmt.Sprintf("非交易间隔处理有误, Fill: %s", params.Fill)
+		return
+	}
+	if len(params.IndexList) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	formExist := make(map[string]bool)
+	indexNames := make([]string, 0)
+	classifyIds := make([]int, 0)
+	for _, v := range params.IndexList {
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+		if !utils.InArrayByInt(classifyIds, v.ClassifyId) {
+			classifyIds = append(classifyIds, v.ClassifyId)
+		}
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		v.IndexName = strings.TrimSpace(v.IndexName)
+		if v.IndexName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		if formExist[v.IndexName] {
+			br.Msg = "指标名称重复, 请重新输入"
+			return
+		}
+		formExist[v.IndexName] = true
+		indexNames = append(indexNames, v.IndexName)
+	}
+
+	// 校验指标分类
+	classifyOb := new(data_manage.BaseFromThsHfClassify)
+	{
+		cond := fmt.Sprintf(` AND %s IN (%s)`, classifyOb.Cols().PrimaryId, utils.GetOrmInReplace(len(classifyIds)))
+		pars := make([]interface{}, 0)
+		pars = append(pars, classifyIds)
+		list, e := classifyOb.GetItemsByCondition(cond, pars, []string{classifyOb.Cols().PrimaryId, classifyOb.Cols().ClassifyName}, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("查询分类失败, %v", e)
+			return
+		}
+		classifyMap := make(map[int]*data_manage.BaseFromThsHfClassify)
+		for _, v := range list {
+			classifyMap[v.BaseFromThsHfClassifyId] = v
+		}
+		for _, v := range params.IndexList {
+			t := classifyMap[v.ClassifyId]
+			if t == nil {
+				br.Msg = fmt.Sprintf("%s分类不存在, 请重新选择或刷新页面", t.ClassifyName)
+				return
+			}
+		}
+	}
+
+	// 校验指标名称
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	{
+		cond := fmt.Sprintf(` AND %s IN (%s)`, indexOb.Cols().IndexName, utils.GetOrmInReplace(len(indexNames)))
+		pars := make([]interface{}, 0)
+		pars = append(pars, indexNames)
+		list, e := indexOb.GetItemsByCondition(cond, pars, []string{indexOb.Cols().IndexName}, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("查询指标名称失败, %v", e)
+			return
+		}
+		if len(list) > 0 {
+			tips := "以下指标名称重复, 请重新输入: \n"
+			for _, v := range list {
+				tips += fmt.Sprintf("%s\n", v.IndexName)
+			}
+			br.Msg = tips
+			return
+		}
+	}
+
+	// 新增指标
+	for _, v := range params.IndexList {
+		var req data_manage.ThsHfBaseAddReq
+		req.StartTime = params.StartTime
+		req.EndTime = params.EndTime
+		req.Interval = params.Interval
+		req.Fill = params.Fill
+		req.CPS = params.CPS
+		req.BaseDate = params.BaseDate
+		req.SysAdminId = sysUser.AdminId
+		req.SysAdminName = sysUser.RealName
+		req.ClassifyId = v.ClassifyId
+		req.Unit = v.Unit
+		req.IndexName = v.IndexName
+		req.Frequency = v.Frequency
+		req.StockCode = v.StockCode
+		req.EdbCode = v.EdbCode
+		_, e = data.BaseAddThsHf(req)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("BaseAddThsHf err: %v", e))
+			continue
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Edit
+// @Title 编辑指标
+// @Description 编辑指标
+// @Param	request	body data_manage.ThsHfIndexEditReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/index/edit [post]
+func (this *BaseFromThsHfController) Edit() {
+	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.ThsHfIndexEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &params); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if params.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", params.IndexId)
+		return
+	}
+	params.IndexName = strings.TrimSpace(params.IndexName)
+	if params.IndexName == "" {
+		br.Msg = "请输入指标名称"
+		return
+	}
+	if params.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	params.Unit = strings.TrimSpace(params.Unit)
+	if params.Unit == "" {
+		br.Msg = "请输入单位"
+		return
+	}
+
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	item, e := indexOb.GetItemById(params.IndexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	classifyOb := new(data_manage.BaseFromThsHfClassify)
+	_, e = classifyOb.GetItemById(params.ClassifyId)
+	if e != nil {
+		br.Msg = "分类有误"
+		br.ErrMsg = fmt.Sprintf("分类信息有误, %v", e)
+		return
+	}
+
+	// 重名验证
+	{
+		cond := fmt.Sprintf(" AND %s = ? AND %s <> ?", indexOb.Cols().IndexName, indexOb.Cols().PrimaryId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, params.IndexName, params.IndexId)
+		count, e := indexOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取重名指标失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "指标名称已存在, 请重新输入"
+			return
+		}
+	}
+
+	item.IndexName = params.IndexName
+	item.BaseFromThsHfClassifyId = params.ClassifyId
+	item.Unit = params.Unit
+	item.ModifyTime = time.Now().Local()
+	updateCols := []string{item.Cols().IndexName, item.Cols().BaseFromThsHfClassifyId, item.Cols().Unit, item.Cols().ModifyTime}
+	if e = item.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新指标信息失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 详情
+// @Description 详情
+// @Param   IndexId  query  int  true  "指标ID"
+// @Param   DataDate  query  string  false  "数据日期"
+// @Success 200 {object} data_manage.ThsHfSearchEdbResp
+// @router /ths_hf/index/detail [get]
+func (this *BaseFromThsHfController) Detail() {
+	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
+	}
+	dataDate := this.GetString("DataDate")
+	if dataDate != "" {
+		_, e := time.Parse(utils.FormatDate, dataDate)
+		if e != nil {
+			br.Msg = "数据日期格式有误"
+			br.ErrMsg = fmt.Sprintf("数据日期格式有误, DataDate: %s", dataDate)
+			return
+		}
+	}
+
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	item, e := indexOb.GetItemById(indexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	indexItem := item.Format2Item()
+	if item.EndDate.IsZero() {
+		item.EndDate = time.Now()
+	}
+	// 默认取有数据的最新日期
+	if dataDate == "" {
+		dataDate = item.EndDate.Format(utils.FormatDate)
+	}
+	startTime := fmt.Sprintf("%s 00:00:00", dataDate)
+	endTime := fmt.Sprintf("%s 23:59:59", dataDate)
+
+	// 获取数据
+	dataList, e := data.GetThsHfBaseIndexData(indexItem.IndexCode, startTime, endTime)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标数据失败, %v", e)
+		return
+	}
+
+	type DetailResp struct {
+		Index    *data_manage.BaseFromThsHfIndexItem
+		DataDate string `description:"数据日期"`
+		DataList []*data_manage.BaseFromThsHfDataItem
+	}
+	resp := new(DetailResp)
+	resp.Index = indexItem
+	resp.DataDate = dataDate
+	resp.DataList = dataList
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Refresh
+// @Title 刷新指标
+// @Description 刷新指标
+// @Param	request	body data_manage.ThsHfIndexOptReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/index/refresh [post]
+func (this *BaseFromThsHfController) Refresh() {
+	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.ThsHfIndexOptReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &params); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if params.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", params.IndexId)
+		return
+	}
+
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	_, e := indexOb.GetItemById(params.IndexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+
+	// 指标刷新逻辑
+	indexIds := []int{params.IndexId}
+	_, e = data.RefreshBaseThsHfIndex(indexIds, 1)
+	if e != nil {
+		br.Msg = "刷新失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Remove
+// @Title 删除指标
+// @Description 删除指标
+// @Param	request	body data_manage.ThsHfIndexOptReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/index/remove [post]
+func (this *BaseFromThsHfController) Remove() {
+	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.ThsHfIndexOptReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &params); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if params.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", params.IndexId)
+		return
+	}
+
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	item, e := indexOb.GetItemById(params.IndexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+
+	// 删除校验, 指标库存在不可删除
+	edbMappingOb := new(data_manage.BaseFromEdbMapping)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", edbMappingOb.Cols().BaseIndexCode)
+		pars := make([]interface{}, 0)
+		pars = append(pars, item.IndexCode)
+		count, e := edbMappingOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取源指标关联失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "指标已被引用, 不允许删除"
+			return
+		}
+	}
+
+	if e = item.Remove(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("删除指标失败, %v", e)
+		return
+	}
+
+	if utils.UseMongo {
+		mogDataObj := mgo.BaseFromThsHfData{}
+		if e = mogDataObj.RemoveMany(bson.M{"index_code": item.IndexCode}); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("删除指标数据失败-Mgo, %v", e)
+			return
+		}
+	} else {
+		dataOb := new(data_manage.BaseFromThsHfData)
+		cond := fmt.Sprintf(" %s = ?", dataOb.Cols().BaseFromThsHfIndexId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, params.IndexId)
+		if e = dataOb.RemoveByCondition(cond, pars); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("删除指标数据失败, %v", e)
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ListChoice
+// @Title 列表选择
+// @Description 列表选择
+// @Param	request	body data_manage.ThsHfIndexMultiOptReq true "type json string"
+// @Success 200 string "获取成功"
+// @router /ths_hf/index/list_choice [get]
+func (this *BaseFromThsHfController) ListChoice() {
+	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.ThsHfIndexListChoiceReq
+	if e := this.ParseForm(&params); e != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, err: %v", e)
+		return
+	}
+	var listIds []int
+	if params.ListIds != "" {
+		strArr := strings.Split(params.ListIds, ",")
+		for _, v := range strArr {
+			t, _ := strconv.Atoi(v)
+			if t > 0 {
+				listIds = append(listIds, t)
+			}
+		}
+	}
+
+	var (
+		cond string
+		pars []interface{}
+	)
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	resp := make([]*data_manage.ThsHfIndexListChoiceItem, 0)
+
+	// 非列表全选
+	if !params.SelectAll {
+		if len(listIds) > 0 {
+			cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(listIds)))
+			pars = append(pars, listIds)
+		}
+	}
+
+	// 列表全选, 根据条件筛选列表并过滤params.ListIds中选择的ID
+	if params.SelectAll {
+		// 查询所有分类-分类查询用
+		classifyOb := new(data_manage.BaseFromThsHfClassify)
+		classifies := make([]*data_manage.BaseFromThsHfClassify, 0)
+		classifyIdItem := make(map[int]*data_manage.BaseFromThsHfClassify)
+		{
+			list, e := classifyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{classifyOb.Cols().PrimaryId, classifyOb.Cols().ClassifyName, classifyOb.Cols().ClassifyNameEn, classifyOb.Cols().LevelPath}, "")
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取分类列表失败, %v", e)
+				return
+			}
+			for _, v := range list {
+				classifyIdItem[v.BaseFromThsHfClassifyId] = v
+			}
+			classifies = list
+		}
+
+		// 筛选项
+		if params.ClassifyId != "" {
+			classifyIdArr := strings.Split(params.ClassifyId, ",")
+			classifyIds := make([]int, 0)
+			for _, v := range classifyIdArr {
+				t, _ := strconv.Atoi(v)
+				if t > 0 {
+					classifyIds = append(classifyIds, t)
+				}
+			}
+
+			// 不包含子分类
+			if len(classifyIds) > 0 && !params.IncludeChild {
+				cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().BaseFromThsHfClassifyId, utils.GetOrmInReplace(len(classifyIds)))
+				pars = append(pars, classifyIds)
+			}
+
+			// 包含子分类
+			if len(classifyIds) > 0 && params.IncludeChild {
+				queryClassifyIds := make([]int, 0)
+				queryClassifyExist := make(map[int]bool)
+
+				for _, v := range classifyIds {
+					// 遍历所有分类从LevelPath中找含有查询分类ID的...=_=!
+					for _, cv := range classifies {
+						if queryClassifyExist[cv.BaseFromThsHfClassifyId] {
+							continue
+						}
+						if cv.LevelPath == "" {
+							continue
+						}
+						strArr := strings.Split(cv.LevelPath, ",")
+						if len(strArr) == 0 {
+							continue
+						}
+						for _, sv := range strArr {
+							tv, _ := strconv.Atoi(sv)
+							if tv == v {
+								queryClassifyIds = append(queryClassifyIds, cv.BaseFromThsHfClassifyId)
+								queryClassifyExist[cv.BaseFromThsHfClassifyId] = true
+								break
+							}
+						}
+					}
+				}
+
+				if len(queryClassifyIds) == 0 {
+					br.Data = resp
+					br.Ret = 200
+					br.Success = true
+					br.Msg = "获取成功"
+					return
+				}
+				cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().BaseFromThsHfClassifyId, utils.GetOrmInReplace(len(queryClassifyIds)))
+				pars = append(pars, queryClassifyIds)
+			}
+		}
+
+		if params.Frequency != "" {
+			frequencyArr := strings.Split(params.Frequency, ",")
+			if len(frequencyArr) > 0 {
+				cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().Frequency, utils.GetOrmInReplace(len(frequencyArr)))
+				pars = append(pars, frequencyArr)
+			}
+		}
+		if params.SysAdminId != "" {
+			adminIdArr := strings.Split(params.SysAdminId, ",")
+			adminIds := make([]int, 0)
+			for _, v := range adminIdArr {
+				t, _ := strconv.Atoi(v)
+				if t > 0 {
+					adminIds = append(adminIds, t)
+				}
+			}
+			if len(adminIds) > 0 {
+				cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().SysUserId, utils.GetOrmInReplace(len(adminIds)))
+				pars = append(pars, adminIds)
+			}
+		}
+		params.Keywords = strings.TrimSpace(params.Keywords)
+		if params.Keywords != "" {
+			cond += fmt.Sprintf(" AND (%s LIKE ? OR %s LIKE ?)", indexOb.Cols().IndexCode, indexOb.Cols().IndexName)
+			kw := fmt.Sprint("%", params.Keywords, "%")
+			pars = append(pars, kw, kw)
+		}
+
+		// 过滤掉选择的指标
+		if len(listIds) > 0 {
+			cond += fmt.Sprintf(" AND %s NOT IN (%s)", indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(listIds)))
+			pars = append(pars, listIds)
+		}
+	}
+
+	fields := []string{indexOb.Cols().PrimaryId, indexOb.Cols().IndexCode, indexOb.Cols().IndexName}
+	list, e := indexOb.GetItemsByCondition(cond, pars, fields, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if len(list) > 500 {
+		br.Msg = "选择指标超过500个, 请重新选择"
+		return
+	}
+	for _, v := range list {
+		resp = append(resp, &data_manage.ThsHfIndexListChoiceItem{
+			IndexId:   v.BaseFromThsHfIndexId,
+			IndexCode: v.IndexCode,
+			IndexName: v.IndexName,
+		})
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// MultiOpt
+// @Title 批量操作-移动分类/删除/刷新
+// @Description 批量操作-移动分类/删除/刷新
+// @Param	request	body data_manage.ThsHfIndexMultiOptReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/index/multi_opt [post]
+func (this *BaseFromThsHfController) MultiOpt() {
+	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.ThsHfIndexMultiOptReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &params); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if !utils.InArrayByInt([]int{1, 2, 3}, params.OptType) {
+		br.Msg = "请选择操作类型"
+		return
+	}
+	if len(params.IndexIds) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if params.OptType == 1 && params.MoveClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	if params.OptType == 3 && !utils.InArrayByInt([]int{1, 2}, params.RefreshType) {
+		br.Msg = "请选择刷新方式"
+		return
+	}
+	resp := new(data_manage.ThsHfIndexMultiOptResp)
+
+	// 指标名称
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	indexMap := make(map[int]*data_manage.BaseFromThsHfIndex)
+	{
+		cond := fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(params.IndexIds)))
+		pars := make([]interface{}, 0)
+		pars = append(pars, params.IndexIds)
+		list, e := indexOb.GetItemsByCondition(cond, pars, []string{indexOb.Cols().PrimaryId, indexOb.Cols().IndexCode, indexOb.Cols().IndexName}, "")
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = fmt.Sprintf("获取源指标列表失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			indexMap[v.BaseFromThsHfIndexId] = v
+		}
+	}
+
+	// 批量移动
+	if params.OptType == 1 {
+		if e := indexOb.UpdateClassifyMulti(params.IndexIds, params.MoveClassifyId); e != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = fmt.Sprintf("批量移动分类失败, %v", e)
+			return
+		}
+	}
+
+	// 批量删除
+	if params.OptType == 2 {
+		// 被引用的指标需要提示出来, 未被引用的直接删除
+		usedIndexIds := make([]int, 0)
+		{
+			edbMappingOb := new(data_manage.BaseFromEdbMapping)
+			cond := fmt.Sprintf(" AND %s IN (%s)", edbMappingOb.Cols().BaseFromIndexId, utils.GetOrmInReplace(len(params.IndexIds)))
+			pars := make([]interface{}, 0)
+			pars = append(pars, params.IndexIds)
+			list, e := edbMappingOb.GetItemsByCondition(cond, pars, []string{}, "")
+			if e != nil {
+				br.Msg = "删除失败"
+				br.ErrMsg = fmt.Sprintf("获取源指标关联失败, %v", e)
+				return
+			}
+			for _, v := range list {
+				if !utils.InArrayByInt(usedIndexIds, v.BaseFromIndexId) {
+					usedIndexIds = append(usedIndexIds, v.BaseFromIndexId)
+				}
+			}
+		}
+
+		// 可删除的指标
+		removeIndexIds := params.IndexIds
+		if len(usedIndexIds) > 0 {
+			removeIndexIds = utils.MinusInt(params.IndexIds, usedIndexIds)
+
+			// 标记不允许删除的
+			for _, v := range usedIndexIds {
+				t := indexMap[v]
+				if t == nil {
+					continue
+				}
+				resp.Fail = append(resp.Fail, &data_manage.ThsHfIndexBaseInfo{
+					IndexId:   v,
+					IndexCode: t.IndexCode,
+					IndexName: t.IndexName,
+				})
+			}
+		}
+
+		if len(removeIndexIds) > 0 {
+			if e := indexOb.MultiRemove(removeIndexIds); e != nil {
+				br.Msg = "删除失败"
+				br.ErrMsg = fmt.Sprintf("批量删除指标失败, %v", e)
+				return
+			}
+
+			if utils.UseMongo {
+				mogDataObj := mgo.BaseFromThsHfData{}
+				for _, v := range removeIndexIds {
+					if e := mogDataObj.RemoveMany(bson.M{"base_from_ths_hf_index_id": v}); e != nil {
+						br.Msg = "操作失败"
+						br.ErrMsg = fmt.Sprintf("批量删除指标数据失败-Mgo, %v", e)
+						return
+					}
+				}
+			} else {
+				dataOb := new(data_manage.BaseFromThsHfData)
+				cond := fmt.Sprintf(" %s IN (%s)", dataOb.Cols().BaseFromThsHfIndexId, utils.GetOrmInReplace(len(removeIndexIds)))
+				pars := make([]interface{}, 0)
+				pars = append(pars, removeIndexIds)
+				if e := dataOb.RemoveByCondition(cond, pars); e != nil {
+					br.Msg = "删除失败"
+					br.ErrMsg = fmt.Sprintf("批量删除指标数据失败, %v", e)
+					return
+				}
+			}
+
+			// 标记删除成功的
+			for _, v := range removeIndexIds {
+				t := indexMap[v]
+				if t == nil {
+					continue
+				}
+				resp.Success = append(resp.Success, &data_manage.ThsHfIndexBaseInfo{
+					IndexId:   v,
+					IndexCode: t.IndexCode,
+					IndexName: t.IndexName,
+				})
+			}
+		}
+	}
+
+	// 批量刷新
+	if params.OptType == 3 {
+		isAsync, e := data.RefreshBaseThsHfIndex(params.IndexIds, params.RefreshType)
+		if e != nil {
+			br.Msg = "刷新失败"
+			br.ErrMsg = fmt.Sprintf("批量刷新指标失败, %v", e)
+			return
+		}
+		if isAsync {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功, 刷新指标较多, 请10分钟后查看"
+			return
+		}
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Save2EdbPre
+// @Title 批量新增指标库-前置
+// @Description 批量新增指标库-前置
+// @Param	request	body data_manage.ThsHfIndexMultiSave2EdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/index/save2edb_pre [post]
+func (this *BaseFromThsHfController) Save2EdbPre() {
+	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.ThsHfIndexMultiSave2EdbPreReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &params); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if len(params.IndexIds) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	convertRule := params.ConvertRule
+	if convertRule.ConvertType != 1 && convertRule.ConvertType != 2 {
+		br.Msg = "请选择数据转换方式"
+		return
+	}
+
+	// 生成的指标名称后缀
+	var (
+		suffixName  string
+		startTimeCn string
+		endTimeCn   string
+		calculateCn string
+	)
+	dayMap := map[int]string{1: "当日", 2: "前日"}
+	calculateMap := map[int]string{1: "均值", 2: "最大值", 3: "最小值"}
+	if convertRule.ConvertType == 1 {
+		if convertRule.ConvertFixed.FixedDay != 1 && convertRule.ConvertFixed.FixedDay != 2 {
+			br.Msg = "请选择指定时间"
+			return
+		}
+		if convertRule.ConvertFixed.FixedTime == "" {
+			br.Msg = "请选择指定时间"
+			return
+		}
+		timePrefix := time.Now().Local().Format(utils.FormatDate)
+		st := fmt.Sprintf("%s %s", timePrefix, convertRule.ConvertFixed.FixedTime)
+		startTime, e := time.Parse(utils.FormatDateTime, st)
+		if e != nil {
+			br.Msg = "指定时间格式有误"
+			return
+		}
+		startTimeCn = fmt.Sprintf("%s%s", dayMap[convertRule.ConvertFixed.FixedDay], startTime.Format("15点04"))
+	}
+	if convertRule.ConvertType == 2 {
+		if convertRule.ConvertArea.StartDay != 1 && convertRule.ConvertArea.StartDay != 2 {
+			br.Msg = "请选择起始时间"
+			return
+		}
+		if convertRule.ConvertArea.StartTime == "" {
+			br.Msg = "请选择起始时间"
+			return
+		}
+		var startTimePre string
+		if convertRule.ConvertArea.StartDay == 1 {
+			startTimePre = time.Now().Local().Format(utils.FormatDate)
+		}
+		if convertRule.ConvertArea.StartDay == 2 {
+			startTimePre = time.Now().Local().AddDate(0, 0, -1).Format(utils.FormatDate)
+		}
+		st := fmt.Sprintf("%s %s", startTimePre, convertRule.ConvertArea.StartTime)
+		startTime, e := time.Parse(utils.FormatDateTime, st)
+		if e != nil {
+			br.Msg = "起始时间格式有误"
+			return
+		}
+		startTimeCn = fmt.Sprintf("%s%s", dayMap[convertRule.ConvertArea.StartDay], startTime.Format("15点04"))
+
+		if convertRule.ConvertArea.EndDay != 1 && convertRule.ConvertArea.EndDay != 2 {
+			br.Msg = "请选择截止时间"
+			return
+		}
+		if convertRule.ConvertArea.EndTime == "" {
+			br.Msg = "请选择截止时间"
+			return
+		}
+		var endTimePre string
+		if convertRule.ConvertArea.EndDay == 1 {
+			endTimePre = time.Now().Local().Format(utils.FormatDate)
+		}
+		if convertRule.ConvertArea.EndDay == 2 {
+			endTimePre = time.Now().Local().AddDate(0, 0, -1).Format(utils.FormatDate)
+		}
+		ed := fmt.Sprintf("%s %s", endTimePre, convertRule.ConvertArea.EndTime)
+		endTime, e := time.Parse(utils.FormatDateTime, ed)
+		if e != nil {
+			br.Msg = "截止时间格式有误"
+			return
+		}
+		endTimeCn = fmt.Sprintf("至%s%s", dayMap[convertRule.ConvertArea.EndDay], endTime.Format("15点04"))
+		if startTime.After(endTime) {
+			br.Msg = "起始日期不可早于截止日期"
+			return
+		}
+		calculateCn = calculateMap[convertRule.ConvertArea.CalculateType]
+	}
+	suffixName = fmt.Sprint(startTimeCn, endTimeCn, calculateCn)
+
+	// 查询指标
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	cond := fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(params.IndexIds)))
+	pars := make([]interface{}, 0)
+	pars = append(pars, params.IndexIds)
+	list, e := indexOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+
+	resp := make([]*data_manage.ThsHfIndexMultiSave2EdbPreItem, 0)
+	for _, v := range list {
+		t := new(data_manage.ThsHfIndexMultiSave2EdbPreItem)
+		t.IndexId = v.BaseFromThsHfIndexId
+		t.IndexCode = v.IndexCode
+		t.IndexName = v.IndexName
+		edbCn := data_manage.ThsHfEdbCodeCn[v.Indicator]
+		if edbCn == "" {
+			edbCn = v.Indicator
+		}
+		t.NewIndexName = fmt.Sprint(v.StockCode, v.Frequency, edbCn, suffixName)
+		t.StockCode = v.StockCode
+		t.EdbCode = v.Indicator
+		t.Unit = v.Unit
+		t.Frequency = v.Frequency
+		t.NewFrequency = "日度"
+		resp = append(resp, t)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Save2Edb
+// @Title 批量新增指标库
+// @Description 批量新增指标库
+// @Param	request	body data_manage.ThsHfIndexMultiSave2EdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/index/save2edb [post]
+func (this *BaseFromThsHfController) Save2Edb() {
+	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.ThsHfIndexMultiSave2EdbReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &params); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if len(params.NewIndexes) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	convertRule := params.ConvertRule
+	if convertRule.ConvertType != 1 && convertRule.ConvertType != 2 {
+		br.Msg = "请选择数据转换方式"
+		return
+	}
+	if convertRule.ConvertType == 1 {
+		if convertRule.ConvertFixed.FixedDay != 1 && convertRule.ConvertFixed.FixedDay != 2 {
+			br.Msg = "请选择指定时间"
+			return
+		}
+		if convertRule.ConvertFixed.FixedTime == "" {
+			br.Msg = "请选择指定时间"
+			return
+		}
+		timePrefix := time.Now().Local().Format(utils.FormatDate)
+		st := fmt.Sprintf("%s %s", timePrefix, convertRule.ConvertFixed.FixedTime)
+		_, e := time.Parse(utils.FormatDateTime, st)
+		if e != nil {
+			br.Msg = "指定时间格式有误"
+			return
+		}
+	}
+	if convertRule.ConvertType == 2 {
+		if convertRule.ConvertArea.StartDay != 1 && convertRule.ConvertArea.StartDay != 2 {
+			br.Msg = "请选择起始时间"
+			return
+		}
+		if convertRule.ConvertArea.StartTime == "" {
+			br.Msg = "请选择起始时间"
+			return
+		}
+		var startTimePre string
+		if convertRule.ConvertArea.StartDay == 1 {
+			startTimePre = time.Now().Local().Format(utils.FormatDate)
+		}
+		if convertRule.ConvertArea.StartDay == 2 {
+			startTimePre = time.Now().Local().AddDate(0, 0, -1).Format(utils.FormatDate)
+		}
+		st := fmt.Sprintf("%s %s", startTimePre, convertRule.ConvertArea.StartTime)
+		startTime, e := time.Parse(utils.FormatDateTime, st)
+		if e != nil {
+			br.Msg = "起始时间格式有误"
+			return
+		}
+
+		if convertRule.ConvertArea.EndDay != 1 && convertRule.ConvertArea.EndDay != 2 {
+			br.Msg = "请选择截止时间"
+			return
+		}
+		if convertRule.ConvertArea.EndTime == "" {
+			br.Msg = "请选择截止时间"
+			return
+		}
+		var endTimePre string
+		if convertRule.ConvertArea.EndDay == 1 {
+			endTimePre = time.Now().Local().Format(utils.FormatDate)
+		}
+		if convertRule.ConvertArea.EndDay == 2 {
+			endTimePre = time.Now().Local().AddDate(0, 0, -1).Format(utils.FormatDate)
+		}
+		ed := fmt.Sprintf("%s %s", endTimePre, convertRule.ConvertArea.EndTime)
+		endTime, e := time.Parse(utils.FormatDateTime, ed)
+		if e != nil {
+			br.Msg = "截止时间格式有误"
+			return
+		}
+		if startTime.After(endTime) {
+			br.Msg = "起始日期不可早于截止日期"
+			return
+		}
+	}
+	resp := new(data_manage.ThsHfIndexMultiSave2EdbResp)
+
+	// 判断指标名称是否重复
+	edbNameExist := make(map[string]bool)
+	{
+		items, e := data_manage.GetEdbInfoFieldList(``, make([]interface{}, 0), []string{"edb_name"})
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取指标名称列表失败, %v", e)
+			return
+		}
+		for _, v := range items {
+			edbNameExist[v.EdbName] = true
+		}
+	}
+	for _, v := range params.NewIndexes {
+		if edbNameExist[v.NewIndexName] {
+			resp.Exist = append(resp.Exist, v)
+		}
+	}
+	if len(resp.Exist) > 0 {
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "含重复指标名称"
+		return
+	}
+
+	// 请求指标库新增
+	for _, v := range params.NewIndexes {
+		var r data_manage.ThsHfIndexMultiSave2EdbLibReq
+		r.ConvertRule = convertRule
+		v.SysAdminId = sysUser.AdminId
+		v.SysAdminName = sysUser.RealName
+		r.NewIndex = v
+		b, e := json.Marshal(r)
+		if e != nil {
+			v.Tips = "新增失败"
+			v.ErrMsg = e.Error()
+			resp.Fail = append(resp.Fail, v)
+			continue
+		}
+
+		res, e := data.AddBaseEdbInfo(string(b), utils.DATA_SOURCE_THS, utils.DATA_SUB_SOURCE_HIGH_FREQUENCY, this.Lang)
+		if e != nil {
+			v.Tips = "新增失败"
+			v.ErrMsg = fmt.Sprintf("AddBaseEdbInfo, err: %v", e)
+			resp.Fail = append(resp.Fail, v)
+			continue
+		}
+		if res.Ret != 200 {
+			v.Tips = res.Msg
+			v.ErrMsg = fmt.Sprintf("AddBaseEdbInfo, Ret: %d, ErrMsg: %s", res.Ret, res.ErrMsg)
+			resp.Fail = append(resp.Fail, v)
+			continue
+		}
+		v.Tips = "新增成功"
+		resp.Success = append(resp.Success, v)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 490 - 0
controllers/data_manage/base_from_ths_hf_classify.go

@@ -0,0 +1,490 @@
+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.ThsHfSearchEdbResp
+// @router /ths_hf/classify/list [get]
+func (this *BaseFromThsHfController) 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")
+
+	resp := make([]*data_manage.BaseFromThsHfClassifyListItem, 0)
+	// 查询分类
+	classifyOb := new(data_manage.BaseFromThsHfClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, parentId)
+		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.BaseFromThsHfClassifyListItem{
+				ClassifyId:     v.BaseFromThsHfClassifyId,
+				ClassifyName:   v.ClassifyName,
+				ClassifyNameEn: v.ClassifyNameEn,
+				ParentId:       v.ParentId,
+				Level:          v.Level,
+				Sort:           v.Sort,
+				UniqueCode:     v.UniqueCode,
+			})
+		}
+	}
+
+	// 查询指标
+	indexOb := new(data_manage.BaseFromThsHfIndex)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", indexOb.Cols().BaseFromThsHfClassifyId)
+		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.BaseFromThsHfClassifyListItem{
+				ItemType:   1,
+				IndexId:    v.BaseFromThsHfIndexId,
+				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.ThsHfSearchEdbResp
+// @router /ths_hf/classify/tree [get]
+func (this *BaseFromThsHfController) 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.BaseFromThsHfClassify)
+	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.BaseFromThsHfClassifyItem, 0)
+	for _, v := range list {
+		items = append(items, v.Format2Item())
+	}
+	tree := data.GetThsHfClassifyTreeRecursive(items, 0)
+
+	br.Data = tree
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ClassifyAdd
+// @Title 新增分类
+// @Description 新增分类
+// @Param	request	body data_manage.ThsHfClassifyAddReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/classify/add [post]
+func (this *BaseFromThsHfController) 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.ThsHfClassifyAddReq
+	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.BaseFromThsHfClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		if this.Lang == utils.EnLangVersion {
+			cond += fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ClassifyNameEn)
+		} else {
+			cond += fmt.Sprintf(" AND %s = ?", 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.ClassifyNameEn = 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.BaseFromThsHfClassifyId)
+		classifyOb.RootId = rootId
+	} else {
+		classifyOb.LevelPath = fmt.Sprint(classifyOb.BaseFromThsHfClassifyId)
+		classifyOb.RootId = classifyOb.BaseFromThsHfClassifyId
+	}
+	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.BaseFromThsHfClassifyId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ClassifyEdit
+// @Title 编辑分类
+// @Description 编辑分类
+// @Param	request	body data_manage.ThsHfClassifyEditReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/classify/edit [post]
+func (this *BaseFromThsHfController) 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.ThsHfClassifyEditReq
+	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.BaseFromThsHfClassify)
+	classifyItem, e := classifyOb.GetItemById(req.ClassifyId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+		return
+	}
+
+	// 校验分类名称
+	{
+		cond := fmt.Sprintf(" AND %s <> ?", classifyOb.Cols().PrimaryId)
+		if this.Lang == utils.EnLangVersion {
+			cond += fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ClassifyNameEn)
+		} else {
+			cond += fmt.Sprintf(" AND %s = ?", 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.ClassifyNameEn = req.ClassifyName
+	classifyItem.ModifyTime = time.Now().Local()
+	updateCols := []string{classifyOb.Cols().ClassifyName, classifyOb.Cols().ClassifyNameEn, 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.ThsHfClassifyRemoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/classify/remove [post]
+func (this *BaseFromThsHfController) 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.ThsHfClassifyRemoveReq
+	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.BaseFromThsHfClassify)
+	classifyItem, e := classifyOb.GetItemById(req.ClassifyId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+		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.BaseFromThsHfIndex)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", indexOb.Cols().BaseFromThsHfClassifyId)
+		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.BaseFromThsHfClassifyMoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ths_hf/classify/move [post]
+func (this *BaseFromThsHfController) 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.BaseFromThsHfClassifyMoveReq
+	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.ThsHfMoveClassify(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 = "操作成功"
+}

+ 2 - 0
controllers/data_manage/chart_classify.go

@@ -709,6 +709,8 @@ func (this *ChartClassifyController) DeleteChartClassify() {
 				}
 			}
 		}
+		// 删除图表中的指标引用
+		_ = data_manage.DeleteEdbRelationByObjectId(req.ChartInfoId, utils.EDB_RELATION_CHART)
 		//新增操作日志
 		{
 			chartLog := new(data_manage.ChartInfoLog)

+ 21 - 9
controllers/data_manage/edb_info.go

@@ -2,7 +2,6 @@ package data_manage
 
 import (
 	"encoding/json"
-	"eta/eta_api/cache"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
@@ -11,7 +10,6 @@ import (
 	request2 "eta/eta_api/models/data_manage/excel/request"
 	"eta/eta_api/models/data_manage/request"
 	"eta/eta_api/models/data_manage/response"
-	"eta/eta_api/models/mgo"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data"
@@ -23,7 +21,6 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"go.mongodb.org/mongo-driver/bson"
 	"strconv"
 	"strings"
 	"sync"
@@ -60,6 +57,9 @@ func (this *EdbInfoController) EdbInfoSearch() {
 	stockCode := this.GetString("StockCode")
 	frequency := this.GetString("Frequency")
 
+	extraPars := this.GetString("ExtraPars")
+	extraPars = strings.TrimSpace(extraPars)
+
 	if source <= 0 {
 		br.Msg = "无效的数据来源"
 		return
@@ -222,7 +222,7 @@ func (this *EdbInfoController) EdbInfoSearch() {
 					br.Msg = "请输入指标代码"
 					return
 				}
-				respItem, err := data.AddEdbDataThsDs(source, stockCode, edbCode)
+				respItem, err := data.AddEdbDataThsDs(source, stockCode, edbCode, extraPars)
 				if err != nil {
 					br.Msg = "获取失败"
 					br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -1735,8 +1735,7 @@ func (this *EdbInfoController) EdbInfoSearch() {
 				return
 			}
 
-			obj := mgo.BaseFromBusinessData{}
-			tmpDataList, err := obj.GetLimitDataList(bson.M{"index_code": edbCode}, utils.EDB_DATA_LIMIT, []string{"-data_time"})
+			_, tmpDataList, err := data.GetPageBaseBusinessIndexData(edbCode, 1, utils.EDB_DATA_LIMIT)
 			if err != nil && err.Error() != utils.ErrNoRow() {
 				br.Msg = "获取失败"
 				br.ErrMsg = "获取自有数据已存在信息失败,Err:" + err.Error()
@@ -1745,7 +1744,7 @@ func (this *EdbInfoController) EdbInfoSearch() {
 			dataItems := make([]*data_manage.EdbInfoSearchData, 0)
 			for _, v := range tmpDataList {
 				dataItems = append(dataItems, &data_manage.EdbInfoSearchData{
-					DataTime: v.DataTime.Format(utils.FormatDate),
+					DataTime: v.DataTime,
 					Value:    v.Value,
 				})
 			}
@@ -3380,6 +3379,10 @@ func (this *EdbInfoController) EdbInfoFilterByEs() {
 			pars = append(pars, frequency)
 		}
 
+		if source > 0 && filterSource != 5 {
+			condition += ` AND source = ? `
+			pars = append(pars, source)
+		}
 		// 查询只允许添加预测指标的搜索
 		if isAddPredictEdb {
 			condition += ` AND frequency in ("日度","周度","月度") `
@@ -3889,8 +3892,6 @@ func (this *ChartInfoController) EdbInfoReplace() {
 		return
 	}
 
-	//加入到缓存队列中处理
-	go cache.AddReplaceEdbInfo(oldEdbInfo, newEdbInfo)
 	br.Msg = "替换成功"
 	br.ErrMsg = "替换成功"
 	br.Ret = 200
@@ -5788,6 +5789,17 @@ func (this *EdbInfoController) EdbInfoBatchAdd() {
 		edbInfoItem.StockCode = v.StockCode
 		edbInfoItem.TerminalCode = terminalCode
 		edbInfoItem.ServerUrl = serverUrl
+		var extra data_manage.EdbInfoExtra
+		if v.ApiExtraPars != "" {
+			extra.ApiExtraPars = v.ApiExtraPars
+			b, e := json.Marshal(extra)
+			if e != nil {
+				br.Msg = "保存失败"
+				br.ErrMsg = fmt.Sprintf("额外参数JSON格式化失败, %v", e)
+				return
+			}
+			edbInfoItem.Extra = string(b)
+		}
 
 		// 指标入库
 		edbInfo, err, errMsg, isSendEmail := data.EdbInfoWsdAdd(edbInfoItem)

+ 1 - 1
controllers/data_manage/edb_info_calculate.go

@@ -1468,7 +1468,7 @@ func (this *EdbInfoController) QueryEdbDataTable() {
 	tableName := data_manage.GetEdbDataTableName(edbInfo.Source, edbInfo.SubSource)
 
 	var templateStr string
-	if edbInfo.Source == utils.DATA_SOURCE_BUSINESS {
+	if edbInfo.Source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		templateStr = fmt.Sprintf("# 查询条件\nquery = {\"edb_code\": \"%s\"}\n# 排序\nsort = [(\"data_time\", -1)]  # -1 表示降序排列,对应 SQL 的 DESC\nprojection = {\"data_time\": 1, \"value\": 1, \"_id\": 0}  # 只选择data_time和value字段,忽略_id字段\n# 使用 find() 方法获取数据,然后使用 aggregate() 转换为列表\nraw_data = list(collection.find(query, projection).sort(sort))\n# 将结果转换为 DataFrame\nraw = pd.DataFrame(raw_data)\n# 转换data_time字段为本地时区时间\nraw['data_time'] = raw['data_time'].apply(lambda x: x.replace(tzinfo=utc_tz)).dt.tz_convert(local_tz).dt.strftime('%s')", edbInfo.EdbCode, "%Y-%m-%d")
 	} else {
 		templateStr = fmt.Sprintf("sql1 = f\"\"\"SELECT data_time,`value` FROM %s WHERE edb_code = '%s' ORDER BY data_time DESC;\"\"\"\nraw = pandas_fetch_all(sql1, db)", tableName, edbInfo.EdbCode)

+ 232 - 2
controllers/data_manage/edb_info_refresh.go

@@ -474,7 +474,7 @@ func (c *EdbInfoController) SaveEdbRefreshStatus() {
 		br.IsSendEmail = false
 		return
 	}
-
+	// todo 批量设置刷新状态修改
 	edbIdList := make([]int, 0)
 	edbCodeList := make([]string, 0)
 	// 指标id列表
@@ -519,13 +519,243 @@ func (c *EdbInfoController) SaveEdbRefreshStatus() {
 		isStop = 1
 	}
 
+	if len(edbIdList) <= 0 {
+		br.Msg = "指标不能为空"
+		br.IsSendEmail = false
+		return
+	}
+	//查询计算指标信息
+	calculateEdbIdList := make([]int, 0)
+	if isStop == 1 {
+		fromEdbIdList := make([]int, 0)
+		edbList, e := data_manage.GetEdbInfoByIdList(edbIdList)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range edbList {
+			if v.EdbInfoType == 0 {
+				fromEdbIdList = append(fromEdbIdList, v.EdbInfoId)
+			}
+		}
+
+		hasFind := make(map[int]struct{})
+		calculateEdbIdList, err = data.GetCalculateEdbByFromEdbInfo(fromEdbIdList, calculateEdbIdList, hasFind)
+		if err != nil {
+			br.Msg = "查询计算指标信息失败"
+			br.ErrMsg = "查询计算指标信息失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	switch req.Source {
 	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL: // 钢联
 		err = data_manage.ModifyMysteelChemicalUpdateStatus(edbIdList, edbCodeList, isStop)
 	case utils.DATA_SOURCE_YS: // 有色
 		err = data_manage.ModifySmmUpdateStatus(edbIdList, edbCodeList, isStop)
 	default:
-		err = data_manage.ModifyEdbInfoUpdateStatus(edbIdList, isStop)
+		err = data_manage.ModifyEdbInfoUpdateStatus(edbIdList, isStop, calculateEdbIdList)
+	}
+	if err != nil {
+		br.Msg = `保存失败`
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// SaveEdbRefreshStatusSingle
+// @Title 设置单个指标刷新状态接口
+// @Description 设置单个指标刷新状态接口
+// @Param	request	body data_manage.SaveEdbRefreshStatusReq true "type json string"
+// @Success 200 {object} data_manage.RefreshBaseEdbInfoResp
+// @router /edb_info/single_refresh/status/save [post]
+func (c *EdbInfoController) SaveEdbRefreshStatusSingle() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	var req request.SaveEdbRefreshStatusSingleReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.EdbInfoId <= 0 {
+		br.Msg = "请选择指标"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 查询指标
+	edbInfo, err := data_manage.GetEdbInfoById(req.EdbInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "指标不存在"
+			return
+		}
+		br.Msg = "查询指标失败"
+		br.ErrMsg = "查询指标失败,Err:" + err.Error()
+		return
+	}
+
+	// 查询指标的指标代码
+	calculateEdbIdList := make([]int, 0)
+
+	isStop := 0
+	if req.ModifyStatus == `暂停` {
+		isStop = 1
+	}
+
+	if isStop == 1 && edbInfo.EdbInfoType == 0 { //基础指标, 只有在停用的情况下才需要查询计算指标
+		hasFind := make(map[int]struct{})
+		calculateEdbIdList, err = data.GetCalculateEdbByFromEdbInfo([]int{edbInfo.EdbInfoId}, calculateEdbIdList, hasFind)
+		if err != nil {
+			br.Msg = "查询计算指标信息失败"
+			br.ErrMsg = "查询计算指标信息失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	switch edbInfo.Source {
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL: // 钢联
+		err = data_manage.ModifyMysteelChemicalUpdateStatusByEdbInfoId(edbInfo.EdbInfoId, isStop, edbInfo.EdbCode, calculateEdbIdList)
+	default: // wind
+		err = data_manage.EdbInfoUpdateStatusByEdbInfoId([]int{edbInfo.EdbInfoId}, isStop, calculateEdbIdList)
+	}
+	if err != nil {
+		br.Msg = `保存失败`
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// SaveRelationEdbRefreshStatus
+// @Title 批量设置被引用的指标刷新状态接口
+// @Description 批量设置被引用的指标刷新状态接口
+// @Param	request	body data_manage.SaveEdbRefreshStatusReq true "type json string"
+// @Success 200 {object} data_manage.RefreshBaseEdbInfoResp
+// @router /edb_info/relation/refresh/save [post]
+func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	var req request.SaveRelationEdbRefreshStatusReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.Source <= 0 {
+		br.Msg = "来源不能为空"
+		br.IsSendEmail = false
+		return
+	}
+	edbType := 1 //基础指标
+	switch req.Source {
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL, utils.DATA_SOURCE_WIND: // wind
+	case -1:
+		req.Source = 0
+		edbType = 2
+	default:
+		br.Msg = "暂不支持设置其他来源的指标"
+		return
+	}
+	// todo 批量设置刷新状态修改
+	edbIdList := make([]int, 0)
+	edbCodeList := make([]string, 0)
+	// 指标id列表
+	if req.IsSelectAll {
+		// 如果是列表全选
+		_, edbList, err := data.GetEdbRelationList(req.Source, edbType, req.ClassifyId, req.SysUserId, req.Frequency, req.Keyword, req.Status, 0, 100000, "", "")
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+
+		// 不配置的指标id
+		notIdMap := make(map[int]int, 0)
+		for _, v := range req.EdbSelectIdList {
+			notIdMap[v] = v
+		}
+
+		for _, v := range edbList {
+			_, ok := notIdMap[v.EdbInfoId]
+			// 在不配置的指标id列表内的话,那就过滤
+			if ok {
+				continue
+			}
+
+			// 加入到待配置的指标列表id
+			edbIdList = append(edbIdList, v.EdbInfoId)
+		}
+	} else {
+		edbIdList = req.EdbSelectIdList
+	}
+
+	if len(edbIdList) <= 0 {
+		br.Msg = "指标不能为空"
+		br.IsSendEmail = false
+		return
+	}
+	//查询指标信息
+	edbList, e := data_manage.GetEdbInfoByIdList(edbIdList)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + e.Error()
+		return
+	}
+	fromEdbIdList := make([]int, 0)
+	for _, v := range edbList {
+		if req.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			edbCodeList = append(edbCodeList, v.EdbCode)
+		}
+		if v.EdbInfoType == 0 {
+			fromEdbIdList = append(fromEdbIdList, v.EdbInfoId)
+		}
+	}
+
+	isStop := 0
+	if req.ModifyStatus == `暂停` {
+		isStop = 1
+	}
+
+	// 查询计算指标ID
+	// 查询相关的计算指标
+	calculateEdbIdList := make([]int, 0)
+	if isStop == 1 {
+		hasFind := make(map[int]struct{})
+		calculateEdbIdList, err = data.GetCalculateEdbByFromEdbInfo(fromEdbIdList, calculateEdbIdList, hasFind)
+		if err != nil {
+			br.Msg = "查询计算指标信息失败"
+			br.ErrMsg = "查询计算指标信息失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	switch req.Source {
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL: // 钢联化工
+		err = data_manage.ModifyMysteelChemicalUpdateStatusByEdbInfoIds(edbIdList, isStop, edbCodeList, calculateEdbIdList)
+	default:
+		err = data_manage.EdbInfoUpdateStatusByEdbInfoId(edbIdList, isStop, calculateEdbIdList)
 	}
 	if err != nil {
 		br.Msg = `保存失败`

+ 298 - 0
controllers/data_manage/edb_info_relation.go

@@ -0,0 +1,298 @@
+package data_manage
+
+import (
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/excel"
+	"eta/eta_api/models/fe_calendar"
+	"eta/eta_api/models/sandbox"
+	"eta/eta_api/services/data"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+// EdbInfoRelationController 指标引用管理
+type EdbInfoRelationController struct {
+	controllers.BaseAuthController
+}
+
+// RelationEdbList
+// @Title 获取被引用的指标列表接口
+// @Description 获取被引用的指标列表接口
+// @Param   Source   query   int  true       "来源:2:wind,34:钢联化工"
+// @Param   ClassifyId   query   string  false             "分类ID,支持多选,用英文,隔开"
+// @Param   SysUserId   query   string  false       "创建人,支持多选,用英文,隔开"
+// @Param   Frequency   query   string  false       "频度,支持多选,用英文,隔开"
+// @Param   Keyword   query   string  false       "关键词"
+// @Param   SortParam   query   string  false       "排序字段参数,用来排序的字段, 枚举值:'RelationTime':引用日期 'RelationNum' 引用次数"
+// @Param   SortType   query   string  true       "如何排序,是正序还是倒序,枚举值:`asc 正序`,`desc 倒叙`"
+// @Success 200 {object} data_manage.BaseRelationEdbInfoResp
+// @router /edb_info/relation/edb_list [get]
+func (c *EdbInfoRelationController) RelationEdbList() {
+	br := new(models.BaseResponse).Init()
+
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	source, _ := c.GetInt("Source")
+	classifyId := c.GetString("ClassifyId")
+	sysUserId := c.GetString("SysUserId")
+	frequency := c.GetString("Frequency")
+	keyword := c.GetString("Keyword")
+	status := c.GetString("Status")
+
+	sortParam := c.GetString("SortParam")
+	sortType := c.GetString("SortType")
+
+	switch sortParam {
+	case "RelationTime":
+		sortParam = " r.relation_time "
+	case "RelationNum":
+		sortParam = " r.relation_num "
+	default:
+		sortParam = " r.relation_time "
+	}
+	switch sortType {
+	case "desc", "asc":
+	default:
+		sortType = "desc"
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	edbType := 1      //基础指标
+	if source == -1 { //计算指标
+		edbType = 2
+		source = 0
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	total, list, err := data.GetEdbRelationList(source, edbType, classifyId, sysUserId, frequency, keyword, status, startSize, pageSize, sortParam, sortType)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	resp := data_manage.BaseRelationEdbInfoResp{
+		Paging: page,
+		List:   list,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// RelationEdbListDetail
+// @Title 获取引用详情接口
+// @Description 获取引用详情接口
+// @Param   EdbInfoId   query   int  true       "指标ID"
+// @Success 200 {object} data_manage.RefreshBaseEdbInfoResp
+// @router /edb_info/relation/detail [get]
+func (c *EdbInfoRelationController) RelationEdbListDetail() {
+	br := new(models.BaseResponse).Init()
+
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	edbInfoId, _ := c.GetInt("EdbInfoId")
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	total, relationList, err := data_manage.GetEdbInfoRelationDetailList(edbInfoId, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	list := make([]*data_manage.EdbInfoRelationDetail, 0)
+
+	// 查询逻辑图
+	sandboxIds := make([]int, 0)
+	// 查询图库
+	chartInfoIds := make([]int, 0)
+	// 查询表格
+	tableInfoIds := make([]int, 0)
+	// 查询事件日历
+	eventInfoIds := make([]int, 0)
+
+	for _, v := range relationList {
+		switch v.ReferObjectType {
+		case utils.EDB_RELATION_SANDBOX:
+			sandboxIds = append(sandboxIds, v.ReferObjectId)
+		case utils.EDB_RELATION_CALENDAR:
+			eventInfoIds = append(eventInfoIds, v.ReferObjectId)
+		case utils.EDB_RELATION_CHART:
+			chartInfoIds = append(chartInfoIds, v.ReferObjectId)
+		case utils.EDB_RELATION_TABLE:
+			tableInfoIds = append(tableInfoIds, v.ReferObjectId)
+		}
+	}
+	objectNameMap := make(map[int]string)
+	if len(sandboxIds) > 0 {
+		sandboxList, err := sandbox.GetSandboxNameByIds(sandboxIds)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range sandboxList {
+			objectNameMap[v.SandboxId] = v.Name
+		}
+	}
+
+	// 查询事件日历
+	if len(eventInfoIds) > 0 {
+		matterOb := new(fe_calendar.FeCalendarMatter)
+		cond := " AND fe_calendar_matter_id in (" + utils.GetOrmInReplace(len(eventInfoIds)) + ")"
+		pars := make([]interface{}, 0)
+		pars = append(pars, eventInfoIds)
+		eventList, err := matterOb.GetItemsByCondition(cond, pars, []string{}, "")
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取事件日历数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range eventList {
+			objectNameMap[v.FeCalendarMatterId] = fmt.Sprintf("%s, %s", v.MatterDate, v.ChartPermissionName)
+		}
+	}
+
+	// 查询图表
+	if len(chartInfoIds) > 0 {
+		chartList, err := data_manage.GetChartInfoByIdList(chartInfoIds)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range chartList {
+			objectNameMap[v.ChartInfoId] = v.ChartName
+		}
+	}
+
+	// 查询表格名称
+	if len(tableInfoIds) > 0 {
+		cond := " AND excel_info_id in (" + utils.GetOrmInReplace(len(tableInfoIds)) + ")"
+		pars := make([]interface{}, 0)
+		pars = append(pars, tableInfoIds)
+		tableList, err := excel.GetNoContentExcelInfoListByConditionNoPage(cond, pars)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取表格数据失败,Err:" + err.Error()
+			return
+		}
+		// 查询父级名称
+		excelParentName := make(map[int]string)
+		parentIds := make([]int, 0)
+		for _, v := range tableList {
+			if v.ParentId > 0 {
+				parentIds = append(parentIds, v.ParentId)
+			}
+		}
+		if len(parentIds) > 0 {
+			cond = " AND excel_info_id in (" + utils.GetOrmInReplace(len(parentIds)) + ")"
+			pars = make([]interface{}, 0)
+			pars = append(pars, parentIds)
+			parentList, err := excel.GetNoContentExcelInfoListByConditionNoPage(cond, pars)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取表格数据失败,Err:" + err.Error()
+				return
+			}
+			for _, v := range parentList {
+				excelParentName[v.ExcelInfoId] = v.ExcelName
+			}
+		}
+		for _, v := range tableList {
+			if v.ParentId > 0 {
+				parentName := excelParentName[v.ParentId]
+				objectNameMap[v.ExcelInfoId] = fmt.Sprintf("%s_%s", parentName, v.ExcelName)
+			} else {
+				objectNameMap[v.ExcelInfoId] = v.ExcelName
+			}
+		}
+	}
+	for _, v := range relationList {
+		referObjectName, _ := objectNameMap[v.ReferObjectId]
+		tmp := &data_manage.EdbInfoRelationDetail{
+			EdbInfoRelationId:  v.EdbInfoRelationId,
+			EdbInfoId:          v.EdbInfoId,
+			ReferObjectId:      v.ReferObjectId,
+			ReferObjectType:    v.ReferObjectType,
+			ReferObjectSubType: v.ReferObjectSubType,
+			RelationTime:       v.RelationTime.Format(utils.FormatDateTime),
+			ReferObjectName:    referObjectName,
+		}
+		switch v.ReferObjectType {
+		case utils.EDB_RELATION_SANDBOX:
+			tmp.ReferObjectTypeName = "逻辑图"
+		case utils.EDB_RELATION_CALENDAR:
+			tmp.ReferObjectTypeName = "事件日历"
+		case utils.EDB_RELATION_CHART:
+			switch v.ReferObjectSubType {
+			case utils.CHART_SOURCE_DEFAULT:
+				tmp.ReferObjectTypeName = "图库"
+			case utils.CHART_SOURCE_CORRELATION, utils.CHART_SOURCE_ROLLING_CORRELATION:
+				tmp.ReferObjectTypeName = "相关性分析"
+			case utils.CHART_SOURCE_LINE_EQUATION:
+				tmp.ReferObjectTypeName = "拟合方程曲线"
+			case utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE, utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
+				tmp.ReferObjectTypeName = "统计特征"
+			case utils.CHART_SOURCE_CROSS_HEDGING:
+				tmp.ReferObjectTypeName = "跨品种分析"
+			case utils.CHART_SOURCE_FUTURE_GOOD, utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
+				tmp.ReferObjectTypeName = "商品价格曲线"
+			}
+		case utils.EDB_RELATION_TABLE:
+			switch v.ReferObjectSubType {
+			case utils.TIME_TABLE:
+				tmp.ReferObjectTypeName = "时间序列表格"
+			case utils.MIXED_TABLE:
+				tmp.ReferObjectTypeName = "混合表格"
+			case utils.BALANCE_TABLE:
+				tmp.ReferObjectTypeName = "平衡表"
+			}
+		}
+		list = append(list, tmp)
+	}
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	resp := data_manage.BaseRelationEdbInfoDetailResp{
+		Paging: page,
+		List:   list,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 6 - 1
controllers/data_manage/excel/excel_info.go

@@ -308,6 +308,8 @@ func (c *ExcelInfoController) Add() {
 	resp.ExcelInfoId = excelInfo.ExcelInfoId
 	resp.UniqueCode = excelInfo.UniqueCode
 
+	//新增指标引用记录
+	_ = data.SaveExcelEdbInfoRelation(excelInfo.ExcelInfoId, excelInfo.Source, false)
 	//新增操作日志
 	//{
 	//	excelLog := &data_manage.ExcelInfoLog{
@@ -1064,7 +1066,8 @@ func (c *ExcelInfoController) Edit() {
 		ExcelInfoId: excelInfo.ExcelInfoId,
 		UniqueCode:  excelInfo.UniqueCode,
 	}
-
+	//新增指标引用记录
+	_ = data.SaveExcelEdbInfoRelation(excelInfo.ExcelInfoId, excelInfo.Source, false)
 	//删除公共图库那边的缓存
 	_ = utils.Rc.Delete(utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + excelInfo.UniqueCode)
 
@@ -2589,6 +2592,8 @@ func (c *ExcelInfoController) Copy() {
 		br.IsSendEmail = isSendEmail
 		return
 	}
+	//新增指标引用记录
+	_ = data.SaveExcelEdbInfoRelation(excelInfo.ExcelInfoId, excelInfo.Source, true)
 
 	resp := new(response.AddExcelInfoResp)
 	resp.ExcelInfoId = excelInfo.ExcelInfoId

+ 6 - 0
controllers/data_manage/future_good/future_good_chart_info.go

@@ -544,6 +544,9 @@ func (this *FutureGoodChartInfoController) ChartInfoAdd() {
 	resp.UniqueCode = chartInfo.UniqueCode
 	resp.ChartType = req.ChartType
 
+	chartInfo.ChartInfoId = chartInfoId
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfoId)
 
@@ -767,6 +770,9 @@ func (this *FutureGoodChartInfoController) ChartInfoEdit() {
 	resp.UniqueCode = chartItem.UniqueCode
 	resp.ChartType = req.ChartType
 
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
+
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
 	//修改my eta es数据

+ 13 - 1
controllers/data_manage/future_good/future_good_profit_chart_info.go

@@ -318,6 +318,8 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoAdd() {
 	resp.UniqueCode = chartInfo.UniqueCode
 	resp.ChartType = req.ChartType
 
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId)
 
@@ -639,6 +641,8 @@ func (this *FutureGoodChartInfoController) ProfitChartInfoEdit() {
 	resp.UniqueCode = chartItem.UniqueCode
 	resp.ChartType = req.ChartType
 
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
 	//修改my eta es数据
@@ -819,7 +823,15 @@ func copyProfitChartInfo(oldChartInfo *data_manage.ChartInfo, chartClassifyId in
 		br.ErrMsg = "保存商品利润图表失败,Err:" + err.Error()
 		return
 	}
-
+	// 添加指标引用记录
+	edbInfoIdArr := make([]int, 0)
+	edbInfoIdArrStr := strings.Split(oldChartInfo.EdbInfoIds, ",")
+	for _, v := range edbInfoIdArrStr {
+		i, _ := strconv.Atoi(v)
+		edbInfoIdArr = append(edbInfoIdArr, i)
+	}
+	//保存图表与指标的关系
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId)
 	//修改my eta es数据

+ 331 - 0
controllers/data_manage/wind_data.go

@@ -0,0 +1,331 @@
+package data_manage
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/response"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"sort"
+)
+
+// WindClassify
+// @Title wind指标分类
+// @Description wind指标分类
+// @Success 200 {object} data_manage.BaseFromYongyiClassify
+// @router /wind/classify [get]
+func (this *EdbInfoController) WindClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	// 默认查一级分类和一级分类下的指标信息,
+	// 如果是 子级分类,查询该子级分类的下一级分类和指标信息
+	// 增加标识判断是文件夹还是指标列表
+	parentId, _ := this.GetInt("ParentId")
+	// 特殊处理顶级分类
+	rootList, err := data_manage.GetEdbClassifyByParentId(parentId, 0)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	realRootMap := make(map[int]struct{})
+	if parentId == 0 {
+		// 查询wind指标的所有一级分类
+		classifyIdList, e := data_manage.GetEdbClassifyIdListBySource(utils.DATA_SOURCE_WIND)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类列表失败,Err:" + e.Error()
+			return
+		}
+		if len(classifyIdList) == 0 {
+			resp := new(data_manage.EdbClassifyListResp)
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			br.Data = resp
+			return
+		}
+		// 查询wind指标的所有一级分类下的指标信息
+		rootIds, e := data_manage.GetEdbClassifyRootIdsByClassifyIds(classifyIdList)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类列表失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range rootIds {
+			realRootMap[v] = struct{}{}
+		}
+	}
+	nodeAll := make([]*data_manage.EdbClassifyItems, 0)
+
+	var sortList data_manage.EdbClassifyItemList
+	if parentId > 0 {
+		// 查询挂在当前分类上的指标列表
+		// 获取当前账号的不可见指标
+		obj := data_manage.EdbInfoNoPermissionAdmin{}
+		confList, err := obj.GetAllListByAdminId(this.SysUser.AdminId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
+			return
+		}
+		noPermissionEdbInfoIdMap := make(map[int]bool)
+		for _, v := range confList {
+			noPermissionEdbInfoIdMap[v.EdbInfoId] = true
+		}
+		allEdbInfo, err := data_manage.GetEdbInfoByClassifyIdAndSource(parentId, 0, utils.DATA_SOURCE_WIND)
+		if err != nil {
+			br.Msg = "获取指标数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+
+		if len(allEdbInfo) > 0 {
+			// 查询当前子分类信息
+			/*currClassify, err := data_manage.GetEdbClassifyById(parentId)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取当前分类信息失败,Err:" + err.Error()
+				return
+			}
+			// 获取所有有权限的指标和分类
+			permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserEdbAndClassifyPermissionList(this.SysUser.AdminId, 0, 0)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
+				return
+			}*/
+			for _, v := range allEdbInfo {
+				// 如果指标不可见,那么就不返回该指标
+				if _, ok := noPermissionEdbInfoIdMap[v.EdbInfoId]; ok {
+					continue
+				}
+				//v.HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(v.IsJoinPermission, currClassify.IsJoinPermission, v.EdbInfoId, v.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
+				/*button := data.GetEdbOpButton(this.SysUser, v.SysUserId, v.EdbType, utils.EDB_INFO_TYPE, v.HaveOperaAuth)
+				button.AddButton = false //不管有没有权限,指标都是没有添加按钮的
+				v.Button = button*/
+				v.Children = make([]*data_manage.EdbClassifyItems, 0)
+				v.ParentId = parentId
+				nodeAll = append(nodeAll, v)
+			}
+		}
+
+	}
+	if len(rootList) > 0 {
+		// 已授权分类id
+		/*permissionClassifyIdList, err := data_manage_permission.GetUserEdbClassifyPermissionList(this.SysUser.AdminId, 0)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+			return
+		}*/
+
+		for _, v := range rootList {
+			// 数据权限
+			/*v.HaveOperaAuth = data_manage_permission.CheckEdbClassifyPermissionByPermissionIdList(v.IsJoinPermission, v.ClassifyId, permissionClassifyIdList)
+			// 按钮权限
+			button := data.GetEdbClassifyOpButton(this.SysUser, v.SysUserId, v.HaveOperaAuth)
+			v.Button = button*/
+			if _, ok := realRootMap[v.ClassifyId]; !ok && parentId == 0 { //查询一级分类时,过滤
+				continue
+			}
+			v.Children = make([]*data_manage.EdbClassifyItems, 0)
+			nodeAll = append(nodeAll, v)
+		}
+	}
+	if len(nodeAll) > 0 {
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.EdbLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.EdbLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	resp := new(data_manage.EdbClassifyListResp)
+	resp.AllNodes = sortList
+	resp.Language = language
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// WindEdbInfoList
+// @Title wind指标列表接口
+// @Description wind指标列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   int  true       "分类id"
+// @Success 200 {object} response.EdbInfoChartListResp
+// @router /wind/index [get]
+func (this *EdbInfoController) WindEdbInfoList() {
+	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
+	}
+
+	// 分页
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	// wind基础指标
+	var condition string
+	var pars []interface{}
+	condition += ` AND edb_info_type = ? AND source =? AND edb_type=?`
+	pars = append(pars, 0, utils.DATA_SOURCE_WIND, 1)
+
+	// 分类筛选
+	classifyId, _ := this.GetInt("ClassifyId")
+	edbInfoId, _ := this.GetInt("EdbInfoId")
+	if classifyId > 0 {
+		childClassify, e, _ := data.GetChildClassifyByClassifyId(classifyId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
+			return
+		}
+		var classifyIds []int
+		for _, v := range childClassify {
+			classifyIds = append(classifyIds, v.ClassifyId)
+		}
+		condition += fmt.Sprintf(` AND classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+		pars = append(pars, classifyIds)
+	}
+	if edbInfoId > 0 {
+		condition += ` AND edb_info_id = ?`
+		pars = append(pars, edbInfoId)
+	}
+
+	// 获取当前账号的不可见指标
+	obj := data_manage.EdbInfoNoPermissionAdmin{}
+	confList, e := obj.GetAllListByAdminId(this.SysUser.AdminId)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取不可见指标配置数据失败,Err:" + e.Error()
+		return
+	}
+	noPermissionEdbInfoIdList := make([]int, 0)
+	for _, v := range confList {
+		noPermissionEdbInfoIdList = append(noPermissionEdbInfoIdList, v.EdbInfoId)
+	}
+	noPermissionEdbInfoIdNum := len(noPermissionEdbInfoIdList)
+	if noPermissionEdbInfoIdNum > 0 {
+		condition += ` AND edb_info_id NOT IN (` + utils.GetOrmInReplace(noPermissionEdbInfoIdNum) + `) `
+		pars = append(pars, noPermissionEdbInfoIdList)
+	}
+	list := make([]*data_manage.WindEdbInfoList, 0)
+	// 获取指标信息
+	dataCount, edbList, e := data_manage.GetEdbInfoFilterList(condition, pars, startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取普通指标列表失败, Err:" + e.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, int(dataCount))
+	if len(edbList) > 0 {
+		classifyMap := make(map[int]*data_manage.EdbClassify)
+		targetClassifyList := make([]*data_manage.EdbClassifyItems, 0)
+		if edbInfoId > 0 && classifyId == 0 {
+			classifyId = edbList[0].ClassifyId
+		}
+		if classifyId > 0 { // todo 当没有传入分类ID时,如何处理 同一个分类ID,顶级分类是一样的
+			targetClassify, err := data_manage.GetEdbClassifyById(classifyId)
+			if err != nil {
+				if err.Error() == utils.ErrNoRow() {
+					br.Msg = "当前分类不存在"
+					return
+				}
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+				return
+			}
+			targetClassifyList, err = data_manage.GetEdbClassifyByRootIdLevel(targetClassify.RootId, targetClassify.ClassifyType, "")
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+				return
+			}
+			classifyIds := make([]int, 0)
+			for _, v := range edbList {
+				classifyIds = append(classifyIds, v.ClassifyId)
+			}
+			//查询分类信息
+			classifyList, err := data_manage.GetEdbClassifyByIdList(classifyIds)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+				return
+			}
+			for _, v := range classifyList {
+				classifyMap[v.ClassifyId] = v
+			}
+		}
+		for _, v := range edbList {
+			//查询目录
+			targetClassify, ok := classifyMap[v.ClassifyId]
+			if !ok {
+				br.Msg = "当前分类不存在"
+				return
+			}
+			classifyList, err, errMsg := data.GetFullClassifyByRootId(targetClassify, targetClassifyList)
+			if err != nil {
+				br.Msg = err.Error()
+				br.ErrMsg = errMsg
+				return
+			}
+			tmp := &data_manage.WindEdbInfoList{
+				EdbInfoList:  v,
+				ClassifyList: classifyList,
+			}
+			list = append(list, tmp)
+		}
+	}
+
+	resp := response.WindEdbInfoListResp{
+		Paging: page,
+		List:   list,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 67 - 3
controllers/english_report/email.go

@@ -9,14 +9,15 @@ import (
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/beego/beego/v2/server/web"
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"github.com/tealeg/xlsx"
 	"os"
 	"path"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/beego/beego/v2/server/web"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
 )
 
 // EnglishReportEmailController 英文研报邮箱/英文客户联系人
@@ -843,6 +844,69 @@ func (this *EnglishReportEmailController) PvList() {
 	br.Data = resp
 }
 
+// UvList
+// @Title 英文研报邮箱UV列表
+// @Description 英文研报邮箱UV列表
+// @Param   ReportId	query	int	false	"英文研报ID"
+// @Param   ReportType	query	int	false	"类型:0英文研报,1英文线上路演"
+// @Success 200 {object} models.EnglishReportEmailPvResp
+// @router /email/uv_list [get]
+func (this *EnglishReportEmailController) UvList() {
+	br := new(models.BaseResponse).Init()
+	br.IsSendEmail = false
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	reportId, e := this.GetInt("ReportId", 0)
+	if reportId == 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	reportType, e := this.GetInt("ReportType", 0)
+
+	var cond string
+	var pars []interface{}
+	cond += ` AND a.report_id = ? AND a.report_type= ?`
+	pars = append(pars, reportId, reportType)
+
+	var startSize int
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	total, list, e := models.GetEnglishReportEmailUvPageList(cond, pars, startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取英文研报邮箱uv列表失败, Err: " + e.Error()
+		return
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := &models.EnglishReportEmailUvPageListResp{
+		Paging: page,
+		List:   list,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
 // ImportListMatch
 // @Title 英文邮箱/联系人批量导入
 // @Description 英文邮箱/联系人批量导入

+ 2 - 0
controllers/fe_calendar/fe_calendar_matter.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/fe_calendar"
+	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
 	"fmt"
 	"strings"
@@ -323,6 +324,7 @@ func (this *FeCalendarMatterController) Save() {
 		return
 	}
 
+	_ = data.SaveCalendarEdbInfoRelation(req.ChartPermissionId, req.MatterDate, editMatters, removeMatters)
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 9 - 1
controllers/sandbox/sandbox.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/sandbox"
 	"eta/eta_api/models/sandbox/request"
 	"eta/eta_api/models/sandbox/response"
+	"eta/eta_api/services/data"
 	sandboxService "eta/eta_api/services/sandbox"
 	"eta/eta_api/utils"
 	"fmt"
@@ -636,7 +637,8 @@ func (this *SandboxController) Delete() {
 		br.Msg = err.Error()
 		return
 	}
-
+	// 删除图表中的指标引用
+	_ = data_manage.DeleteEdbRelationByObjectId(req.SandboxId, utils.EDB_RELATION_SANDBOX)
 	msg := "删除成功"
 	br.Ret = 200
 	br.Success = true
@@ -1802,6 +1804,7 @@ func (this *SandboxController) SaveV2() {
 
 	var errMsg string
 
+	var sandBoxData *sandbox.Sandbox
 	if req.SandboxId <= 0 {
 		//新增沙盘
 		sandboxResp, err = sandboxService.AddSandboxV2(req, sysUser.AdminId, sysUser.RealName)
@@ -1813,6 +1816,7 @@ func (this *SandboxController) SaveV2() {
 			br.ErrMsg = "保存失败,Err:" + err.Error()
 			return
 		}
+		sandBoxData = sandboxResp.Sandbox
 	} else {
 		//编辑沙盘
 		sandboxInfo := &sandbox.Sandbox{
@@ -1841,8 +1845,12 @@ func (this *SandboxController) SaveV2() {
 			br.ErrMsg = "保存失败,Err:" + err.Error()
 			return
 		}
+		sandBoxData = sandboxInfo
 	}
 
+	//解析逻辑图的指标
+	_ = data.SaveSandBoxEdbInfoRelation(sandBoxData.SandboxId, sandBoxData.Content)
+
 	msg := "保存成功"
 	br.Ret = 200
 	br.Success = true

+ 33 - 0
models/business_conf.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"html"
@@ -47,6 +48,8 @@ const (
 	BusinessConfTencentApiSecretKey          = "TencentApiSecretKey"          // 腾讯云API-密钥对
 	BusinessConfTencentApiRecTaskCallbackUrl = "TencentApiRecTaskCallbackUrl" // 腾讯云API-语音识别回调地址
 	BusinessConfSmsJhgjVariable              = "SmsJhgjVariable"              // 聚合国际短信变量
+
+	BusinessConfEdbStopRefreshRule = "EdbStopRefreshRule" // 是否停止指标刷新规则
 )
 
 const (
@@ -220,3 +223,33 @@ func GetBusinessConfByKey(key string) (item *BusinessConf, err error) {
 	err = o.Raw(sql, key).QueryRow(&item)
 	return
 }
+
+type BusinessConfSingleSaveReq struct {
+	ConfKey string `description:"配置Key"`
+	ConfVal string `description:"配置值"`
+}
+
+type EdbStopRefreshRule struct {
+	IsOpen            int `description:"是否开启自动禁用1,开启,0未开启"`
+	BaseIndexStopDays int `description:"数据源间隔天数未加入指标库则停用"`
+	EdbStopDays       int `description:"指标库间隔天数未引用则停用"`
+}
+
+type BusinessConfSingleResp struct {
+	ConfVal string
+}
+
+// InitUseMongoConf
+// @Description:
+// @author: Roc
+// @datetime 2024-07-01 13:49:09
+func InitUseMongoConf() {
+	useMongo, e := GetBusinessConfByKey("UseMongo")
+	if e != nil {
+		return
+	}
+
+	if useMongo.ConfVal == `true` {
+		utils.UseMongo = true
+	}
+}

+ 231 - 0
models/data_manage/base_from_business_data.go

@@ -0,0 +1,231 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// BaseFromBusinessData
+// @Description: 外部指标(商家系统)原始数据表
+type BaseFromBusinessData struct {
+	BusinessDataId          int       `orm:"column(business_data_id);pk" json:"business_data_id"`
+	BaseFromBusinessIndexId int       `json:"base_from_business_index_id"` // 指标id
+	IndexCode               string    `json:"index_code"`                  // 指标编码
+	DataTime                time.Time `json:"data_time"`                   // 数据日期
+	Value                   float64   `json:"value"`                       // 数据值
+	CreateTime              time.Time `json:"create_time"`                 // 创建时间
+	ModifyTime              time.Time `json:"modify_time"`                 // 修改时间
+}
+
+// TableName
+// @Description:  获取表名
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:41:36
+// @return string
+func (m *BaseFromBusinessData) TableName() string {
+	return "base_from_business_data"
+}
+
+// CollectionName
+// @Description:  获取集合名称
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:41:36
+// @return string
+func (m *BaseFromBusinessData) CollectionName() string {
+	return "base_from_business_data"
+}
+
+// DataBaseName
+// @Description: 获取数据库名称
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:41:33
+// @return string
+func (m *BaseFromBusinessData) DataBaseName() string {
+	return utils.MgoDataDbName
+}
+
+type WhereParams struct {
+	Condition string
+	Pars      []interface{}
+	Order     string `description:"排序字段"`
+}
+
+// GetAllDataList
+// @Description: 根据条件获取所有数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:42:19
+// @param sort []string
+// @param whereParams interface{}
+// @return result []BaseFromBusinessData
+// @return err error
+func (m *BaseFromBusinessData) GetAllDataList(condition string, pars []interface{}, order string) (result []*BaseFromBusinessData, err error) {
+	o := orm.NewOrmUsingDB("data")
+
+	sql := `SELECT * FROM base_from_business_data WHERE 1=1 `
+	if condition != `` {
+		sql += ` ` + condition
+	}
+
+	if order != `` {
+		sql += ` ORDER BY ` + order
+	}
+
+	_, err = o.Raw(sql, pars).QueryRows(&result)
+
+	return
+}
+
+// GetLimitDataList
+// @Description: 根据条件获取指定数量数据列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-06 17:08:32
+// @param whereParams interface{}
+// @param size int64
+// @return result []*BaseFromBusinessData
+// @return err error
+func (m *BaseFromBusinessData) GetLimitDataList(condition string, pars []interface{}, order string, size int64) (result []*BaseFromBusinessData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_business_data WHERE 1=1 `
+	if condition != `` {
+		sql += ` ` + condition
+	}
+
+	if order != `` {
+		sql += ` ORDER BY ` + order
+	}
+
+	sql += fmt.Sprintf(` LIMIT %d`, size)
+
+	_, err = o.Raw(sql, pars).QueryRows(&result)
+
+	return
+}
+
+// GetPageDataList
+// @Description: 根据条件获取分页数据列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-07 10:21:07
+// @param whereParams interface{}
+// @param startSize int64
+// @param size int64
+// @param sort []string
+// @return result []*BaseFromBusinessData
+// @return err error
+func (m *BaseFromBusinessData) GetPageDataList(condition []string, pars []interface{}, order string, startSize, size int64) (result []*BaseFromBusinessData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_business_data `
+	if len(condition) > 0 {
+		sql += ` WHERE ` + strings.Join(condition, " AND ")
+	}
+
+	if order != `` {
+		sql += ` ORDER BY ` + order
+	}
+
+	sql += fmt.Sprintf(` LIMIT %d,%d`, startSize, size)
+
+	_, err = o.Raw(sql, pars).QueryRows(&result)
+
+	return
+}
+
+// GetCountDataList
+// @Description:  根据条件获取数据列表总数
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-07 10:29:00
+// @param whereParams interface{}
+// @return count int64
+// @return err error
+func (m *BaseFromBusinessData) GetCountDataList(condition []string, pars []interface{}) (count int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) FROM base_from_business_data `
+	if len(condition) > 0 {
+		sql += ` WHERE ` + strings.Join(condition, " AND ")
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+
+	return
+}
+
+// InsertDataByColl
+// @Description: 写入单条数据(外部传入集合)
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param addData interface{}
+// @return err error
+func (m *BaseFromBusinessData) InsertDataByColl(addData interface{}) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(addData)
+
+	return
+}
+
+// BatchInsertData
+// @Description: 批量写入数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param bulk int 每次请求保存的数据量
+// @param dataList []interface{}
+// @return err error
+func (m *BaseFromBusinessData) BatchInsertData(bulk int, dataList []interface{}) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(bulk, dataList)
+
+	return
+}
+
+// UpdateData
+// @Description: 单条数据修改
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 15:01:51
+// @param whereParams interface{}
+// @param updateParams interface{}
+// @return err error
+func (m *BaseFromBusinessData) UpdateData(updateCols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, updateCols...)
+	if err != nil {
+		fmt.Println("UpdateDataByColl:Err:" + err.Error())
+		return
+	}
+
+	return
+}
+
+// GetEdbInfoMaxAndMinInfo
+// @Description: 获取当前指标的最大最小值
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 17:15:39
+// @param whereParams interface{}
+// @return result EdbInfoMaxAndMinInfo
+// @return err error
+func (m *BaseFromBusinessData) GetEdbInfoMaxAndMinInfo(indexCode string) (result EdbInfoMaxAndMinInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ``
+	sql = ` SELECT MIN(data_time) AS min_date,MAX(data_time) AS max_date,MIN(value) AS min_value,MAX(value) AS max_value FROM base_from_business_data WHERE index_code = ? `
+	err = o.Raw(sql, indexCode).QueryRow(&result)
+	if err != nil {
+		return
+	}
+
+	var latestValue float64
+	sql = ` SELECT value AS latest_value FROM base_from_business_data WHERE index_code = ? ORDER BY data_time DESC LIMIT 1 `
+	err = o.Raw(sql, indexCode).QueryRow(&latestValue)
+	result.LatestValue = latestValue
+
+	return
+}

+ 162 - 0
models/data_manage/base_from_edb_mapping.go

@@ -0,0 +1,162 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// BaseFromEdbMapping 同花顺高频数据
+type BaseFromEdbMapping struct {
+	Id              int       `orm:"column(id);pk"`
+	BaseFromIndexId int       `description:"源指标ID"`
+	BaseIndexCode   string    `description:"源指标编码"`
+	EdbInfoId       int       `description:"指标ID"`
+	EdbCode         string    `description:"指标编码"`
+	Source          int       `description:"指标来源:1-同花顺..."`
+	SubSource       int       `description:"子数据来源:0-经济数据库;1-日期序列;2-高频数据"`
+	ConvertRule     string    `description:"转换规则"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"修改时间"`
+}
+
+func (m *BaseFromEdbMapping) TableName() string {
+	return "base_from_edb_mapping"
+}
+
+type BaseFromEdbMappingCols struct {
+	PrimaryId       string
+	BaseFromIndexId string
+	BaseIndexCode   string
+	EdbInfoId       string
+	EdbCode         string
+	Source          string
+	SubSource       string
+	ConvertRule     string
+	CreateTime      string
+	ModifyTime      string
+}
+
+func (m *BaseFromEdbMapping) Cols() BaseFromEdbMappingCols {
+	return BaseFromEdbMappingCols{
+		PrimaryId:       "id",
+		BaseFromIndexId: "base_from_index_id",
+		BaseIndexCode:   "base_index_code",
+		EdbInfoId:       "edb_info_id",
+		EdbCode:         "edb_code",
+		Source:          "source",
+		SubSource:       "sub_source",
+		ConvertRule:     "convert_rule",
+		CreateTime:      "create_time",
+		ModifyTime:      "modify_time",
+	}
+}
+
+func (m *BaseFromEdbMapping) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *BaseFromEdbMapping) CreateMulti(items []*BaseFromEdbMapping) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *BaseFromEdbMapping) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *BaseFromEdbMapping) Remove() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *BaseFromEdbMapping) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func (m *BaseFromEdbMapping) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}
+
+func (m *BaseFromEdbMapping) GetItemById(id int) (item *BaseFromEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromEdbMapping) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromEdbMapping) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *BaseFromEdbMapping) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRows(&items)
+	return
+}
+
+func (m *BaseFromEdbMapping) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 509 - 0
models/data_manage/base_from_ths_hf.go

@@ -0,0 +1,509 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// TODO:有时间还是放在配置里面吧...
+var (
+	ThsHfPeriodArr = []int{1, 3, 5, 10, 15, 30, 60}                                                                                                                                // 时间周期
+	ThsHfCPSArr    = []string{"no", "forward1", "forward2", "forward3", "forward4", "backward1", "backward2", "backward3", "backward4"}                                            // 复权方式
+	ThsHfFillArr   = []string{"Original", "Previous", "Blank"}                                                                                                                     // 非交易间隔处理
+	ThsHfEdbCodeCn = map[string]string{"open": "开盘价", "close": "收盘价", "high": "最高价", "low": "最低价", "avgprice": "均价", "volume": "成交价", "amt": "成交额", "pct_chg": "涨跌幅", "oi": "持仓量"} // 可选指标代码对应中文
+)
+
+// BaseFromThsHfIndex 同花顺高频数据
+type BaseFromThsHfIndex struct {
+	BaseFromThsHfIndexId    int       `orm:"column(base_from_ths_hf_index_id);pk"`
+	BaseFromThsHfClassifyId int       `description:"分类ID"`
+	IndexCode               string    `description:"指标编码"`
+	IndexName               string    `description:"指标名称"`
+	Unit                    string    `description:"单位"`
+	Source                  string    `description:"数据来源"`
+	Frequency               string    `description:"频度"`
+	StartDate               time.Time `description:"开始日期(至时分秒)"`
+	EndDate                 time.Time `description:"结束日期(至时分秒)"`
+	Describe                string    `description:"指标描述"`
+	Sort                    int       `description:"排序"`
+	IsStop                  int       `description:"是否停更:0-否;1-停更"`
+	TerminalCode            string    `description:"所属终端编码"`
+	StockCode               string    `description:"证券代码"`
+	Indicator               string    `description:"同花顺指标代码"`
+	ApiPars                 string    `description:"API请求参数"`
+	LatestValue             float64   `description:"最新值"`
+	SysUserId               int       `description:"创建人ID"`
+	SysUserRealName         string    `description:"创建人姓名"`
+	CreateTime              time.Time `description:"创建时间"`
+	ModifyTime              time.Time `description:"修改时间"`
+}
+
+func (m *BaseFromThsHfIndex) TableName() string {
+	return "base_from_ths_hf_index"
+}
+
+type BaseFromThsHfIndexCols struct {
+	PrimaryId               string
+	BaseFromThsHfClassifyId string
+	IndexCode               string
+	IndexName               string
+	Unit                    string
+	Source                  string
+	Frequency               string
+	StartDate               string
+	EndDate                 string
+	Describe                string
+	Sort                    string
+	IsStop                  string
+	TerminalCode            string
+	StockCode               string
+	Indicator               string
+	ApiPars                 string
+	LatestValue             string
+	SysUserId               string
+	SysUserRealName         string
+	CreateTime              string
+	ModifyTime              string
+}
+
+func (m *BaseFromThsHfIndex) Cols() BaseFromThsHfIndexCols {
+	return BaseFromThsHfIndexCols{
+		PrimaryId:               "base_from_ths_hf_index_id",
+		BaseFromThsHfClassifyId: "base_from_ths_hf_classify_id",
+		IndexCode:               "index_code",
+		IndexName:               "index_name",
+		Unit:                    "unit",
+		Source:                  "source",
+		Frequency:               "frequency",
+		StartDate:               "start_date",
+		EndDate:                 "end_date",
+		Describe:                "describe",
+		Sort:                    "sort",
+		IsStop:                  "is_stop",
+		TerminalCode:            "terminal_code",
+		StockCode:               "stock_code",
+		Indicator:               "indicator",
+		ApiPars:                 "api_pars",
+		LatestValue:             "latest_value",
+		SysUserId:               "sys_user_id",
+		SysUserRealName:         "sys_user_real_name",
+		CreateTime:              "create_time",
+		ModifyTime:              "modify_time",
+	}
+}
+
+func (m *BaseFromThsHfIndex) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.BaseFromThsHfIndexId = int(id)
+	return
+}
+
+func (m *BaseFromThsHfIndex) CreateMulti(items []*BaseFromThsHfIndex) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *BaseFromThsHfIndex) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *BaseFromThsHfIndex) Remove() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sql, m.BaseFromThsHfIndexId).Exec()
+	return
+}
+
+func (m *BaseFromThsHfIndex) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func (m *BaseFromThsHfIndex) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}
+
+func (m *BaseFromThsHfIndex) GetItemById(id int) (item *BaseFromThsHfIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromThsHfIndex) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromThsHfIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromThsHfIndex) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *BaseFromThsHfIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromThsHfIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRows(&items)
+	return
+}
+
+func (m *BaseFromThsHfIndex) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromThsHfIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// BaseFromThsHfIndexItem 同花顺高频数据信息
+type BaseFromThsHfIndexItem 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:"结束日期(至时分秒)"`
+	Describe     string  `description:"指标描述"`
+	Sort         int     `description:"排序"`
+	StockCode    string  `description:"证券代码"`
+	Indicator    string  `description:"同花顺指标代码"`
+	LatestValue  float64 `description:"最新值"`
+	ClassifyPath string  `description:"完整分类路径"`
+	CreateTime   string  `description:"创建时间"`
+	ModifyTime   string  `description:"修改时间"`
+}
+
+func (m *BaseFromThsHfIndex) Format2Item() (item *BaseFromThsHfIndexItem) {
+	item = new(BaseFromThsHfIndexItem)
+	item.ClassifyId = m.BaseFromThsHfClassifyId
+	item.IndexId = m.BaseFromThsHfIndexId
+	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.FormatDateTime, m.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDateTime, m.EndDate)
+	item.Describe = m.Describe
+	item.Sort = m.Sort
+	item.StockCode = m.StockCode
+	item.Indicator = m.Indicator
+	item.LatestValue = m.LatestValue
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+func (m *BaseFromThsHfIndex) UpdateClassifyMulti(ids []int, classifyId int) (err error) {
+	if len(ids) == 0 || classifyId <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ?, %s = NOW() WHERE %s IN (%s)`, m.TableName(), m.Cols().BaseFromThsHfClassifyId, m.Cols().ModifyTime, m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, classifyId, ids).Exec()
+	return
+}
+
+// ThsHfSearchEdbReq 搜索指标请求体
+type ThsHfSearchEdbReq 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:"复权基点"`
+}
+
+// ThsHfExistCheckResp 指标存在校验响应
+type ThsHfExistCheckResp struct {
+	ExistAll   bool                   `description:"指标是否全部存在: true-是; false-否"`
+	ExistIndex []ThsHfExistCheckIndex `description:"已存在的指标信息"`
+}
+
+// ThsHfExistCheckIndex 指标存在校验-指标信息
+type ThsHfExistCheckIndex struct {
+	IndexId   int    `description:"指标ID"`
+	IndexCode string `description:"指标编码"`
+	IndexName string `description:"指标名称"`
+}
+
+// ThsHfSearchEdbResp 响应体
+type ThsHfSearchEdbResp struct {
+	StockCode string               `description:"证券代码" `
+	EdbCode   string               `description:"指标代码"`
+	IndexName string               `description:"指标名称"`
+	Frequency int                  `description:"频度: 1-60"`
+	IndexData []ThsHfSearchEdbData `description:"指标数据"`
+}
+
+type ThsHfSearchEdbData struct {
+	DataTime string
+	Value    string
+}
+
+// ThsHfIndexWithData 同花顺高频指标
+type ThsHfIndexWithData struct {
+	StockCode string            `description:"证券代码"`
+	EdbCode   string            `description:"指标代码"`
+	IndexData []*ThsHfIndexData `description:"指标数据"`
+}
+
+// ThsHfIndexData 同花顺高频指标数据
+type ThsHfIndexData struct {
+	DataTime time.Time `description:"数据时间(2006-01-02 15:04)"`
+	Value    float64   `description:"数据值"`
+}
+
+// ThsHfIndexDataLibResp 同花顺高频指标-指标库响应
+type ThsHfIndexDataLibResp struct {
+	Ret     int
+	Msg     string
+	ErrMsg  string
+	ErrCode string
+	Data    []*ThsHfIndexWithData
+	Success bool `description:"true 执行成功,false 执行失败"`
+}
+
+// ThsHfAddEdbReq 新增指标请求体
+type ThsHfAddEdbReq struct {
+	StartTime string                   `description:"每日数据开始时间"`
+	EndTime   string                   `description:"每日数据结束时间"`
+	Interval  int                      `description:"时间周期"`
+	Fill      string                   `description:"非交易间隔处理"`
+	CPS       string                   `description:"复权方式"`
+	BaseDate  string                   `description:"复权基点"`
+	IndexList []*ThsHfBaseAddIndexItem `description:"指标信息"`
+}
+
+type ThsHfBaseAddIndexItem struct {
+	ClassifyId int    `description:"分类ID"`
+	Unit       string `description:"单位"`
+	IndexName  string `description:"指标名称"`
+	Frequency  string `description:"频度"`
+	StockCode  string `description:"证券代码"`
+	EdbCode    string `description:"指标代码"`
+}
+
+type ThsHfBaseAddReq 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:"创建人姓名"`
+	ThsHfBaseAddIndexItem `description:"指标信息"`
+}
+
+// ThsHfIndexEditReq 编辑指标请求
+type ThsHfIndexEditReq struct {
+	IndexId    int    `description:"指标ID"`
+	IndexName  string `description:"指标名称"`
+	ClassifyId int    `description:"分类ID"`
+	Unit       string `description:"单位"`
+}
+
+// ThsHfIndexOptReq 指标操作请求
+type ThsHfIndexOptReq struct {
+	IndexId int `description:"指标ID"`
+}
+
+// ThsHfIndexListChoiceReq 指标列表选择请求
+type ThsHfIndexListChoiceReq 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"`
+}
+
+// ThsHfIndexListChoiceItem 指标列表选择响应
+type ThsHfIndexListChoiceItem struct {
+	IndexId   int    `description:"指标ID"`
+	IndexCode string `description:"指标编码"`
+	IndexName string `description:"指标名称"`
+}
+
+// ThsHfIndexMultiOptReq 指标批量操作请求
+type ThsHfIndexMultiOptReq struct {
+	IndexIds       []int `description:"指标IDs"`
+	OptType        int   `description:"操作类型: 1-移动分类; 2-删除; 3-刷新"`
+	MoveClassifyId int   `description:"移动至分类ID"`
+	RefreshType    int   `description:"刷新类型: 1-最近6小时; 2-全部刷新"`
+}
+
+// ThsHfIndexListForm 指标列表表单
+type ThsHfIndexListForm struct {
+	PageSize     int    `form:"PageSize" description:"每页数据量"`
+	CurrentIndex int    `form:"CurrentIndex" description:"页码"`
+	SortField    int    `form:"SortField" description:"排序字段: 1-指标开始时间; 2-指标最新时间; 3-更新时间; 4-最新值"`
+	SortType     int    `form:"SortType" description:"排序类型: 1-升序; 2-降序"`
+	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/指标名称"`
+}
+
+type ThsHfIndexPageListResp struct {
+	List   []*BaseFromThsHfIndexItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+func GetThsHfIndexById(indexId int) (item *BaseFromThsHfIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ths_hf_index WHERE base_from_ths_hf_index_id = ?`
+	err = o.Raw(sql, indexId).QueryRow(&item)
+	return
+}
+
+// UpdateThsHfIndexSortByClassifyId 根据分类id更新排序
+func UpdateThsHfIndexSortByClassifyId(classifyId, nowSort int, prevEdbInfoId int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update base_from_ths_hf_index set sort = ` + updateSort + ` WHERE base_from_ths_hf_classify_id=?`
+	if prevEdbInfoId > 0 {
+		sql += ` AND ( sort > ? or ( base_from_ths_hf_index_id > ` + fmt.Sprint(prevEdbInfoId) + ` and sort=` + fmt.Sprint(nowSort) + ` )) `
+	} else {
+		sql += ` AND ( sort > ? )`
+	}
+	_, err = o.Raw(sql, classifyId, nowSort).Exec()
+	return
+}
+
+// GetThsHfIndexMaxSortByClassifyId 获取分类下指标的最大的排序数
+func GetThsHfIndexMaxSortByClassifyId(classifyId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT Max(sort) AS sort FROM base_from_ths_hf_index WHERE base_from_ths_hf_classify_id = ?`
+	err = o.Raw(sql, classifyId).QueryRow(&sort)
+	return
+}
+
+// GetFirstThsHfIndexByClassifyId 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstThsHfIndexByClassifyId(classifyId int) (item *BaseFromThsHfIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ths_hf_index WHERE base_from_ths_hf_classify_id = ? order by sort asc,base_from_ths_hf_index_id asc limit 1`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+type ThsHfIndexConvert2EdbRule 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:"区间计算值"`
+}
+
+// ThsHfIndexMultiSave2EdbPreReq 批量添加指标库请求
+type ThsHfIndexMultiSave2EdbPreReq struct {
+	ConvertRule ThsHfIndexConvert2EdbRule
+	IndexIds    []int `description:"指标IDs"`
+}
+
+type ThsHfIndexMultiSave2EdbReq struct {
+	ConvertRule ThsHfIndexConvert2EdbRule
+	NewIndexes  []*ThsHfIndexMultiSave2EdbPreItem `description:"新增指标"`
+}
+
+type ThsHfIndexMultiSave2EdbLibReq struct {
+	ConvertRule ThsHfIndexConvert2EdbRule
+	NewIndex    *ThsHfIndexMultiSave2EdbPreItem `description:"新增指标"`
+}
+
+// ThsHfIndexMultiSave2EdbPreItem 批量新增指标库信息
+type ThsHfIndexMultiSave2EdbPreItem 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 ThsHfIndexMultiSave2EdbResp struct {
+	Exist   []*ThsHfIndexMultiSave2EdbPreItem `description:"已存在的指标"`
+	Success []*ThsHfIndexMultiSave2EdbPreItem `description:"添加成功的指标"`
+	Fail    []*ThsHfIndexMultiSave2EdbPreItem `description:"添加失败的指标"`
+}
+
+type ThsHfIndexMultiOptResp struct {
+	Success []*ThsHfIndexBaseInfo `description:"操作成功的指标"`
+	Fail    []*ThsHfIndexBaseInfo `description:"操作失败的指标"`
+}
+
+type ThsHfIndexBaseInfo struct {
+	IndexId   int    `description:"指标ID"`
+	IndexCode string `description:"指标编码"`
+	IndexName string `description:"指标名称"`
+}

+ 297 - 0
models/data_manage/base_from_ths_hf_classify.go

@@ -0,0 +1,297 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// BaseFromThsHfClassify 同花顺高频数据-分类
+type BaseFromThsHfClassify struct {
+	BaseFromThsHfClassifyId int       `orm:"column(base_from_ths_hf_classify_id);pk"`
+	ClassifyName            string    `description:"分类名称"`
+	ClassifyNameEn          string    `description:"英文分类名称"`
+	ParentId                int       `description:"父级ID"`
+	SysUserId               int       `description:"创建人ID"`
+	SysUserRealName         string    `description:"创建人姓名"`
+	Level                   int       `description:"层级"`
+	Sort                    int       `description:"排序"`
+	RootId                  int       `description:"顶级分类ID"`
+	LevelPath               string    `description:"层级路径"`
+	UniqueCode              string    `description:"唯一编码"`
+	CreateTime              time.Time `description:"创建时间"`
+	ModifyTime              time.Time `description:"修改时间"`
+}
+
+func (m *BaseFromThsHfClassify) TableName() string {
+	return "base_from_ths_hf_classify"
+}
+
+type BaseFromThsHfClassifyCols struct {
+	PrimaryId       string
+	ClassifyName    string
+	ClassifyNameEn  string
+	ParentId        string
+	SysUserId       string
+	SysUserRealName string
+	Level           string
+	Sort            string
+	RootId          string
+	LevelPath       string
+	UniqueCode      string
+	CreateTime      string
+	ModifyTime      string
+}
+
+func (m *BaseFromThsHfClassify) Cols() BaseFromThsHfClassifyCols {
+	return BaseFromThsHfClassifyCols{
+		PrimaryId:       "base_from_ths_hf_classify_id",
+		ClassifyName:    "classify_name",
+		ClassifyNameEn:  "classify_name_en",
+		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 *BaseFromThsHfClassify) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.BaseFromThsHfClassifyId = int(id)
+	return
+}
+
+func (m *BaseFromThsHfClassify) CreateMulti(items []*BaseFromThsHfClassify) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *BaseFromThsHfClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *BaseFromThsHfClassify) Remove() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sql, m.BaseFromThsHfClassifyId).Exec()
+	return
+}
+
+func (m *BaseFromThsHfClassify) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func (m *BaseFromThsHfClassify) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}
+
+func (m *BaseFromThsHfClassify) GetItemById(id int) (item *BaseFromThsHfClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromThsHfClassify) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromThsHfClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromThsHfClassify) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *BaseFromThsHfClassify) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromThsHfClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRows(&items)
+	return
+}
+
+func (m *BaseFromThsHfClassify) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromThsHfClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// BaseFromThsHfClassifyItem 同花顺高频数据信息
+type BaseFromThsHfClassifyItem struct {
+	ClassifyId     int                          `description:"分类ID"`
+	ClassifyName   string                       `description:"分类名称"`
+	ClassifyNameEn string                       `description:"英文分类名称"`
+	ParentId       int                          `description:"父级ID"`
+	Level          int                          `description:"层级"`
+	Sort           int                          `description:"排序"`
+	LevelPath      string                       `description:"层级路径"`
+	UniqueCode     string                       `description:"唯一编码"`
+	Children       []*BaseFromThsHfClassifyItem `description:"子分类"`
+}
+
+func (m *BaseFromThsHfClassify) Format2Item() (item *BaseFromThsHfClassifyItem) {
+	item = new(BaseFromThsHfClassifyItem)
+	item.ClassifyId = m.BaseFromThsHfClassifyId
+	item.ClassifyName = m.ClassifyName
+	item.ClassifyNameEn = m.ClassifyNameEn
+	item.ParentId = m.ParentId
+	item.Level = m.Level
+	item.Sort = m.Sort
+	item.LevelPath = m.LevelPath
+	item.UniqueCode = m.UniqueCode
+	item.Children = make([]*BaseFromThsHfClassifyItem, 0)
+	return
+}
+
+type ThsHfClassifyAddReq struct {
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级ID, 第一级传0"`
+	Level        int    `description:"层级, 第一级传0, 其余传上一级的层级"`
+}
+
+type ThsHfClassifyEditReq struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+}
+
+func (m *BaseFromThsHfClassify) GetSortMax(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT Max(%s) FROM %s WHERE %s = ?`, m.Cols().Sort, m.TableName(), m.Cols().ParentId)
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type ThsHfClassifyRemoveReq struct {
+	ClassifyId int `description:"分类ID"`
+}
+
+type BaseFromThsHfClassifyListItem struct {
+	ItemType       int                              `description:"类型: 0-分类; 1-指标"`
+	ClassifyId     int                              `description:"分类ID"`
+	ClassifyName   string                           `description:"分类名称"`
+	ClassifyNameEn 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       []*BaseFromThsHfClassifyListItem `description:"子分类"`
+}
+
+type BaseFromThsHfClassifyMoveReq 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 GetThsHfClassifyById(classifyId int) (item *BaseFromThsHfClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_ths_hf_classify WHERE base_from_ths_hf_classify_id = ?`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func GetThsHfClassifyByRootIdLevel(rootId int, orderStr string) (items []*BaseFromThsHfClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ths_hf_classify WHERE root_id = ? `
+	if orderStr != "" {
+		sql += orderStr
+	} else {
+		sql += ` order by level desc, sort asc, base_from_ths_hf_classify_id asc`
+	}
+	_, err = o.Raw(sql, rootId).QueryRows(&items)
+	return
+}
+
+// UpdateThsHfClassifySortByParentId 根据父类id更新排序
+func UpdateThsHfClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update base_from_ths_hf_classify set sort = ` + updateSort + ` WHERE parent_id = ? AND sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( base_from_ths_hf_classify_id > ` + fmt.Sprint(classifyId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// GetFirstThsHfClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func GetFirstThsHfClassifyByParentId(parentId int) (item *BaseFromThsHfClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ths_hf_classify WHERE parent_id = ? order by sort asc,base_from_ths_hf_classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+func UpdateThsHfClassifyChildByParentClassifyId(classifyIds []int, rootId int, levelStep int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	var pars []interface{}
+	pars = append(pars, rootId, levelStep)
+	pars = append(pars, classifyIds)
+	// 更新相关联的二级分类的parentId,和classify_name_second
+	sql := `update base_from_ths_hf_classify SET root_id = ?, level = level+? where base_from_ths_hf_classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	_, err = o.Raw(sql, pars).Exec()
+	if err != nil {
+		return
+	}
+	return
+}

+ 180 - 0
models/data_manage/base_from_ths_hf_data.go

@@ -0,0 +1,180 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// BaseFromThsHfData 同花顺高频数据-指标数据
+type BaseFromThsHfData struct {
+	BaseFromThsHfDataId  int       `orm:"column(base_from_ths_hf_data_id);pk"`
+	BaseFromThsHfIndexId int       `description:"指标ID"`
+	IndexCode            string    `description:"指标编码"`
+	DataTime             time.Time `description:"数据日期(至时分秒)"`
+	Value                float64   `description:"数据值"`
+	UniqueCode           string    `description:"唯一编码"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"修改时间"`
+	DataTimestamp        int64     `description:"数据日期时间戳"`
+}
+
+func (m *BaseFromThsHfData) TableName() string {
+	return "base_from_ths_hf_data"
+}
+
+type BaseFromThsHfDataCols struct {
+	PrimaryId            string
+	BaseFromThsHfIndexId string
+	IndexCode            string
+	DataTime             string
+	Value                string
+	UniqueCode           string
+	CreateTime           string
+	ModifyTime           string
+	DataTimestamp        string
+}
+
+func (m *BaseFromThsHfData) Cols() BaseFromThsHfDataCols {
+	return BaseFromThsHfDataCols{
+		PrimaryId:            "base_from_ths_hf_data_id",
+		BaseFromThsHfIndexId: "base_from_ths_hf_index_id",
+		IndexCode:            "index_code",
+		DataTime:             "data_time",
+		Value:                "value",
+		UniqueCode:           "unique_code",
+		CreateTime:           "create_time",
+		ModifyTime:           "modify_time",
+		DataTimestamp:        "data_timestamp",
+	}
+}
+
+func (m *BaseFromThsHfData) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.BaseFromThsHfDataId = int(id)
+	return
+}
+
+func (m *BaseFromThsHfData) CreateMulti(items []*BaseFromThsHfData) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *BaseFromThsHfData) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *BaseFromThsHfData) Remove() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sql, m.BaseFromThsHfDataId).Exec()
+	return
+}
+
+func (m *BaseFromThsHfData) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func (m *BaseFromThsHfData) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sql, pars).Exec()
+	return
+}
+
+func (m *BaseFromThsHfData) GetItemById(id int) (item *BaseFromThsHfData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromThsHfData) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromThsHfData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRow(&item)
+	return
+}
+
+func (m *BaseFromThsHfData) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *BaseFromThsHfData) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromThsHfData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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).QueryRows(&items)
+	return
+}
+
+func (m *BaseFromThsHfData) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromThsHfData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	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)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// BaseFromThsHfDataItem 同花顺高频数据信息
+type BaseFromThsHfDataItem struct {
+	DataId     int     `description:"数据ID"`
+	IndexId    int     `description:"指标ID"`
+	IndexCode  string  `description:"指标编码"`
+	DataTime   string  `description:"数据日期(至时分秒)"`
+	Value      float64 `description:"数据值"`
+	UniqueCode string  `description:"唯一编码"`
+}
+
+func (m *BaseFromThsHfData) Format2Item() (item *BaseFromThsHfDataItem) {
+	item = new(BaseFromThsHfDataItem)
+	item.DataId = m.BaseFromThsHfDataId
+	item.IndexId = m.BaseFromThsHfIndexId
+	item.IndexCode = m.IndexCode
+	item.DataTime = utils.TimeTransferString(utils.FormatDateTime, m.DataTime)
+	item.Value = m.Value
+	item.UniqueCode = m.UniqueCode
+	return
+}

+ 11 - 0
models/data_manage/chart_edb_mapping.go

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

+ 77 - 2
models/data_manage/chart_info.go

@@ -395,9 +395,12 @@ type EdbDataList struct {
 // @return err error
 func GetEdbDataList(source, subSource, edbInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
 	// 自有数据需要额外处理(从mongo获取)
-	if source == utils.DATA_SOURCE_BUSINESS {
+	if source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		return getEdbDataListByMongo(source, subSource, edbInfoId, startDate, endDate)
 	}
+	if source == utils.DATA_SOURCE_THS && subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		return getThsHfEdbDataListByMongo(source, subSource, edbInfoId, startDate, endDate)
+	}
 
 	return getEdbDataListByMysql(source, subSource, edbInfoId, startDate, endDate)
 
@@ -518,9 +521,12 @@ func GetEdbDataLunarList(endInfoId int, startDate, endDate string) (list []*EdbD
 // @return err error
 func GetEdbDataListMinAndMax(source, subSource, edbInfoId int, startDate, endDate string) (min_data, max_data float64, err error) {
 	// 自有数据需要额外处理(从mongo获取)
-	if source == utils.DATA_SOURCE_BUSINESS {
+	if source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		return getEdbDataListMinAndMaxByMongo(source, subSource, edbInfoId, startDate, endDate)
 	}
+	if source == utils.DATA_SOURCE_THS && subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		return getThsHfEdbDataListMinAndMaxByMongo(source, subSource, edbInfoId, startDate, endDate)
+	}
 
 	return getEdbDataListMinAndMaxByMysql(source, subSource, edbInfoId, startDate, endDate)
 }
@@ -2409,3 +2415,72 @@ func GetChartInfoBySourceAndParentId(source, parentId, adminId int) (items []*Ch
 	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
 }
+
+func getThsHfEdbDataListByMongo(source, subSource, edbInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
+	list = make([]*EdbDataList, 0)
+
+	mogDataObj := mgo.EdbDataThsHf{}
+	// 构建查询条件
+	queryConditions := bson.M{
+		"edb_info_id": edbInfoId,
+	}
+
+	// 数据日期
+	dateCondition, err := mgo.BuildDateCondition(startDate, endDate)
+	if err != nil {
+		return
+	}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
+	}
+
+	// 获取列表数据
+	tmpDataList, tmpErr := mogDataObj.GetAllDataList(queryConditions, []string{"data_time"})
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	for k, v := range tmpDataList {
+		list = append(list, &EdbDataList{
+			EdbDataId:     k + 1,
+			EdbInfoId:     v.EdbInfoId,
+			DataTime:      v.DataTime.Format(utils.FormatDate),
+			DataTimestamp: v.DataTimestamp,
+			Value:         v.Value,
+		})
+	}
+	return
+}
+
+func getThsHfEdbDataListMinAndMaxByMongo(source, subSource, edbInfoId int, startDate, endDate string) (minData, maxData float64, err error) {
+	mogDataObj := mgo.EdbDataThsHf{}
+	// 构建查询条件
+	queryConditions := bson.M{
+		"edb_info_id": edbInfoId,
+	}
+	// 日期
+	dateCondition, err := mgo.BuildDateCondition(startDate, endDate)
+	if err != nil {
+		return
+	}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
+	}
+
+	pipeline := []bson.M{
+		{"$match": queryConditions},
+		{"$group": bson.M{
+			"_id":       nil,
+			"min_value": bson.M{"$min": "$value"},
+			"max_value": bson.M{"$max": "$value"},
+		}},
+		{"$project": bson.M{"_id": 0}}, // 可选,如果不需要_id字段
+	}
+	result, err := mogDataObj.GetEdbInfoMaxAndMinInfo(pipeline)
+	if err != nil {
+		return
+	}
+	minData = result.MinValue
+	maxData = result.MaxValue
+	return
+}

+ 8 - 0
models/data_manage/edb_classify.go

@@ -653,3 +653,11 @@ func GetEdbClassifyByClassifyTypeAndIsJoinPermission(classifyType, isJoinPermiss
 	_, err = o.Raw(sql, classifyType, isJoinPermission).QueryRows(&items)
 	return
 }
+
+// GetEdbClassifyRootIdsByClassifyIds 获取普通指标的顶级分类列表
+func GetEdbClassifyRootIdsByClassifyIds(classifyIds []int) (items []int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT distinct root_id FROM edb_classify WHERE classify_type=0 and classify_id in (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	_, err = o.Raw(sql, classifyIds).QueryRows(&items)
+	return
+}

+ 66 - 4
models/data_manage/edb_data_base.go

@@ -14,10 +14,12 @@ import (
 func GetEdbDataTableName(source, subSource int) (tableName string) {
 	switch source {
 	case utils.DATA_SOURCE_THS:
-		tableName = "edb_data_ths"
-		if subSource == utils.DATA_SUB_SOURCE_DATE {
+		switch subSource {
+		case utils.DATA_SUB_SOURCE_DATE:
 			tableName = "edb_data_ths_ds"
-		} else {
+		case utils.DATA_SUB_SOURCE_HIGH_FREQUENCY:
+			tableName = "edb_data_ths_hf"
+		default:
 			tableName = "edb_data_ths"
 		}
 	case utils.DATA_SOURCE_WIND:
@@ -222,9 +224,12 @@ type EdbDataBase struct {
 
 func GetEdbDataAllByEdbCode(edbCode string, source, subSource, limit int) (items []*EdbInfoSearchData, err error) {
 	// 自有数据需要额外处理(从mongo获取)
-	if source == utils.DATA_SOURCE_BUSINESS {
+	if source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		return GetEdbDataAllByEdbCodeByMongo(edbCode, source, subSource, limit)
 	}
+	if source == utils.DATA_SOURCE_THS && subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		return GetThsHfEdbDataAllByEdbCodeByMongo(edbCode, source, subSource, limit)
+	}
 
 	return GetEdbDataAllByEdbCodeByMysql(edbCode, source, subSource, limit)
 }
@@ -403,3 +408,60 @@ func GetAddSql(edbInfoId, edbCode, dataTime, timestampStr string, value string)
 	addSql += "),"
 	return
 }
+
+func GetThsHfEdbDataAllByEdbCodeByMongo(edbCode string, source, subSource, limit int) (list []*EdbInfoSearchData, err error) {
+	list = make([]*EdbInfoSearchData, 0)
+
+	mogDataObj := mgo.EdbDataThsHf{}
+	// 构建查询条件
+	queryConditions := bson.M{
+		"edb_code": edbCode,
+	}
+
+	var tmpDataList []*mgo.EdbDataThsHf
+	// 获取列表数据
+	if limit > 0 {
+		tmpDataList, err = mogDataObj.GetLimitDataList(queryConditions, int64(limit), []string{"-data_time"})
+	} else {
+		tmpDataList, err = mogDataObj.GetLimitDataList(queryConditions, int64(limit), []string{"-data_time"})
+	}
+	if err != nil {
+		return
+	}
+	for _, v := range tmpDataList {
+		list = append(list, &EdbInfoSearchData{
+			DataTime: v.DataTime.Format(utils.FormatDate),
+			Value:    v.Value,
+		})
+	}
+	return
+}
+
+func GetThsHfEdbDataBaseMongoByEdbInfoId(edbInfoId int, source, subSource int) (list []*EdbDataBase, err error) {
+	list = make([]*EdbDataBase, 0)
+
+	mogDataObj := mgo.EdbDataThsHf{}
+	// 构建查询条件
+	queryConditions := bson.M{
+		"edb_info_id": edbInfoId,
+	}
+	// 获取列表数据
+	tmpDataList, tmpErr := mogDataObj.GetAllDataList(queryConditions, []string{"data_time"})
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	for k, v := range tmpDataList {
+
+		list = append(list, &EdbDataBase{
+			EdbDataId:     k + 1,
+			EdbInfoId:     v.EdbInfoId,
+			EdbCode:       v.EdbCode,
+			DataTime:      v.DataTime.Format(utils.FormatDate),
+			DataTimestamp: v.DataTimestamp,
+			Value:         strconv.FormatFloat(v.Value, 'f', -1, 64),
+		})
+	}
+
+	return
+}

+ 102 - 1
models/data_manage/edb_data_insert_config.go

@@ -133,8 +133,10 @@ func CreateEdbDataInsertConfigAndData(edbInfo *EdbInfo, date time.Time, value st
 	}
 
 	// 指标明细数据更新
-	if edbInfo.Source == utils.DATA_SOURCE_BUSINESS {
+	if edbInfo.Source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		dateStr, err = updateInsertConfigValueByMongo(to, edbInfo, oldConfigDate, date, value)
+	} else if edbInfo.Source == utils.DATA_SOURCE_THS && edbInfo.SubSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		dateStr, err = updateThsHfInsertConfigValueByMongo(to, edbInfo, oldConfigDate, date, value)
 	} else {
 		dateStr, err = updateInsertConfigValueByMysql(to, edbInfo, oldConfigDate, date, value)
 	}
@@ -336,3 +338,102 @@ func updateInsertConfigValueByMongo(to orm.TxOrmer, edbInfo *EdbInfo, oldConfigD
 
 	return
 }
+
+func updateThsHfInsertConfigValueByMongo(to orm.TxOrmer, edbInfo *EdbInfo, oldConfigDate, newDate time.Time, value string) (dateStr string, err error) {
+	tableName := GetEdbDataTableName(edbInfo.Source, edbInfo.SubSource)
+	if tableName == `` {
+		err = errors.New("找不到该指标的数据表")
+		return
+	}
+
+	dateStr = newDate.Format(utils.FormatDate)
+	timestamp := newDate.UnixNano() / 1e6
+	var floatValue float64
+	if value != "" {
+		floatValue, err = strconv.ParseFloat(value, 64)
+		if err != nil {
+			fmt.Println("转换失败:", err.Error())
+			return
+		}
+	}
+
+	mogDataObj := mgo.EdbDataThsHf{}
+	coll := mogDataObj.GetCollection()
+
+	var edbDateData *mgo.EdbDataThsHf
+	if !oldConfigDate.IsZero() {
+		// 构建查询条件
+		queryConditions := bson.M{
+			"edb_info_id": edbInfo.EdbInfoId,
+			"data_time":   oldConfigDate,
+		}
+
+		edbDateData, err = mogDataObj.GetItem(queryConditions)
+		//if tmpErr != nil && tmpErr == mongo.ErrNoDocuments {
+		//	err = tmpErr
+		//	return
+		//}
+		if err != nil && err != mongo.ErrNoDocuments {
+			return
+		}
+		err = nil
+
+	}
+
+	// 如果是没有历史数据,那么就需要增加数据
+	if edbDateData == nil {
+		addDataItem := mgo.EdbDataThsHf{
+			//ID:            primitive.ObjectID{},
+			EdbInfoId:     edbInfo.EdbInfoId,
+			EdbCode:       edbInfo.EdbCode,
+			DataTime:      newDate,
+			Value:         floatValue,
+			CreateTime:    time.Now(),
+			ModifyTime:    time.Now(),
+			DataTimestamp: timestamp,
+		}
+		err = mogDataObj.InsertDataByColl(coll, addDataItem)
+		if err != nil {
+			fmt.Println("mogDataObj.BatchInsertData() Err:" + err.Error())
+			return
+		}
+		return
+	}
+
+	// 数据清空
+	if value == "" {
+		queryConditions := bson.M{
+			"edb_info_id": edbInfo.EdbInfoId,
+		}
+
+		// 获取最新的两条数据
+		tmpDataList, tmpErr := mogDataObj.GetLimitDataList(queryConditions, 2, []string{"-data_time"})
+		if tmpErr != nil {
+			fmt.Println("mogDataObj.GetLimitDataList() Err:" + tmpErr.Error())
+			return
+		}
+		// 如果并没有两条数据,那么就返回
+		if len(tmpDataList) < 2 {
+			return
+		}
+
+		// 实际应该是倒数第二条数据的日期
+		dateStr = tmpDataList[1].DataTime.Format(utils.FormatDate)
+
+		// 删除插入的数据
+		err = mogDataObj.RemoveManyByColl(coll, bson.M{"_id": tmpDataList[0].ID})
+
+		return
+	}
+
+	// 更新配置的数据
+	updateData := bson.M{"$set": bson.M{
+		"value":          floatValue,
+		"modify_time":    time.Now(),
+		"data_time":      newDate,
+		"data_timestamp": timestamp,
+	}}
+	err = mogDataObj.UpdateDataByColl(coll, bson.M{"_id": edbDateData.ID}, updateData)
+
+	return
+}

+ 8 - 0
models/data_manage/edb_data_mysteel_chemical.go

@@ -11,3 +11,11 @@ func GetEdbDataMysteelChemicalMaxOrMinDate(edbCode string) (minDate, maxDate str
 	err = o.Raw(sql, edbCode).QueryRow(&minDate, &maxDate)
 	return
 }
+
+// 更新钢联化工指标的刷新状态
+func UpdateMysteelChemicalRefreshStatus(edbCode string, isStop int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = ? WHERE index_code =? and is_stop=1`
+	_, err = o.Raw(sql, isStop, edbCode).Exec()
+	return
+}

+ 41 - 0
models/data_manage/edb_data_wind.go

@@ -1,6 +1,7 @@
 package data_manage
 
 import (
+	"eta/eta_api/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"time"
 )
@@ -63,3 +64,43 @@ type EdbDataFromWind struct {
 	Dt     map[string]int64   `json:"DT"`
 	ErrMsg string
 }
+
+func EdbInfoUpdateStatusByEdbInfoId(edbInfoIds []int, isStop int, calculateEdbInfoIds []int) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	// 更改指标的更新状态
+	if len(edbInfoIds) == 1 {
+		sql := ` UPDATE edb_info SET no_update = ? WHERE edb_info_id=? `
+		_, err = o.Raw(sql, isStop, edbInfoIds[0]).Exec()
+		if err != nil {
+			return
+		}
+	} else {
+		sql := ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, edbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	if len(calculateEdbInfoIds) > 0 {
+		// 批量更新相关联的指标ID
+		sql := ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 158 - 12
models/data_manage/edb_info.go

@@ -254,8 +254,11 @@ func DeleteEdbInfoAndData(edbInfoId, source, subSource int) (err error) {
 	}
 
 	// 删除指标的明细数据
-	if source == utils.DATA_SOURCE_BUSINESS {
+	if source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		err = deleteAllEdbDataByMongo(to, edbInfoId, source, subSource)
+	} else if source == utils.DATA_SOURCE_THS && subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		mogDataObj := mgo.EdbDataThsHf{}
+		err = mogDataObj.RemoveMany(bson.M{"edb_info_id": edbInfoId})
 	} else {
 		err = deleteAllEdbDataByMysql(to, edbInfoId, source, subSource)
 	}
@@ -454,6 +457,11 @@ type EdbInfoListResp struct {
 	ClassifyList []*EdbClassifyIdItems
 }
 
+type WindEdbInfoList struct {
+	*EdbInfoList
+	ClassifyList []*EdbClassifyIdItems
+}
+
 func GetEdbInfoByCondition(condition string, pars []interface{}) (item *EdbInfoList, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT * FROM edb_info WHERE 1=1 `
@@ -614,9 +622,12 @@ type EdbInfoMaxAndMinInfo struct {
 // @return err error
 func GetEdbInfoMaxAndMinInfo(source, subSource int, edbCode string) (item *EdbInfoMaxAndMinInfo, err error) {
 	// 自有数据需要额外处理(从mongo获取)
-	if source == utils.DATA_SOURCE_BUSINESS {
+	if source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		return GetEdbInfoMaxAndMinInfoByMongo(source, subSource, edbCode)
 	}
+	if source == utils.DATA_SOURCE_THS && subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		return GetThsHfEdbInfoMaxAndMinInfoByMongo(source, subSource, edbCode)
+	}
 
 	// 默认走mysql
 	return GetEdbInfoMaxAndMinInfoByMysql(source, subSource, edbCode)
@@ -1157,6 +1168,17 @@ func GetAllCalculateByEdbInfoId(edbInfoId int) (mappingList []*EdbInfoCalculateM
 	return
 }
 
+// 获取指标的所有计算指标,以及计算指标所依赖计算指标
+func GetAllCalculateByEdbInfoIds(edbInfoIds []int) (mappingList []*EdbInfoCalculateMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	msql := ` SELECT * FROM edb_info_calculate_mapping WHERE from_edb_info_id in (` + utils.GetOrmInReplace(len(edbInfoIds)) + `)  GROUP BY edb_info_id `
+	_, err = o.Raw(msql, edbInfoIds).QueryRows(&mappingList)
+	if err != nil {
+		return
+	}
+	return
+}
+
 func GetAllCalculate(edbInfoId int, edbInfoMap map[int]int) (mappingList []*EdbInfoCalculateMapping, err error) {
 	calculateList, err := GetAllCalculateByEdbInfoId(edbInfoId)
 	if err != nil && err.Error() != utils.ErrNoRow() {
@@ -1549,13 +1571,14 @@ type BatchAddEdbInfoReq struct {
 }
 
 type BatchAddEdbInfo struct {
-	Source     int    `description:"来源id"`
-	EdbName    string `description:"指标名称"`
-	Frequency  string `description:"频率"`
-	Unit       string `description:"单位"`
-	ClassifyId int    `description:"分类id"`
-	StockCode  string `description:"证券代码"`
-	EdbCode    string `description:"指标编码"`
+	Source       int    `description:"来源id"`
+	EdbName      string `description:"指标名称"`
+	Frequency    string `description:"频率"`
+	Unit         string `description:"单位"`
+	ClassifyId   int    `description:"分类id"`
+	StockCode    string `description:"证券代码"`
+	EdbCode      string `description:"指标编码"`
+	ApiExtraPars string `description:"API额外参数"`
 }
 
 func GetEdbInfoWsdMaxAndMinInfo(source, subSource int, edbCode string) (item *EdbInfoMaxAndMinInfo, err error) {
@@ -1656,7 +1679,7 @@ func GetEdbBaseInfoList(condition string, pars []interface{}, orderBy string, st
 // @param indexCodeList []string
 // @param isStop int
 // @return err error
-func ModifyEdbInfoUpdateStatus(edbIdList []int, isStop int) (err error) {
+func ModifyEdbInfoUpdateStatus(edbIdList []int, isStop int, calculateEdbInfoIds []int) (err error) {
 	idNum := len(edbIdList)
 	if idNum <= 0 {
 		return
@@ -1679,7 +1702,14 @@ func ModifyEdbInfoUpdateStatus(edbIdList []int, isStop int) (err error) {
 	if err != nil {
 		return
 	}
-
+	if len(calculateEdbInfoIds) > 0 {
+		// 批量更新相关联的指标ID
+		sql = ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
 	return
 }
 
@@ -1733,9 +1763,12 @@ func GetEdbCodesBySource(source int) (items []*EdbInfo, err error) {
 // @return err error
 func GetAllEdbDataListData(edbInfoId, source, subSource int, startDataTime string) (dataList []*EdbData, err error) {
 	// 自有数据需要额外处理(从mongo获取)
-	if source == utils.DATA_SOURCE_BUSINESS {
+	if source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		return getAllDataByMongo(edbInfoId, source, subSource, startDataTime)
 	}
+	if source == utils.DATA_SOURCE_THS && subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		return getThsHfAllDataByMongo(edbInfoId, source, subSource, startDataTime)
+	}
 
 	// 默认走mysql
 	return getAllDataByMysql(edbInfoId, source, subSource, startDataTime)
@@ -1820,11 +1853,46 @@ func getAllDataByMongo(edbInfoId, source, subSource int, startDataTime string) (
 	return
 }
 
+// GetEdbClassifyIdListBySource 获取普通指标的分类列表
+func GetEdbClassifyIdListBySource(source int) (items []int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT distinct classify_id FROM edb_info WHERE source = ? and edb_info_type=0`
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}
+
+// GetEdbInfoByClassifyIdAndSource 用于分类展示
+func GetEdbInfoByClassifyIdAndSource(classifyId, edbInfoType, source int) (items []*EdbClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT edb_info_id,classify_id,edb_name AS classify_name,edb_name_en AS classify_name_en,unique_code,source_name,source,sys_user_id,sys_user_real_name,start_date,edb_code,edb_type, sort,is_join_permission FROM edb_info WHERE classify_id = ? AND edb_info_type = ? AND source =?`
+
+	pars := []interface{}{classifyId, edbInfoType, source}
+	sql += ` order by sort asc,edb_info_id asc `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
 type ReplaceEdbInfoItem struct {
 	OldEdbInfo *EdbInfo
 	NewEdbInfo *EdbInfo
 }
 
+func GetEdbInfoFieldList(cond string, pars []interface{}, fields []string) (items []*EdbInfo, err error) {
+	field := " * "
+	if len(fields) > 0 {
+		field = strings.Join(fields, ",")
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT %s FROM edb_info WHERE 1=1 %s `, field, cond)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// EdbInfoExtra 指标额外数据-extra字段
+type EdbInfoExtra struct {
+	ApiExtraPars string `description:"API-额外参数(如同花顺日期序列)"`
+}
+
 func GetEdbInfoListByCond(condition string, pars []interface{}) (list []*EdbInfoList, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT * FROM edb_info WHERE 1=1 `
@@ -1835,3 +1903,81 @@ func GetEdbInfoListByCond(condition string, pars []interface{}) (list []*EdbInfo
 	_, err = o.Raw(sql, pars).QueryRows(&list)
 	return
 }
+
+func GetThsHfEdbInfoMaxAndMinInfoByMongo(source, subSource int, edbCode string) (item *EdbInfoMaxAndMinInfo, err error) {
+	mogDataObj := new(mgo.EdbDataThsHf)
+	pipeline := []bson.M{
+		{"$match": bson.M{"edb_code": edbCode}},
+		{"$group": bson.M{
+			"_id":       nil,
+			"min_date":  bson.M{"$min": "$data_time"},
+			"max_date":  bson.M{"$max": "$data_time"},
+			"min_value": bson.M{"$min": "$value"},
+			"max_value": bson.M{"$max": "$value"},
+		}},
+		{"$project": bson.M{"_id": 0}}, // 可选,如果不需要_id字段
+	}
+	result, err := mogDataObj.GetEdbInfoMaxAndMinInfo(pipeline)
+	if err != nil {
+		fmt.Println("EdbDataThsHf getEdbDataThsHfList Err:" + err.Error())
+		return
+	}
+
+	if !result.MaxDate.IsZero() {
+		whereQuery := bson.M{"edb_code": edbCode, "data_time": result.MaxDate}
+		selectParam := bson.D{{"value", 1}, {"_id", 0}}
+		latestValue, tmpErr := mogDataObj.GetLatestValue(whereQuery, selectParam)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		result.LatestValue = latestValue.Value
+		result.EndValue = latestValue.Value
+	}
+
+	item = &EdbInfoMaxAndMinInfo{
+		MinDate:     result.MinDate.Format(utils.FormatDate),
+		MaxDate:     result.MaxDate.Format(utils.FormatDate),
+		MinValue:    result.MinValue,
+		MaxValue:    result.MaxValue,
+		LatestValue: result.LatestValue,
+	}
+
+	return
+}
+
+func getThsHfAllDataByMongo(edbInfoId, source, subSource int, startDataTime string) (dataList []*EdbData, err error) {
+	dataList = make([]*EdbData, 0)
+
+	mogDataObj := mgo.EdbDataThsHf{}
+	// 构建查询条件
+	queryConditions := bson.M{
+		"edb_info_id": edbInfoId,
+	}
+
+	// 开始日期
+	dateCondition, err := mgo.BuildDateCondition(startDataTime, "")
+	if err != nil {
+		return
+	}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
+	}
+
+	// 获取列表数据
+	tmpDataList, tmpErr := mogDataObj.GetAllDataList(queryConditions, []string{"-data_time"})
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	for k, v := range tmpDataList {
+		dataList = append(dataList, &EdbData{
+			EdbDataId: k + 1,
+			EdbInfoId: v.EdbInfoId,
+			DataTime:  v.DataTime.Format(utils.FormatDate),
+			Value:     v.Value,
+		})
+	}
+
+	return
+}

+ 11 - 0
models/data_manage/edb_info_calculate_mapping.go

@@ -1,6 +1,7 @@
 package data_manage
 
 import (
+	"eta/eta_api/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"time"
 )
@@ -101,3 +102,13 @@ func GetEdbInfoCalculateMappingListByEdbInfoId(edbInfoId int) (items []*EdbInfoC
 	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
 	return
 }
+
+// GetEdbInfoCalculateMappingListByEdbInfoIds 根据生成的指标id获取来源的指标id列表
+func GetEdbInfoCalculateMappingListByEdbInfoIds(edbInfoIds []int) (items []*EdbInfoCalculateMappingInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT a.*,b.edb_type as from_edb_type,b.edb_info_type as from_edb_info_type, b.unique_code AS from_unique_code, b.classify_id AS from_classify_id,b.no_update FROM edb_info_calculate_mapping AS a
+			INNER JOIN edb_info AS b ON a.from_edb_info_id=b.edb_info_id
+			WHERE a.edb_info_id in (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+	_, err = o.Raw(sql, edbInfoIds).QueryRows(&items)
+	return
+}

+ 518 - 0
models/data_manage/edb_info_relation.go

@@ -0,0 +1,518 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type EdbInfoRelation struct {
+	EdbInfoRelationId  int       `orm:"column(edb_info_relation_id);pk"`
+	EdbInfoId          int       `description:"指标id"`
+	Source             int       `description:"来源:1:同花顺,2:wind,3:彭博,4:指标运算,5:累计值转月,6:同比值,7:同差值,8:N数值移动平均计算,9:手工指标,10:隆众"`
+	EdbName            string    `description:"指标名称"`
+	EdbCode            string    `description:"指标编码"`
+	ReferObjectId      int       `description:"引用对象ID(图表ID,ETA逻辑ID等)"`
+	ReferObjectType    int       `description:"引用对象ID类型(1.图表,2.ETA逻辑)"`
+	ReferObjectSubType int       `description:"引用对象子类"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+	RelationTime       time.Time `description:"引用时间"`
+	RelationType       int       `description:"引用类型,0:直接饮用,1间接引用"`
+	RootEdbInfoId      int       `description:"间接引用时,关联的直接引用的指标ID"`
+	ChildEdbInfoId     int       `description:"间接引用时,关联的计算指标ID"`
+	RelationCode       string    `description:"引用标识"`
+	ParentRelationId   int       `description:"间接引用关联的直接引用的ID"`
+}
+
+func (e *EdbInfoRelation) TableName() string {
+	return "edb_info_relation"
+}
+
+// GetEdbInfoRelationByRelationIds 查询引用的指标ID
+func GetEdbInfoRelationByRelationIds(ids []int) (items []*EdbInfoRelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	msql := ` SELECT * FROM edb_info_relation WHERE edb_info_relation_id in (` + utils.GetOrmInReplace(len(ids)) + `) `
+	_, err = o.Raw(msql, ids).QueryRows(&items)
+	return
+}
+
+// GetEdbInfoRelationByReferObjectId 查询直接引用的指标ID
+func GetEdbInfoRelationByReferObjectId(referObjectId int, referObjectType int) (items []*EdbInfoRelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	msql := ` SELECT * FROM edb_info_relation WHERE refer_object_id =? and relation_type=0 AND refer_object_type=?  GROUP BY edb_info_id `
+	_, err = o.Raw(msql, referObjectId, referObjectType).QueryRows(&items)
+	return
+}
+
+// GetEdbInfoRelationByReferObjectIds 查询引用的指标ID
+func GetEdbInfoRelationByReferObjectIds(referObjectIds []int, referObjectType int) (items []*EdbInfoRelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	msql := ` SELECT * FROM edb_info_relation WHERE refer_object_id in (` + utils.GetOrmInReplace(len(referObjectIds)) + `) AND refer_object_type=? and relation_type=0`
+	_, err = o.Raw(msql, referObjectIds, referObjectType).QueryRows(&items)
+	return
+}
+
+// GetEdbInfoRelationAllByReferObjectIds 查询引用的指标ID
+func GetEdbInfoRelationAllByReferObjectIds(referObjectIds []int, referObjectType int) (items []*EdbInfoRelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	msql := ` SELECT * FROM edb_info_relation WHERE refer_object_id in (` + utils.GetOrmInReplace(len(referObjectIds)) + `) AND refer_object_type=?`
+	_, err = o.Raw(msql, referObjectIds, referObjectType).QueryRows(&items)
+	return
+}
+
+// 新增记录
+func AddOrUpdateEdbInfoRelation(objectId, objectType int, relationList []*EdbInfoRelation, deleteEdbInfoIds []int, refreshEdbInfoIds []int, indexCodeList []string) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	if len(deleteEdbInfoIds) > 0 {
+		sql := ` DELETE FROM edb_info_relation WHERE refer_object_id = ? AND refer_object_type=? AND edb_info_id in (` + utils.GetOrmInReplace(len(deleteEdbInfoIds)) + `) AND relation_type=0`
+		_, err = o.Raw(sql, objectId, objectType, deleteEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+		// 同时删除相关连的间接引用的指标ID
+		sql = ` DELETE FROM edb_info_relation WHERE refer_object_id = ? AND refer_object_type=? AND root_edb_info_id in (` + utils.GetOrmInReplace(len(deleteEdbInfoIds)) + `) AND relation_type=1 `
+		_, err = o.Raw(sql, objectId, objectType, deleteEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+	relationCodesMap := make(map[string]struct{}, 0)
+	if len(relationList) > 0 {
+		for _, relation := range relationList {
+			if relation.RelationType == 1 {
+				relationCodesMap[relation.RelationCode] = struct{}{}
+			}
+		}
+		_, err = o.InsertMulti(len(relationList), relationList)
+		if err != nil {
+			return
+		}
+	}
+
+	if len(refreshEdbInfoIds) > 0 {
+		//todo 是否需要所有指标的刷新状态
+		sql := ` UPDATE edb_info SET no_update = 0 WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND  no_update = 1`
+		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	//更新数据源钢联化工指标
+	if len(indexCodeList) > 0 {
+		// 更改数据源的更新状态
+		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
+		_, err = o.Raw(sql, indexCodeList).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	if len(relationList) > 0 {
+		// 更新间接引用指标的关联ID
+		relationCodes := make([]string, 0)
+		for relationCode := range relationCodesMap {
+			relationCodes = append(relationCodes, relationCode)
+		}
+		if len(relationCodes) > 0 {
+			sql := ` UPDATE edb_info_relation e1  
+JOIN edb_info_relation e2 ON e1.relation_code = e2.relation_code   
+SET e1.parent_relation_id = e2.edb_info_relation_id  
+WHERE  
+    e1.relation_type = 1   
+    AND e2.relation_type = 0 AND e1.parent_relation_id !=e2.edb_info_relation_id AND e1.relation_code in (` + utils.GetOrmInReplace(len(relationCodes)) + `)`
+			_, err = o.Raw(sql, relationCodes).Exec()
+			if err != nil {
+				return
+			}
+		}
+	}
+	return
+}
+
+// 新增记录
+func AddOrUpdateEdbInfoRelationMulti(relationList []*EdbInfoRelation, refreshEdbInfoIds []int, indexCodeList []string) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	relationCodesMap := make(map[string]struct{}, 0)
+	if len(relationList) > 0 {
+		for _, relation := range relationList {
+			if relation.RelationType == 1 {
+				relationCodesMap[relation.RelationCode] = struct{}{}
+			}
+		}
+		_, err = o.InsertMulti(len(relationList), relationList)
+		if err != nil {
+			return
+		}
+	}
+
+	if len(refreshEdbInfoIds) > 0 {
+		// todo 更新指标的刷新状态
+		sql := ` UPDATE edb_info SET no_update = 0 WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
+		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	//更新数据源钢联化工指标
+	if len(indexCodeList) > 0 {
+		// 更改数据源的更新状态
+		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
+		_, err = o.Raw(sql, indexCodeList).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	if len(relationList) > 0 {
+		// 更新间接引用指标的关联ID
+		relationCodes := make([]string, 0)
+		for relationCode := range relationCodesMap {
+			relationCodes = append(relationCodes, relationCode)
+		}
+		if len(relationCodes) > 0 {
+			sql := ` UPDATE edb_info_relation e1  
+JOIN edb_info_relation e2 ON e1.relation_code = e2.relation_code   
+SET e1.parent_relation_id = e2.edb_info_relation_id  
+WHERE  
+    e1.relation_type = 1   
+    AND e2.relation_type = 0 AND e1.parent_relation_id !=e2.edb_info_relation_id AND e1.relation_code in (` + utils.GetOrmInReplace(len(relationCodes)) + `)`
+			_, err = o.Raw(sql, relationCodes).Exec()
+			if err != nil {
+				return
+			}
+		}
+	}
+	return
+}
+
+// 删除指标引用内容
+func DeleteEdbRelationByObjectIds(referObjectIds []int, referObjectType int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` DELETE FROM edb_info_relation WHERE refer_object_id in (` + utils.GetOrmInReplace(len(referObjectIds)) + `) AND refer_object_type=?`
+	_, err = o.Raw(sql, referObjectIds, referObjectType).Exec()
+	return
+}
+
+// DeleteEdbRelationByObjectId 删除指标引用内容
+func DeleteEdbRelationByObjectId(referObjectId int, referObjectType int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` DELETE FROM edb_info_relation WHERE refer_object_id =? AND refer_object_type=?`
+	_, err = o.Raw(sql, referObjectId, referObjectType).Exec()
+	return
+}
+
+type BaseRelationEdbInfo struct {
+	EdbInfoId       int
+	ClassifyId      int    `description:"指标分类id"`
+	EdbName         string `description:"指标名称"`
+	EdbCode         string `description:"指标编码"`
+	SysUserId       int    `description:"创建人id"`
+	SysUserRealName string `description:"创建人姓名"`
+	Frequency       string `description:"频度"`
+	IsStop          int    `description:"是否停更:1:停更,0:未停更"`
+	RelationNum     int    `description:"引用次数"`
+	RelationTime    string `description:"引用时间"`
+}
+
+type BaseRelationEdbInfoResp struct {
+	Paging *paging.PagingItem
+	List   []*BaseRelationEdbInfo
+}
+
+type EdbInfoRelationDetail struct {
+	EdbInfoRelationId   int    `orm:"column(edb_info_relation_id);pk"`
+	EdbInfoId           int    `description:"指标id"`
+	ReferObjectId       int    `description:"引用对象ID(图表ID,ETA逻辑ID等)"`
+	ReferObjectTypeName string `description:"引用对象类型"`
+	ReferObjectType     int    `description:"引用对象ID类型(1.图表,2.ETA逻辑)"`
+	ReferObjectSubType  int    `description:"引用对象子类"`
+	RelationTime        string `description:"引用时间"`
+	ReferObjectName     string `description:"引用对象名称"`
+}
+type BaseRelationEdbInfoDetailResp struct {
+	Paging *paging.PagingItem
+	List   []*EdbInfoRelationDetail
+}
+
+// 查询指标引用列表
+func GetEdbInfoRelationList(condition string, pars []interface{}, orderBy string, startSize, pageSize int) (total int, items []*BaseRelationEdbInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 数量汇总
+	totalSql := ` SELECT count(1) FROM edb_info e LEFT JOIN (
+SELECT count(edb_info_id) as relation_num, edb_info_id, max(relation_time) as relation_time FROM edb_info_relation GROUP BY edb_info_id) r on e.edb_info_id=r.edb_info_id WHERE 1=1 `
+	if condition != "" {
+		totalSql += condition
+	}
+	err = o.Raw(totalSql, pars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	// 列表数据
+	sql := ` SELECT e.edb_info_id, e.classify_id,e.edb_code,e.edb_name,e.sys_user_id,e.sys_user_real_name,e.frequency,e.no_update as is_stop, r.relation_num, r.relation_time from edb_info e LEFT JOIN (
+SELECT count(edb_info_id) as relation_num, edb_info_id, max(relation_time) as relation_time FROM edb_info_relation GROUP BY edb_info_id) r on e.edb_info_id=r.edb_info_id WHERE 1=1
+ `
+	if condition != "" {
+		sql += condition
+	}
+
+	if orderBy != "" {
+		sql += ` ORDER BY ` + orderBy
+	} else {
+		sql += ` ORDER BY edb_info_id ASC `
+	}
+	sql += `  LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+
+	return
+}
+
+// GetEdbInfoRelationDetailList 查询指标引用详情列表
+func GetEdbInfoRelationDetailList(edbInfoId int, startSize, pageSize int) (total int, items []*EdbInfoRelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 数量汇总
+	totalSql := ` SELECT count(*) FROM edb_info_relation where edb_info_id=?`
+	err = o.Raw(totalSql, edbInfoId).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	// 列表数据
+	sql := ` SELECT *FROM edb_info_relation where edb_info_id=? ORDER BY relation_time desc, edb_info_id ASC `
+	sql += `  LIMIT ?,? `
+	_, err = o.Raw(sql, edbInfoId, startSize, pageSize).QueryRows(&items)
+
+	return
+}
+
+// 查询相关的指标记录总数
+func GetReplaceChildEdbInfoRelationTotal(edbInfoId int) (total int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 数量汇总
+	totalSql := ` SELECT count(*) FROM edb_info_relation where  relation_type=1 and (child_edb_info_id=? or edb_info_id=? ) group by parent_relation_id`
+	err = o.Raw(totalSql, edbInfoId, edbInfoId).QueryRow(&total)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// 查询相关的指标记录列表
+func GetReplaceChildEdbInfoRelationList(edbInfoId int, startSize, pageSize int) (items []*EdbInfoRelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 列表数据
+	sql := ` SELECT * FROM edb_info_relation where  relation_type=1 and (child_edb_info_id=? or edb_info_id=? )  group by parent_relation_id ORDER BY edb_info_relation_id ASC  LIMIT ?,? `
+	_, err = o.Raw(sql, edbInfoId, edbInfoId, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// 查询相关的指标记录总数
+func GetReplaceEdbInfoRelationTotal(edbInfoId int) (total int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 数量汇总
+	totalSql := ` SELECT count(*) FROM edb_info_relation where edb_info_id=? and relation_type = 0`
+	err = o.Raw(totalSql, edbInfoId).QueryRow(&total)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// 查询相关的指标记录列表
+func GetReplaceEdbInfoRelationList(edbInfoId int, startSize, pageSize int) (items []*EdbInfoRelation, err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 列表数据
+	sql := ` SELECT * FROM edb_info_relation where edb_info_id=? and relation_type = 0 ORDER BY edb_info_relation_id ASC LIMIT ?,? `
+	_, err = o.Raw(sql, edbInfoId, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// 替换指标引用表中直接引用的指标
+func ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo *EdbInfo, edbRelationIds []int, relationList []*EdbInfoRelation, refreshEdbInfoIds []int, indexCodeList []string) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	now := time.Now()
+	// 删除相关的间接引用
+	sql := ` DELETE FROM edb_info_relation WHERE root_edb_info_id=?  and relation_type=1 and parent_relation_id in (` + utils.GetOrmInReplace(len(edbRelationIds)) + `)`
+	_, err = o.Raw(sql, oldEdbInfo.EdbInfoId, edbRelationIds).Exec()
+	if err != nil {
+		return
+	}
+
+	sourceWhere := ` and (refer_object_type in (1,2 ) or (refer_object_type=4 and refer_object_sub_type !=5) )` //平衡表和事件日历中的直接引用无需替换,
+	// 替换edb_info_id
+	sql = ` UPDATE edb_info_relation SET edb_info_id=?, source=?, edb_name=?, edb_code=?, modify_time=?, relation_time=?  WHERE edb_info_id=? ` + sourceWhere + ` and relation_type=0 and edb_info_relation_id in (` + utils.GetOrmInReplace(len(edbRelationIds)) + `)`
+	_, err = o.Raw(sql, newEdbInfo.EdbInfoId, newEdbInfo.Source, newEdbInfo.EdbName, newEdbInfo.EdbCode, now, now, oldEdbInfo.EdbInfoId, edbRelationIds).Exec()
+	if err != nil {
+		return
+	}
+
+	// 更新code值
+	sql = ` UPDATE edb_info_relation SET relation_code=CONCAT_WS("_", edb_info_id,refer_object_id,refer_object_type,refer_object_sub_type)  WHERE relation_type=0 ` + sourceWhere + `  and edb_info_relation_id in (` + utils.GetOrmInReplace(len(edbRelationIds)) + `)`
+	_, err = o.Raw(sql, edbRelationIds).Exec()
+	if err != nil {
+		return
+	}
+	// 新增间接引用
+	relationCodesMap := make(map[string]struct{}, 0)
+	if len(relationList) > 0 {
+		for _, relation := range relationList {
+			if relation.RelationType == 1 {
+				relationCodesMap[relation.RelationCode] = struct{}{}
+			}
+		}
+		_, err = o.InsertMulti(len(relationList), relationList)
+		if err != nil {
+			return
+		}
+	}
+
+	if len(refreshEdbInfoIds) > 0 {
+		// todo 更新指标的刷新状态
+		sql := ` UPDATE edb_info SET no_update = 0 WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
+		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	//更新数据源钢联化工指标
+	if len(indexCodeList) > 0 {
+		// 更改数据源的更新状态
+		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
+		_, err = o.Raw(sql, indexCodeList).Exec()
+		if err != nil {
+			return
+		}
+	}
+	if len(relationList) > 0 {
+		// 更新间接引用指标的关联ID
+		relationCodes := make([]string, 0)
+		for relationCode := range relationCodesMap {
+			relationCodes = append(relationCodes, relationCode)
+		}
+		if len(relationCodes) > 0 {
+			sql := ` UPDATE edb_info_relation e1  
+JOIN edb_info_relation e2 ON e1.relation_code = e2.relation_code   
+SET e1.parent_relation_id = e2.edb_info_relation_id  
+WHERE  
+    e1.relation_type = 1   
+    AND e2.relation_type = 0 AND e1.parent_relation_id !=e2.edb_info_relation_id AND e1.relation_code in (` + utils.GetOrmInReplace(len(relationCodes)) + `)`
+			_, err = o.Raw(sql, relationCodes).Exec()
+			if err != nil {
+				return
+			}
+		}
+	}
+	return
+}
+
+// UpdateSecondRelationEdbInfoId 更新指标替换后的间接引用记录
+func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfoRelation, refreshEdbInfoIds []int, indexCodeList []string) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+	// 删除相关的间接引用
+	sql := ` DELETE FROM edb_info_relation WHERE relation_type=1 and parent_relation_id in (` + utils.GetOrmInReplace(len(edbRelationIds)) + `)`
+	_, err = o.Raw(sql, edbRelationIds).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增间接引用
+	relationCodesMap := make(map[string]struct{}, 0)
+	if len(relationList) > 0 {
+		for _, relation := range relationList {
+			if relation.RelationType == 1 {
+				relationCodesMap[relation.RelationCode] = struct{}{}
+			}
+		}
+		_, err = o.InsertMulti(len(relationList), relationList)
+		if err != nil {
+			return
+		}
+	}
+
+	if len(refreshEdbInfoIds) > 0 {
+		// todo 更新指标的刷新状态
+		sql := ` UPDATE edb_info SET no_update = 0 WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
+		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	//更新数据源钢联化工指标
+	if len(indexCodeList) > 0 {
+		// 更改数据源的更新状态
+		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
+		_, err = o.Raw(sql, indexCodeList).Exec()
+		if err != nil {
+			return
+		}
+	}
+	if len(relationList) > 0 {
+		// 更新间接引用指标的关联ID
+		relationCodes := make([]string, 0)
+		for relationCode := range relationCodesMap {
+			relationCodes = append(relationCodes, relationCode)
+		}
+		if len(relationCodes) > 0 {
+			sql := ` UPDATE edb_info_relation e1  
+JOIN edb_info_relation e2 ON e1.relation_code = e2.relation_code   
+SET e1.parent_relation_id = e2.edb_info_relation_id  
+WHERE  
+    e1.relation_type = 1   
+    AND e2.relation_type = 0 AND e1.parent_relation_id !=e2.edb_info_relation_id AND e1.relation_code in (` + utils.GetOrmInReplace(len(relationCodes)) + `)`
+			_, err = o.Raw(sql, relationCodes).Exec()
+			if err != nil {
+				return
+			}
+		}
+	}
+	return
+}

+ 22 - 0
models/data_manage/edb_refresh/request/edb_info_refresh.go

@@ -50,3 +50,25 @@ type SaveEdbRefreshStatusReq struct {
 	EdbSelectIdList []int  `description:"选择的指标id列表"`
 	ModifyStatus    string `description:"需要更改的状态,枚举值:启用、暂停"`
 }
+
+// SaveEdbRefreshStatusSingleReq
+// @Description: 设置单个指标的刷新状态
+type SaveEdbRefreshStatusSingleReq struct {
+	EdbInfoId    int
+	ModifyStatus string `description:"需要更改的状态,枚举值:启用、暂停"`
+}
+
+// SaveRelationEdbRefreshStatusReq
+// @Description: 设置被引用的指标的刷新状态
+type SaveRelationEdbRefreshStatusReq struct {
+	Source          int    `description:"来源"`
+	ClassifyId      string `description:"分类id,支持多选,用英文,隔开"`
+	SysUserId       string `description:"操作人id,支持多选,用英文,隔开"`
+	Frequency       string `description:"频度,支持多选,用英文,隔开"`
+	Status          string `description:"状态,枚举值:启用、暂停"`
+	EdbInfoType     int    `description:"1计算指标,2预测指标"`
+	Keyword         string `description:"关键字"`
+	IsSelectAll     bool   `description:"是否选择所有指标"`
+	EdbSelectIdList []int  `description:"选择的指标id列表"`
+	ModifyStatus    string `description:"需要更改的状态,枚举值:启用、暂停"`
+}

+ 9 - 0
models/data_manage/excel/excel_edb_mapping.go

@@ -119,6 +119,15 @@ func GetAllExcelEdbMappingByExcelInfoId(excelInfoId int) (items []*ExcelEdbMappi
 	return
 }
 
+// GetAllExcelEdbMappingByExcelInfoIds 根据excel的id获取所有的指标
+func GetAllExcelEdbMappingByExcelInfoIds(excelInfoIds []int) (items []*ExcelEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT a.* FROM  excel_edb_mapping a
+         WHERE a.excel_info_id in (` + utils.GetOrmInReplace(len(excelInfoIds)) + `) ORDER BY a.excel_edb_mapping_id ASC `
+	_, err = o.Raw(sql, excelInfoIds).QueryRows(&items)
+	return
+}
+
 // GetExcelEdbMappingByEdbInfoIdAndSource 根据指标id获取配置关系
 func GetExcelEdbMappingByEdbInfoIdAndSource(edbInfoId int, sources []int) (items []*ExcelEdbMapping, err error) {
 	o := orm.NewOrmUsingDB("data")

+ 85 - 0
models/data_manage/mysteel_chemical_index.go

@@ -189,6 +189,7 @@ type MysteelChemicalList struct {
 	FrequencyName                     string             `orm:"column(frequency)"`
 	EdbInfoId                         int                `description:"指标库的id"`
 	UpdateTime                        string             `orm:"column(modify_time)"`
+	IsStop                            int                `description:"是否停更:1:停更,0:未停更"`
 	Paging                            *paging.PagingItem `description:"分页数据"`
 	DataList                          []*MysteelChemicalData
 }
@@ -613,3 +614,87 @@ func ModifyMysteelChemicalUpdateStatus(edbIdList []int, indexCodeList []string,
 
 	return
 }
+
+// ModifyMysteelChemicalUpdateStatusByEdbInfoId
+// @Description:  修改单个钢联化工指标停更状态,同时停更依赖于该指标的计算指标
+// @author: Roc
+// @datetime 2024-01-08 16:23:31
+// @param edbIdList []int
+// @param indexCodeList []string
+// @param isStop int
+// @return err error
+func ModifyMysteelChemicalUpdateStatusByEdbInfoId(edbInfoId, isStop int, edbCode string, calculateEdbInfoIds []int) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	// 更改数据源的更新状态
+	sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = ? WHERE index_code = ? `
+	_, err = o.Raw(sql, isStop, edbCode).Exec()
+	if err != nil {
+		return
+	}
+
+	// 更改指标的更新状态
+	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND sub_source= ? AND edb_info_id=? `
+	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, edbInfoId).Exec()
+	if err != nil {
+		return
+	}
+	if len(calculateEdbInfoIds) > 0 {
+		// 批量更新相关联的指标ID
+		sql = ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+func ModifyMysteelChemicalUpdateStatusByEdbInfoIds(edbInfoIds []int, isStop int, edbCodes []string, calculateEdbInfoIds []int) (err error) {
+	o, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = o.Rollback()
+			return
+		}
+		_ = o.Commit()
+	}()
+
+	// 更改数据源的更新状态
+	sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = ? WHERE index_code IN (` + utils.GetOrmInReplace(len(edbCodes)) + `) `
+	_, err = o.Raw(sql, isStop, edbCodes).Exec()
+	if err != nil {
+		return
+	}
+
+	// 更改指标的更新状态
+	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, edbInfoIds).Exec()
+	if err != nil {
+		return
+	}
+	if len(calculateEdbInfoIds) > 0 {
+		// 批量更新相关联的指标ID
+		sql = ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 6 - 0
models/data_manage/response/edb_info.go

@@ -10,3 +10,9 @@ type EdbInfoChartListResp struct {
 	Paging *paging.PagingItem
 	List   []*data_manage.EdbInfoList
 }
+
+// WindEdbInfoListResp wind指标列表返回数据
+type WindEdbInfoListResp struct {
+	Paging *paging.PagingItem
+	List   []*data_manage.WindEdbInfoList
+}

+ 18 - 1
models/db.go

@@ -202,7 +202,7 @@ func init() {
 	initFactorEdbSeries()
 
 	// 初始化部分数据表变量(直接init会有顺序问题=_=!)
-	data_manage.InitEdbSourceVar()
+	afterInitTable()
 }
 
 // initSystem 系统表 数据表
@@ -339,6 +339,11 @@ func initEdbData() {
 		new(data_manage.EdbDataInsertConfig),      // 指标数据插入配置表
 		new(data_manage.EdbInfoNoPermissionAdmin), //指标不可见用户配置表
 		new(data_manage.EdbTerminal),              //指标终端
+		new(data_manage.BaseFromThsHfIndex),
+		new(data_manage.BaseFromThsHfData),
+		new(data_manage.BaseFromThsHfClassify),
+		new(data_manage.BaseFromEdbMapping),
+		new(data_manage.EdbInfoRelation), //指标关系表
 	)
 }
 
@@ -612,3 +617,15 @@ func initFactorEdbSeries() {
 		new(data_manage.FactorEdbSeriesCalculateData), // 因子指标系列-指标关联
 	)
 }
+
+// afterInitTable
+// @Description: 初始化表结构的的后置操作
+// @author: Roc
+// @datetime 2024-07-01 13:31:09
+func afterInitTable() {
+	// 初始化指标来源配置
+	data_manage.InitEdbSourceVar()
+
+	// 初始化是否启用mongo配置
+	InitUseMongoConf()
+}

+ 7 - 4
models/english_report.go

@@ -4,10 +4,11 @@ import (
 	"errors"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/beego/beego/v2/client/orm"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strings"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 type EnglishReport struct {
@@ -36,6 +37,7 @@ type EnglishReport struct {
 	ReportCode         string    `description:"报告唯一编码"`
 	Pv                 int       `description:"Pv"`
 	PvEmail            int       `description:"邮箱PV"`
+	UvEmail            int       `description:"邮箱UV"`
 	EmailState         int       `description:"群发邮件状态: 0-未发送; 1-已发送"`
 	Overview           string    `description:"英文概述部分"`
 	KeyTakeaways       string    `description:"关键点"`
@@ -258,6 +260,7 @@ type EnglishReportList struct {
 	Pv                 int       `description:"Pv"`
 	ShareUrl           string    `description:"分享url"`
 	PvEmail            int       `description:"邮箱PV"`
+	UvEmail            int       `description:"邮箱UV"`
 	EmailState         int       `description:"群发邮件状态: 0-未发送; 1-已发送"`
 	EmailAuth          bool      `description:"是否有权限群发邮件"`
 	EmailHasFail       bool      `description:"是否存在邮件发送失败的记录"`
@@ -962,6 +965,7 @@ func FormatEnglishReport2ListItem(origin *EnglishReport) (item *EnglishReportLis
 	item.ReportCode = origin.ReportCode
 	item.Pv = origin.Pv
 	item.PvEmail = origin.PvEmail
+	item.UvEmail = origin.UvEmail
 	// 邮箱PV大于0的时候, 不展示最初版本的PV
 	if item.PvEmail > 0 {
 		item.Pv = 0
@@ -987,11 +991,10 @@ func UpdateEnglishReportEmailHasFail(reportId int) (err error) {
 	return
 }
 
-
 // UpdatePdfUrlEnglishReportById 清空pdf相关字段
 func UpdatePdfUrlEnglishReportById(reportId int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := `UPDATE english_report SET detail_img_url = '',detail_pdf_url='',modify_time=NOW() WHERE id = ? `
 	_, err = o.Raw(sql, reportId).Exec()
 	return
-}
+}

+ 38 - 1
models/english_report_email_pv.go

@@ -1,9 +1,10 @@
 package models
 
 import (
+	"time"
+
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"time"
 )
 
 // EnglishReportEmailPV 英文研报-邮箱pv
@@ -30,6 +31,10 @@ type EnglishReportEmailPvPageListResp struct {
 	List   []*EnglishReportEmailPvResp
 	Paging *paging.PagingItem `description:"分页数据"`
 }
+type EnglishReportEmailUvPageListResp struct {
+	List   []*EnglishReportEmailUvResp
+	Paging *paging.PagingItem `description:"分页数据"`
+}
 
 // EnglishReportEmailPvResp 邮箱响应体
 type EnglishReportEmailPvResp struct {
@@ -39,6 +44,12 @@ type EnglishReportEmailPvResp struct {
 	RecentClickTime string `description:"最近一次点击时间"`
 }
 
+type EnglishReportEmailUvResp struct {
+	Name            string `description:"客户名称"`
+	Email           string `description:"邮箱地址"`
+	RecentClickTime string `description:"最近一次点击时间"`
+}
+
 // GetEnglishReportEmailPageList 获取邮箱pv列表-分页
 func GetEnglishReportEmailPvPageList(condition string, pars []interface{}, startSize, pageSize int) (total int, list []*EnglishReportEmailPvResp, err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -66,6 +77,32 @@ func GetEnglishReportEmailPvPageList(condition string, pars []interface{}, start
 	return
 }
 
+// GetEnglishReportEmailUvPageList 获取邮箱uv列表-分页
+func GetEnglishReportEmailUvPageList(condition string, pars []interface{}, startSize, pageSize int) (total int, list []*EnglishReportEmailUvResp, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT
+				b.name,
+				b.email,
+				MAX(a.create_time) AS recent_click_time
+			FROM
+				english_report_email_pv AS a
+			JOIN english_report_email AS b ON a.email_id = b.id
+			WHERE 1 = 1 `
+	if condition != `` {
+		sql += condition
+	}
+	sql += ` GROUP BY a.email_id `
+
+	totalSQl := `SELECT COUNT(1) total FROM (` + sql + `) z`
+	if err = o.Raw(totalSQl, pars).QueryRow(&total); err != nil {
+		return
+	}
+	sql += ` ORDER BY recent_click_time DESC LIMIT ?,?`
+
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
 // EnglishEmailViewPageListResp  邮箱/联系人阅读分页列表响应体
 type EnglishEmailViewPageListResp struct {
 	List   []*EnglishEmailViewResp

+ 5 - 2
models/english_video.go

@@ -2,10 +2,11 @@ package models
 
 import (
 	"eta/eta_api/utils"
-	"github.com/beego/beego/v2/client/orm"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strings"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 type EnglishVideo struct {
@@ -27,6 +28,7 @@ type EnglishVideo struct {
 	VideoCode          string    `description:"报告唯一编码"`
 	Pv                 int       `description:"Pv"`
 	PvEmail            int       `description:"邮箱PV"`
+	UvEmail            int       `description:"邮箱UV"`
 	EmailState         int       `description:"群发邮件状态: 0-未发送; 1-已发送"`
 	Overview           string    `description:"英文概述部分"`
 	AdminId            int       `description:"上传视频的管理员账号"`
@@ -164,6 +166,7 @@ type EnglishVideoList struct {
 	Pv                 int    `description:"Pv"`
 	ShareUrl           string `description:"分享url"`
 	PvEmail            int    `description:"邮箱PV"`
+	UvEmail            int    `description:"邮箱UV"`
 	EmailState         int    `description:"群发邮件状态: 0-未发送; 1-已发送"`
 	EmailAuth          bool   `description:"是否有权限群发邮件"`
 	EmailHasFail       bool   `description:"是否存在邮件发送失败的记录"`

+ 423 - 0
models/mgo/base_from_ths_hf_data.go

@@ -0,0 +1,423 @@
+package mgo
+
+import (
+	"context"
+	"errors"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/qiniu/qmgo"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"time"
+)
+
+// BaseFromThsHfData
+// @Description: 同花顺高频集合
+type BaseFromThsHfData struct {
+	ID                   primitive.ObjectID `json:"_id" bson:"_id,omitempty"`                                   // 文档id
+	BaseFromThsHfDataId  int64              `json:"base_from_ths_hf_data_id" bson:"base_from_ths_hf_data_id"`   // 指标数据ID
+	BaseFromThsHfIndexId int64              `json:"base_from_ths_hf_index_id" bson:"base_from_ths_hf_index_id"` // 指标ID
+	IndexCode            string             `json:"index_code" bson:"index_code"`                               // 指标编码
+	DataTime             time.Time          `json:"data_time" bson:"data_time"`                                 // 数据日期
+	Value                float64            `json:"value" bson:"value"`                                         // 数据值
+	UniqueCode           string             `json:"unique_code" bson:"unique_code"`                             // 唯一编码
+	CreateTime           time.Time          `json:"create_time" bson:"create_time"`                             // 创建时间
+	ModifyTime           time.Time          `json:"modify_time" bson:"modify_time"`                             // 修改时间
+	DataTimestamp        int64              `json:"data_timestamp" bson:"data_timestamp"`                       // 数据日期时间戳
+}
+
+// CollectionName
+// @Description:  获取集合名称
+func (m *BaseFromThsHfData) CollectionName() string {
+	return "base_from_ths_hf_data"
+}
+
+// DataBaseName
+// @Description: 获取数据库名称
+func (m *BaseFromThsHfData) DataBaseName() string {
+	return utils.MgoDataDbName
+}
+
+// GetCollection
+// @Description: 获取mongodb集合的句柄
+func (m *BaseFromThsHfData) GetCollection() *qmgo.Collection {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	return db.Collection(m.CollectionName())
+}
+
+// GetAllDataList
+// @Description: 根据条件获取所有数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:42:19
+// @param sort []string
+// @param whereParams interface{}
+// @return result []BaseFromThsHfData
+// @return err error
+func (m *BaseFromThsHfData) GetAllDataList(whereParams interface{}, sort []string) (result []*BaseFromThsHfData, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Find(ctx, whereParams).Sort(sort...).All(&result)
+	if err != nil {
+		return
+	}
+
+	for _, v := range result {
+		v.DataTime = v.DataTime.In(time.Local)
+		v.CreateTime = v.CreateTime.In(time.Local)
+		v.ModifyTime = v.ModifyTime.In(time.Local)
+	}
+	return
+}
+
+// GetLimitDataList
+// @Description: 根据条件获取指定数量数据列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-06 17:08:32
+// @param whereParams interface{}
+// @param size int64
+// @return result []*BaseFromThsHfData
+// @return err error
+func (m *BaseFromThsHfData) GetLimitDataList(whereParams interface{}, size int64, sort []string) (result []*BaseFromThsHfData, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Find(ctx, whereParams).Sort(sort...).Limit(size).All(&result)
+	if err != nil {
+		return
+	}
+
+	for _, v := range result {
+		v.DataTime = v.DataTime.In(time.Local)
+		v.CreateTime = v.CreateTime.In(time.Local)
+		v.ModifyTime = v.ModifyTime.In(time.Local)
+	}
+
+	return
+}
+
+// GetPageDataList
+// @Description: 根据条件获取分页数据列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-07 10:21:07
+// @param whereParams interface{}
+// @param startSize int64
+// @param size int64
+// @param sort []string
+// @return result []*BaseFromThsHfData
+// @return err error
+func (m *BaseFromThsHfData) GetPageDataList(whereParams interface{}, startSize, size int64, sort []string) (result []*BaseFromThsHfData, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Find(ctx, whereParams).Sort(sort...).Skip(startSize).Limit(size).All(&result)
+	if err != nil {
+		return
+	}
+
+	for _, v := range result {
+		v.DataTime = v.DataTime.In(time.Local)
+		v.CreateTime = v.CreateTime.In(time.Local)
+		v.ModifyTime = v.ModifyTime.In(time.Local)
+	}
+
+	return
+}
+
+// GetCountDataList
+// @Description:  根据条件获取数据列表总数
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-07 10:29:00
+// @param whereParams interface{}
+// @return count int64
+// @return err error
+func (m *BaseFromThsHfData) GetCountDataList(whereParams interface{}) (count int64, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	count, err = coll.Find(ctx, whereParams).Count()
+
+	return
+}
+
+// InsertDataByColl
+// @Description: 写入单条数据(外部传入集合)
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param addData interface{}
+// @return err error
+func (m *BaseFromThsHfData) InsertDataByColl(coll *qmgo.Collection, addData interface{}) (err error) {
+	ctx := context.TODO()
+	_, err = coll.InsertOne(ctx, addData)
+	if err != nil {
+		fmt.Println("InsertDataByColl:Err:" + err.Error())
+		return
+	}
+
+	return
+}
+
+// BatchInsertData
+// @Description: 批量写入数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param bulk int 每次请求保存的数据量
+// @param dataList []interface{}
+// @return err error
+func (m *BaseFromThsHfData) BatchInsertData(bulk int, dataList []interface{}) (err error) {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+
+	return m.BatchInsertDataByColl(coll, bulk, dataList)
+}
+
+// BatchInsertDataByColl
+// @Description: 批量写入数据(外部传入集合)
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param coll *qmgo.Collection
+// @param bulk int 每次请求保存的数据量
+// @param dataList []interface{}
+// @return err error
+func (m *BaseFromThsHfData) BatchInsertDataByColl(coll *qmgo.Collection, bulk int, dataList []interface{}) (err error) {
+	ctx := context.TODO()
+	dataNum := len(dataList)
+	if dataNum <= 0 {
+		return
+	}
+
+	// 不设置每次保存切片数量大小,或者实际数据量小于设置的切片数量大小,那么就直接保存吧
+	if bulk <= 0 || dataNum <= bulk {
+		_, err = coll.InsertMany(ctx, dataList)
+		if err != nil {
+			fmt.Println("BatchInsertData:Err:" + err.Error())
+			return
+		}
+		return
+	}
+
+	// 分批保存
+	i := 0
+	tmpAddDataList := make([]interface{}, 0)
+	for _, v := range dataList {
+		tmpAddDataList = append(tmpAddDataList, v)
+		i++
+		if i >= bulk {
+			_, err = coll.InsertMany(ctx, tmpAddDataList)
+			if err != nil {
+				fmt.Println("BatchInsertData:Err:" + err.Error())
+				return
+			}
+			i = 0
+			tmpAddDataList = make([]interface{}, 0)
+		}
+	}
+
+	if len(tmpAddDataList) > 0 {
+		_, err = coll.InsertMany(ctx, tmpAddDataList)
+		if err != nil {
+			fmt.Println("BatchInsertData:Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+// UpdateDataByColl
+// @Description: 单条数据修改
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 15:01:51
+// @param whereParams interface{}
+// @param updateParams interface{}
+// @return err error
+func (m *BaseFromThsHfData) UpdateDataByColl(coll *qmgo.Collection, whereParams, updateParams interface{}) (err error) {
+	ctx := context.TODO()
+	err = coll.UpdateOne(ctx, whereParams, updateParams)
+	if err != nil {
+		fmt.Println("UpdateDataByColl:Err:" + err.Error())
+		return
+	}
+	return
+}
+
+// UpdateData
+// @Description: 单条数据修改
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 15:01:51
+// @param whereParams interface{}
+// @param updateParams interface{}
+// @return err error
+func (m *BaseFromThsHfData) UpdateData(whereParams, updateParams interface{}) (err error) {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	err = coll.UpdateOne(ctx, whereParams, updateParams)
+	if err != nil {
+		fmt.Println("UpdateData:Err:" + err.Error())
+		return
+	}
+	return
+}
+
+// HandleData
+// @Description: 事务处理数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 10:40:20
+// @param addDataList []BaseAddFromBusinessData
+// @param updateDataList []BaseFromThsHfData
+// @return result interface{}
+// @return err error
+func (m *BaseFromThsHfData) HandleData(addDataList, updateDataList []BaseFromThsHfData) (result interface{}, err error) {
+
+	ctx := context.TODO()
+
+	callback := func(sessCtx context.Context) (interface{}, error) {
+		// 重要:确保事务中的每一个操作,都使用传入的sessCtx参数
+
+		db := utils.MgoDataCli.Database(m.DataBaseName())
+		coll := db.Collection(m.CollectionName())
+
+		// 插入数据
+		if len(addDataList) > 0 {
+			_, err = coll.InsertMany(sessCtx, addDataList)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+		// 修改
+
+		if len(updateDataList) > 0 {
+			for _, v := range updateDataList {
+				err = coll.UpdateOne(ctx, bson.M{"_id": v.ID}, bson.M{"$set": bson.M{"value": v.Value, "modify_time": v.ModifyTime}})
+				if err != nil {
+					fmt.Println("BatchInsertData:Err:" + err.Error())
+					return nil, err
+				}
+			}
+		}
+
+		return nil, nil
+	}
+	result, err = utils.MgoDataCli.DoTransaction(ctx, callback)
+
+	return
+}
+
+// GetEdbInfoMaxAndMinInfo
+// @Description: 获取当前指标的最大最小值
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 17:15:39
+// @param whereParams interface{}
+// @return result EdbInfoMaxAndMinInfo
+// @return err error
+func (m *BaseFromThsHfData) GetEdbInfoMaxAndMinInfo(whereParams interface{}) (result EdbInfoMaxAndMinInfo, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Aggregate(ctx, whereParams).One(&result)
+	if err != nil {
+		return
+	}
+	result.MinDate = result.MinDate.In(time.Local)
+	result.MaxDate = result.MaxDate.In(time.Local)
+	result.LatestDate = result.LatestDate.In(time.Local)
+
+	return
+}
+
+// GetLatestValue
+// @Description: 获取当前指标的最新数据记录
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 17:16:15
+// @param whereParams interface{}
+// @param selectParam interface{}
+// @return latestValue LatestValue
+// @return err error
+func (m *BaseFromThsHfData) GetLatestValue(whereParams, selectParam interface{}) (latestValue LatestValue, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+
+	//var result interface{}
+	//err = coll.Find(ctx, whereParams).Select(selectParam).One(&result)
+	err = coll.Find(ctx, whereParams).Select(selectParam).One(&latestValue)
+	return
+}
+
+func (m *BaseFromThsHfData) RemoveMany(whereParams interface{}) (err error) {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	return m.RemoveManyByColl(coll, whereParams)
+}
+
+func (m *BaseFromThsHfData) RemoveManyByColl(coll *qmgo.Collection, whereParams interface{}) (err error) {
+	ctx := context.TODO()
+	_, err = coll.RemoveAll(ctx, whereParams)
+	if err != nil {
+		fmt.Println("RemoveManyByColl:Err:" + err.Error())
+		return
+	}
+	return
+}

+ 510 - 0
models/mgo/edb_data_ths_hf.go

@@ -0,0 +1,510 @@
+package mgo
+
+import (
+	"context"
+	"errors"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/qiniu/qmgo"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"time"
+)
+
+// EdbDataThsHf
+// @Description: 同花顺高频集合(指标库)
+type EdbDataThsHf struct {
+	ID            primitive.ObjectID `json:"_id" bson:"_id,omitempty" `            // 文档id
+	EdbInfoId     int                `json:"edb_info_id" bson:"edb_info_id"`       // 指标ID
+	EdbCode       string             `json:"edb_code" bson:"edb_code"`             // 指标编码
+	DataTime      time.Time          `json:"data_time" bson:"data_time"`           // 数据日期
+	Value         float64            `json:"value" bson:"value"`                   // 数据值
+	CreateTime    time.Time          `json:"create_time" bson:"create_time"`       // 创建时间
+	ModifyTime    time.Time          `json:"modify_time" bson:"modify_time"`       // 修改时间
+	DataTimestamp int64              `json:"data_timestamp" bson:"data_timestamp"` // 数据日期时间戳
+}
+
+// CollectionName
+// @Description:  获取集合名称
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:41:36
+// @return string
+func (m *EdbDataThsHf) CollectionName() string {
+	return "edb_data_ths_hf"
+}
+
+// DataBaseName
+// @Description: 获取数据库名称
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:41:33
+// @return string
+func (m *EdbDataThsHf) DataBaseName() string {
+	return utils.MgoDataDbName
+}
+
+// GetCollection
+// @Description: 获取mongodb集合的句柄
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:41:33
+// @return string
+func (m *EdbDataThsHf) GetCollection() *qmgo.Collection {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	return db.Collection(m.CollectionName())
+}
+
+// GetItem
+// @Description: 根据条件获取单条数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-09 10:00:49
+// @param whereParams interface{}
+// @return item *EdbDataThsHf
+// @return err error
+func (m *EdbDataThsHf) GetItem(whereParams interface{}) (item *EdbDataThsHf, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+
+	return m.GetItemByColl(coll, whereParams)
+}
+
+// GetItemByColl
+// @Description: 根据条件获取单条数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-09 13:22:06
+// @param coll *qmgo.Collection
+// @param whereParams interface{}
+// @return item *EdbDataThsHf
+// @return err error
+func (m *EdbDataThsHf) GetItemByColl(coll *qmgo.Collection, whereParams interface{}) (item *EdbDataThsHf, err error) {
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Find(ctx, whereParams).One(&item)
+	if err != nil {
+		return
+	}
+
+	item.DataTime = item.DataTime.In(time.Local)
+	item.CreateTime = item.CreateTime.In(time.Local)
+	item.ModifyTime = item.ModifyTime.In(time.Local)
+
+	return
+}
+
+// GetAllDataList
+// @Description: 根据条件获取所有数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 13:42:19
+// @param whereParams interface{}
+// @param sort []string
+// @return result []EdbDataThsHf
+// @return err error
+func (m *EdbDataThsHf) GetAllDataList(whereParams interface{}, sort []string) (result []*EdbDataThsHf, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Find(ctx, whereParams).Sort(sort...).All(&result)
+	if err != nil {
+		return
+	}
+
+	for _, v := range result {
+		v.DataTime = v.DataTime.In(time.Local)
+		v.CreateTime = v.CreateTime.In(time.Local)
+		v.ModifyTime = v.ModifyTime.In(time.Local)
+	}
+
+	return
+}
+
+// GetLimitDataList
+// @Description: 根据条件获取指定数量数据列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-06 17:08:32
+// @param whereParams interface{}
+// @param size int64
+// @param sort []string
+// @return result []*BaseFromBusinessData
+// @return err error
+func (m *EdbDataThsHf) GetLimitDataList(whereParams interface{}, size int64, sort []string) (result []*EdbDataThsHf, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Find(ctx, whereParams).Sort(sort...).Limit(size).All(&result)
+	if err != nil {
+		return
+	}
+
+	for _, v := range result {
+		v.DataTime = v.DataTime.In(time.Local)
+		v.CreateTime = v.CreateTime.In(time.Local)
+		v.ModifyTime = v.ModifyTime.In(time.Local)
+	}
+
+	return
+}
+
+// GetPageDataList
+// @Description: 根据条件获取分页数据列表
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-07 10:21:07
+// @param whereParams interface{}
+// @param startSize int64
+// @param size int64
+// @param sort []string
+// @return result []*EdbDataThsHf
+// @return err error
+func (m *EdbDataThsHf) GetPageDataList(whereParams interface{}, startSize, size int64, sort []string) (result []*EdbDataThsHf, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Find(ctx, whereParams).Sort(sort...).Skip(startSize).Limit(size).All(&result)
+	if err != nil {
+		return
+	}
+
+	for _, v := range result {
+		v.DataTime = v.DataTime.In(time.Local)
+		v.CreateTime = v.CreateTime.In(time.Local)
+		v.ModifyTime = v.ModifyTime.In(time.Local)
+	}
+
+	return
+}
+
+// GetCountDataList
+// @Description:  根据条件获取数据列表总数
+// @author: Roc
+// @receiver m
+// @datetime 2024-05-07 10:29:00
+// @param whereParams interface{}
+// @return count int64
+// @return err error
+func (m *EdbDataThsHf) GetCountDataList(whereParams interface{}) (count int64, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	count, err = coll.Find(ctx, whereParams).Count()
+
+	return
+}
+
+// InsertDataByColl
+// @Description: 写入单条数据(外部传入集合)
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param addData interface{}
+// @return err error
+func (m *EdbDataThsHf) InsertDataByColl(coll *qmgo.Collection, addData interface{}) (err error) {
+	ctx := context.TODO()
+	_, err = coll.InsertOne(ctx, addData)
+	if err != nil {
+		fmt.Println("InsertDataByColl:Err:" + err.Error())
+		return
+	}
+
+	return
+}
+
+// BatchInsertData
+// @Description: 批量写入数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param bulk int 每次请求保存的数据量
+// @param dataList []interface{}
+// @return err error
+func (m *EdbDataThsHf) BatchInsertData(bulk int, dataList []interface{}) (err error) {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+
+	return m.BatchInsertDataByColl(coll, bulk, dataList)
+}
+
+// BatchInsertDataByColl
+// @Description: 批量写入数据(外部传入集合)
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 14:22:18
+// @param coll *qmgo.Collection
+// @param bulk int 每次请求保存的数据量
+// @param dataList []interface{}
+// @return err error
+func (m *EdbDataThsHf) BatchInsertDataByColl(coll *qmgo.Collection, bulk int, dataList []interface{}) (err error) {
+	ctx := context.TODO()
+	dataNum := len(dataList)
+	if dataNum <= 0 {
+		return
+	}
+
+	// 不设置每次保存切片数量大小,或者实际数据量小于设置的切片数量大小,那么就直接保存吧
+	if bulk <= 0 || dataNum <= bulk {
+		_, err = coll.InsertMany(ctx, dataList)
+		if err != nil {
+			fmt.Println("BatchInsertData:Err:" + err.Error())
+			return
+		}
+		return
+	}
+
+	// 分批保存
+	i := 0
+	tmpAddDataList := make([]interface{}, 0)
+	for _, v := range dataList {
+		tmpAddDataList = append(tmpAddDataList, v)
+		i++
+		if i >= bulk {
+			_, err = coll.InsertMany(ctx, tmpAddDataList)
+			if err != nil {
+				fmt.Println("BatchInsertData:Err:" + err.Error())
+				return
+			}
+			i = 0
+			tmpAddDataList = make([]interface{}, 0)
+		}
+	}
+
+	if len(tmpAddDataList) > 0 {
+		_, err = coll.InsertMany(ctx, tmpAddDataList)
+		if err != nil {
+			fmt.Println("BatchInsertData:Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+// UpdateData
+// @Description: 单条数据修改
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 15:01:51
+// @param whereParams interface{}
+// @param updateParams interface{}
+// @return err error
+func (m *EdbDataThsHf) UpdateData(whereParams, updateParams interface{}) (err error) {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+
+	return m.UpdateDataByColl(coll, whereParams, updateParams)
+}
+
+// UpdateDataByColl
+// @Description: 单条数据修改(外部传入集合)
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-26 15:01:51
+// @param whereParams interface{}
+// @param updateParams interface{}
+// @return err error
+func (m *EdbDataThsHf) UpdateDataByColl(coll *qmgo.Collection, whereParams, updateParams interface{}) (err error) {
+	ctx := context.TODO()
+	err = coll.UpdateOne(ctx, whereParams, updateParams)
+	if err != nil {
+		fmt.Println("UpdateDataByColl:Err:" + err.Error())
+		return
+	}
+
+	return
+}
+
+// RemoveMany
+// @Description: 根据条件删除多条数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 13:17:02
+// @param whereParams interface{}
+// @return err error
+func (m *EdbDataThsHf) RemoveMany(whereParams interface{}) (err error) {
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+
+	return m.RemoveManyByColl(coll, whereParams)
+}
+
+// RemoveManyByColl
+// @Description: 根据条件删除多条数据(外部传入集合)
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 13:18:42
+// @param coll *qmgo.Collection
+// @param whereParams interface{}
+// @return err error
+func (m *EdbDataThsHf) RemoveManyByColl(coll *qmgo.Collection, whereParams interface{}) (err error) {
+	ctx := context.TODO()
+	_, err = coll.RemoveAll(ctx, whereParams)
+	if err != nil {
+		fmt.Println("RemoveManyByColl:Err:" + err.Error())
+		return
+	}
+
+	return
+}
+
+// HandleData
+// @Description: 事务处理数据
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 10:39:01
+// @param addDataList []AddEdbDataThsHf
+// @param updateDataList []EdbDataThsHf
+// @return result interface{}
+// @return err error
+func (m *EdbDataThsHf) HandleData(addDataList, updateDataList []EdbDataThsHf) (result interface{}, err error) {
+
+	ctx := context.TODO()
+
+	callback := func(sessCtx context.Context) (interface{}, error) {
+		// 重要:确保事务中的每一个操作,都使用传入的sessCtx参数
+
+		db := utils.MgoDataCli.Database(m.DataBaseName())
+		coll := db.Collection(m.CollectionName())
+
+		// 插入数据
+		if len(addDataList) > 0 {
+			_, err = coll.InsertMany(sessCtx, addDataList)
+			if err != nil {
+				return nil, err
+			}
+		}
+
+		// 修改
+
+		if len(updateDataList) > 0 {
+			for _, v := range updateDataList {
+				err = coll.UpdateOne(ctx, bson.M{"_id": v.ID}, bson.M{"$set": bson.M{"value": v.Value, "modify_time": v.ModifyTime}})
+				if err != nil {
+					fmt.Println("BatchInsertData:Err:" + err.Error())
+					return nil, err
+				}
+			}
+		}
+
+		return nil, nil
+	}
+	result, err = utils.MgoDataCli.DoTransaction(ctx, callback)
+
+	return
+}
+
+// EdbInfoMaxAndMinInfo 指标最新数据记录结构体
+//type EdbInfoMaxAndMinInfo struct {
+//	MinDate     time.Time `description:"最小日期" bson:"min_date"`
+//	MaxDate     time.Time `description:"最大日期" bson:"max_date"`
+//	MinValue    float64   `description:"最小值" bson:"min_value"`
+//	MaxValue    float64   `description:"最大值" bson:"max_value"`
+//	LatestValue float64   `description:"最新值" bson:"latest_value"`
+//	LatestDate  time.Time `description:"实际数据最新日期" bson:"latest_date"`
+//	EndValue    float64   `description:"最新值" bson:"end_value"`
+//}
+
+// GetEdbInfoMaxAndMinInfo
+// @Description: 获取当前指标的最大最小值
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 17:15:39
+// @param whereParams interface{}
+// @return result EdbInfoMaxAndMinInfo
+// @return err error
+func (m *EdbDataThsHf) GetEdbInfoMaxAndMinInfo(whereParams interface{}) (result EdbInfoMaxAndMinInfo, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+	err = coll.Aggregate(ctx, whereParams).One(&result)
+	if err != nil {
+		return
+	}
+	result.MinDate = result.MinDate.In(time.Local)
+	result.MaxDate = result.MaxDate.In(time.Local)
+	result.LatestDate = result.LatestDate.In(time.Local)
+
+	return
+}
+
+// LatestValue 指标最新数据记录结构体
+//type LatestValue struct {
+//	Value float64 `description:"值" bson:"value"`
+//}
+
+// GetLatestValue
+// @Description: 获取当前指标的最新数据记录
+// @author: Roc
+// @receiver m
+// @datetime 2024-04-30 17:16:15
+// @param whereParams interface{}
+// @param selectParam interface{}
+// @return latestValue LatestValue
+// @return err error
+func (m *EdbDataThsHf) GetLatestValue(whereParams, selectParam interface{}) (latestValue LatestValue, err error) {
+	if utils.MgoDataCli == nil {
+		err = errors.New("mongodb连接失败")
+		return
+	}
+	db := utils.MgoDataCli.Database(m.DataBaseName())
+	coll := db.Collection(m.CollectionName())
+	ctx := context.TODO()
+	if err != nil {
+		fmt.Println("MgoGetColl Err:", err.Error())
+		return
+	}
+
+	//var result interface{}
+	//err = coll.Find(ctx, whereParams).Select(selectParam).One(&result)
+	err = coll.Find(ctx, whereParams).Select(selectParam).One(&latestValue)
+	return
+}

+ 36 - 0
models/sandbox/sandbox.go

@@ -374,6 +374,14 @@ func GetSandboxByClassifyIdAndName(classifyId int, name string) (item *Sandbox,
 	return
 }
 
+// GetSandboxNameByIds 根据沙盘名称
+func GetSandboxNameByIds(ids []int) (items []*Sandbox, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT sandbox_id, name FROM sandbox WHERE sandbox_id in (` + utils.GetOrmInReplace(len(ids)) + `) `
+	_, err = o.Raw(sql, ids).QueryRows(&items)
+	return
+}
+
 func MoveSandbox(sandboxId, classifyId int) (err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` UPDATE  sandbox
@@ -403,6 +411,34 @@ func GetFirstSandboxByClassifyId(classifyId int) (item *Sandbox, err error) {
 	return
 }
 
+// ContentDataStruct 沙盘内容结构体
+type ContentDataStruct struct {
+	Cells []struct {
+		Data *NodeData `json:"data,omitempty"`
+	} `json:"cells"`
+}
+
+type NodeData struct {
+	LinkData []*LinkData `json:"linkData"`
+	LinkFold bool        `json:"linkFold"`
+}
+
+type LinkData struct {
+	RId          string       `json:"RId"`
+	Id           int          `json:"Id"`
+	Name         string       `json:"Name"`
+	Type         int          `json:"Type"`
+	Editing      bool         `json:"editing"`
+	DatabaseType int          `json:"databaseType"`
+	DetailParams DetailParams `json:"detailParams"`
+}
+
+type DetailParams struct {
+	Code       string `json:"code"`
+	Id         int    `json:"id"`
+	ClassifyId int    `json:"classifyId"`
+}
+
 // UpdateSandboxContent 更新沙盘内容
 func UpdateSandboxContent(list []Sandbox) (err error) {
 	o := orm.NewOrmUsingDB("data")

+ 243 - 0
routers/commentsRouter.go

@@ -2077,6 +2077,168 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ClassifyAdd",
+            Router: `/ths_hf/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ClassifyEdit",
+            Router: `/ths_hf/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ClassifyList",
+            Router: `/ths_hf/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ClassifyMove",
+            Router: `/ths_hf/classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ClassifyRemove",
+            Router: `/ths_hf/classify/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ClassifyTree",
+            Router: `/ths_hf/classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/ths_hf/index/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/ths_hf/index/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/ths_hf/index/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ExistCheck",
+            Router: `/ths_hf/index/exist_check`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/ths_hf/index/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "ListChoice",
+            Router: `/ths_hf/index/list_choice`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "MultiOpt",
+            Router: `/ths_hf/index/multi_opt`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/ths_hf/index/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/ths_hf/index/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Save2Edb",
+            Router: `/ths_hf/index/save2edb`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Save2EdbPre",
+            Router: `/ths_hf/index/save2edb_pre`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromThsHfController"],
+        beego.ControllerComments{
+            Method: "Search",
+            Router: `/ths_hf/index/search`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BloombergDataController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BloombergDataController"],
         beego.ControllerComments{
             Method: "AddCheck",
@@ -3634,6 +3796,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "SaveRelationEdbRefreshStatus",
+            Router: `/edb_info/relation/refresh/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
         beego.ControllerComments{
             Method: "EdbInfoReplaceCheck",
@@ -3652,6 +3823,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "SaveEdbRefreshStatusSingle",
+            Router: `/edb_info/single_refresh/status/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
         beego.ControllerComments{
             Method: "SmmEdbInfoBatchAdd",
@@ -4372,6 +4552,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "WindClassify",
+            Router: `/wind/classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "WindEdbInfoList",
+            Router: `/wind/index`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
         beego.ControllerComments{
             Method: "YongyiClassify",
@@ -4417,6 +4615,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoRelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoRelationController"],
+        beego.ControllerComments{
+            Method: "RelationEdbListDetail",
+            Router: `/edb_info/relation/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoRelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoRelationController"],
+        beego.ControllerComments{
+            Method: "RelationEdbList",
+            Router: `/edb_info/relation/edb_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:FactorEdbSeriesController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:FactorEdbSeriesController"],
         beego.ControllerComments{
             Method: "Add",
@@ -5794,6 +6010,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportEmailController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportEmailController"],
+        beego.ControllerComments{
+            Method: "UvList",
+            Router: `/email/uv_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportEmailController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportEmailController"],
         beego.ControllerComments{
             Method: "VideoResend",
@@ -7036,6 +7261,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"],
+        beego.ControllerComments{
+            Method: "GetSingle",
+            Router: `/single`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"],
+        beego.ControllerComments{
+            Method: "SingleSave",
+            Router: `/single/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfOpenController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfOpenController"],
         beego.ControllerComments{
             Method: "CodeEncrypt",

+ 2 - 0
routers/router.go

@@ -170,6 +170,8 @@ func init() {
 				&data_manage_permission.DataMangePermissionController{},
 				&data_manage.BloombergDataController{},
 				&data_manage.EdbBusinessController{},
+				&data_manage.BaseFromThsHfController{},
+				&data_manage.EdbInfoRelationController{},
 				&data_manage.FactorEdbSeriesController{},
 			),
 		),

+ 12 - 9
services/data/base_edb_lib.go

@@ -104,16 +104,13 @@ func AddEdbDataWindWsd(source int, stockCode, edbCode string) (resp *models.Base
 }
 
 // AddEdbDataThsDs 新增指标数据
-func AddEdbDataThsDs(source int, stockCode, edbCode string) (resp *models.BaseResponse, err error) {
+func AddEdbDataThsDs(source int, stockCode, edbCode, extraPars string) (resp *models.BaseResponse, err error) {
 	param := make(map[string]interface{})
 	param["EdbCode"] = edbCode
 	param["StockCode"] = stockCode
 	param["Source"] = source
+	param["ExtraPars"] = extraPars
 	urlStr := `ths/ds/add`
-	if urlStr == "" {
-		err = fmt.Errorf("未实现该指标的刷新接口,请联系管理员")
-		return
-	}
 	resp, err = postRefreshEdbData(param, urlStr)
 	return
 }
@@ -205,11 +202,13 @@ func RefreshEdbData(edbInfoId, source, subSource int, edbCode, startDate string)
 	urlStr := ``
 	switch source {
 	case utils.DATA_SOURCE_THS:
-		urlStr = "ths/refresh"
-		if subSource == 0 {
-			urlStr = "ths/refresh"
-		} else {
+		switch subSource {
+		case utils.DATA_SUB_SOURCE_DATE:
 			urlStr = "ths/ds/refresh"
+		case utils.DATA_SUB_SOURCE_HIGH_FREQUENCY:
+			urlStr = "ths/hf/edb/refresh"
+		default:
+			urlStr = "ths/refresh"
 		}
 	case utils.DATA_SOURCE_WIND:
 		if subSource == 0 {
@@ -482,6 +481,10 @@ func AddBaseEdbInfo(addBaseEdbInfoReqStr string, source, subSource int, lang str
 	switch source {
 	case utils.DATA_SOURCE_BUSINESS:
 		urlStr = "business_index/add"
+	case utils.DATA_SOURCE_THS:
+		if subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY {
+			urlStr = "ths/hf/edb/add"
+		}
 	default:
 		edbSource := data_manage.EdbSourceIdMap[source]
 		if edbSource != nil {

+ 66 - 0
services/data/base_from_business.go

@@ -71,6 +71,26 @@ func BusinessIndexSource2Edb(req BusinessIndexSource2EdbReq, lang string) (edb *
 // @return dataList []*data_manage.BaseFromBusinessIndexDataItem
 // @return err error
 func GetPageBaseBusinessIndexData(indexCode string, startSize, pageSize int) (dataCount int, dataList []*data_manage.BaseFromBusinessIndexDataItem, err error) {
+	if utils.UseMongo {
+		dataCount, dataList, err = getPageBaseBusinessIndexDataByMongo(indexCode, startSize, pageSize)
+	} else {
+		dataCount, dataList, err = getPageBaseBusinessIndexDataByMysql(indexCode, startSize, pageSize)
+	}
+
+	return
+}
+
+// getPageBaseBusinessIndexDataByMongo
+// @Description: 获取自有数据的分页数据(从mongo中获取)
+// @author: Roc
+// @datetime 2024-07-01 14:01:04
+// @param indexCode string
+// @param startSize int
+// @param pageSize int
+// @return dataCount int
+// @return dataList []*data_manage.BaseFromBusinessIndexDataItem
+// @return err error
+func getPageBaseBusinessIndexDataByMongo(indexCode string, startSize, pageSize int) (dataCount int, dataList []*data_manage.BaseFromBusinessIndexDataItem, err error) {
 	dataList = make([]*data_manage.BaseFromBusinessIndexDataItem, 0)
 
 	mogDataObj := mgo.BaseFromBusinessData{}
@@ -104,3 +124,49 @@ func GetPageBaseBusinessIndexData(indexCode string, startSize, pageSize int) (da
 
 	return
 }
+
+// getPageBaseBusinessIndexDataByMysql
+// @Description: 获取自有数据的分页数据(从mysql中获取)
+// @author: Roc
+// @datetime 2024-07-01 14:00:41
+// @param indexCode string
+// @param startSize int
+// @param pageSize int
+// @return dataCount int
+// @return dataList []*data_manage.BaseFromBusinessIndexDataItem
+// @return err error
+func getPageBaseBusinessIndexDataByMysql(indexCode string, startSize, pageSize int) (dataCount int, dataList []*data_manage.BaseFromBusinessIndexDataItem, err error) {
+	dataList = make([]*data_manage.BaseFromBusinessIndexDataItem, 0)
+
+	businessDataObj := data_manage.BaseFromBusinessData{}
+	var condition []string
+	var pars []interface{}
+
+	condition = append(condition, "index_code = ?")
+	pars = append(pars, indexCode)
+
+	// 获取数据总量
+	tmpCount, tmpErr := businessDataObj.GetCountDataList(condition, pars)
+	if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+		err = tmpErr
+		return
+	}
+	dataCount = int(tmpCount)
+
+	// 获取列表数据
+	tmpDataList, tmpErr := businessDataObj.GetPageDataList(condition, pars, " data_time desc", int64(startSize), int64(pageSize))
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	for _, v := range tmpDataList {
+		dataList = append(dataList, &data_manage.BaseFromBusinessIndexDataItem{
+			ID:        fmt.Sprint(v.BusinessDataId),
+			IndexCode: v.IndexCode,
+			DataTime:  v.DataTime.Format(utils.FormatDate),
+			Value:     v.Value,
+		})
+	}
+
+	return
+}

+ 302 - 0
services/data/base_from_ths_hf.go

@@ -0,0 +1,302 @@
+package data
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/mgo"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/utils"
+	"fmt"
+	"go.mongodb.org/mongo-driver/bson"
+	"time"
+)
+
+// CheckExistThsHfEdb 校验已存在的同花顺高频指标
+func CheckExistThsHfEdb(stockCodes, edbCodes []string) (checkResp data_manage.ThsHfExistCheckResp, existMap map[string]bool, err error) {
+	// 待校验的指标编码
+	var indexCode []string
+	prefix := utils.ThsHf
+	for _, sv := range stockCodes {
+		for _, ev := range edbCodes {
+			t := fmt.Sprintf("%s%s%s", prefix, sv, ev)
+			indexCode = append(indexCode, t)
+		}
+	}
+
+	baseIndexes := make([]*data_manage.BaseFromThsHfIndex, 0)
+	{
+		ob := new(data_manage.BaseFromThsHfIndex)
+		fields := []string{ob.Cols().PrimaryId, ob.Cols().IndexCode, ob.Cols().IndexName, ob.Cols().StockCode, ob.Cols().Indicator}
+		list, e := ob.GetItemsByCondition(``, make([]interface{}, 0), fields, "")
+		if e != nil {
+			err = fmt.Errorf("获取高频指标列表失败, %v", e)
+			return
+		}
+		baseIndexes = list
+	}
+	var existNum int
+	existMap = make(map[string]bool)
+	for _, bv := range baseIndexes {
+		code := fmt.Sprintf("%s%s%s", prefix, bv.StockCode, bv.Indicator)
+		for _, iv := range indexCode {
+			if code != iv {
+				continue
+			}
+			existMap[fmt.Sprintf("%s-%s", bv.StockCode, bv.Indicator)] = true
+			existNum += 1
+			checkResp.ExistIndex = append(checkResp.ExistIndex, data_manage.ThsHfExistCheckIndex{
+				IndexId:   bv.BaseFromThsHfIndexId,
+				IndexCode: bv.IndexCode,
+				IndexName: bv.IndexName,
+			})
+			break
+		}
+	}
+	if existNum > 0 && existNum == len(indexCode) {
+		checkResp.ExistAll = true
+	}
+	return
+}
+
+// GetEdbDataThsHf 获取同花顺高频数据指标
+func GetEdbDataThsHf(req data_manage.ThsHfSearchEdbReq) (indexes []*data_manage.ThsHfIndexWithData, err error) {
+	param := make(map[string]interface{})
+	param["StockCode"] = req.StockCode
+	param["EdbCode"] = req.EdbCode
+	param["StartTime"] = req.StartTime
+	param["EndTime"] = req.EndTime
+	param["Interval"] = req.Interval
+	param["Fill"] = req.Fill
+	param["CPS"] = req.CPS
+	param["BaseDate"] = req.BaseDate
+	uri := `ths/hf/edb_data`
+	resp, e := postThsHfEdbData(param, uri)
+	if e != nil {
+		err = fmt.Errorf("postThsHfEdbData, %v", e)
+		return
+	}
+	if resp.Ret == 200 {
+		indexes = resp.Data
+	}
+	return
+}
+
+// postThsHfEdbData 刷新指标数据
+func postThsHfEdbData(param map[string]interface{}, urlStr string) (resp *data_manage.ThsHfIndexDataLibResp, err error) {
+	postUrl := utils.EDB_LIB_URL + urlStr
+	postData, err := json.Marshal(param)
+	if err != nil {
+		return
+	}
+	result, err := HttpPost(postUrl, string(postData), utils.ZhLangVersion, "application/json")
+	if err != nil {
+		return
+	}
+	utils.FileLog.Info("postRefreshEdbData:" + postUrl + ";" + string(postData) + ";result:" + string(result))
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+	return resp, nil
+}
+
+// BaseAddThsHf 新增数据源
+func BaseAddThsHf(req data_manage.ThsHfBaseAddReq) (resp *models.BaseResponse, err error) {
+	param := make(map[string]interface{})
+	param["StartTime"] = req.StartTime
+	param["EndTime"] = req.EndTime
+	param["Interval"] = req.Interval
+	param["Fill"] = req.Fill
+	param["CPS"] = req.CPS
+	param["BaseDate"] = req.BaseDate
+	param["SysAdminId"] = req.SysAdminId
+	param["SysAdminName"] = req.SysAdminName
+	param["ClassifyId"] = req.ClassifyId
+	param["Unit"] = req.Unit
+	param["IndexName"] = req.IndexName
+	param["Frequency"] = req.Frequency
+	param["StockCode"] = req.StockCode
+	param["EdbCode"] = req.EdbCode
+	uri := `ths/hf/base/add`
+	res, e := postRefreshEdbData(param, uri)
+	if e != nil {
+		err = fmt.Errorf("postRefreshEdbData, %v", e)
+		return
+	}
+	resp = res
+	return
+}
+
+// RefreshBaseThsHfIndex 刷新源指标
+func RefreshBaseThsHfIndex(indexIds []int, refreshType int) (isAsync bool, err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("RefreshBaseThsHfIndex-刷新同花顺高频指标失败, %v", err)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+
+	indexes := make([]*data_manage.BaseFromThsHfIndex, 0)
+	{
+		ob := new(data_manage.BaseFromThsHfIndex)
+		cond := fmt.Sprintf(" AND %s IN (%s)", ob.Cols().PrimaryId, utils.GetOrmInReplace(len(indexIds)))
+		pars := make([]interface{}, 0)
+		pars = append(pars, indexIds)
+		list, e := ob.GetItemsByCondition(cond, pars, []string{}, "")
+		if e != nil {
+			err = fmt.Errorf("获取源指标失败, %v", e)
+			return
+		}
+		if len(list) == 0 {
+			return
+		}
+		indexes = list
+	}
+
+	refreshUrl := "ths/hf/base/refresh"
+	// 异步刷新
+	if len(indexes) > 10 {
+		isAsync = true
+
+		go func() {
+			for _, v := range indexes {
+				param := make(map[string]interface{})
+				param["BaseIndexCode"] = v.IndexCode
+				param["RefreshType"] = refreshType
+				resp, e := postRefreshEdbData(param, refreshUrl)
+				if e != nil {
+					utils.FileLog.Info(fmt.Sprintf("thsHf-postRefreshEdbData, code: %s, err: %v", v.IndexCode, e))
+					continue
+				}
+				if resp != nil && resp.Ret != 200 {
+					utils.FileLog.Info(fmt.Sprintf("thsHf-postRefreshEdbData, code: %s, Ret: %d, ErrMsg: %s", v.IndexCode, resp.Ret, resp.ErrMsg))
+					continue
+				}
+			}
+		}()
+		return
+	}
+
+	// 同步刷新
+	for _, v := range indexes {
+		param := make(map[string]interface{})
+		param["BaseIndexCode"] = v.IndexCode
+		param["RefreshType"] = refreshType
+		resp, e := postRefreshEdbData(param, refreshUrl)
+		if e != nil {
+			utils.FileLog.Info(fmt.Sprintf("thsHf-postRefreshEdbData, code: %s, err: %v", v.IndexCode, e))
+			continue
+		}
+		if resp.Ret != 200 {
+			utils.FileLog.Info(fmt.Sprintf("thsHf-postRefreshEdbData, code: %s, Ret: %d, ErrMsg: %s", v.IndexCode, resp.Ret, resp.ErrMsg))
+			continue
+		}
+	}
+	return
+}
+
+// GetThsHfBaseIndexData 获取指标数据
+func GetThsHfBaseIndexData(indexCode, startTime, endTime string) (dataList []*data_manage.BaseFromThsHfDataItem, err error) {
+	if utils.UseMongo {
+		list, e := getThsHfBaseIndexDataByMongo(indexCode, startTime, endTime)
+		if e != nil {
+			err = fmt.Errorf("获取指标数据失败-Mongo, %v", e)
+			return
+		}
+		dataList = list
+		return
+	}
+
+	// MySQL
+	var (
+		cond string
+		pars []interface{}
+	)
+	dataOb := new(data_manage.BaseFromThsHfData)
+	if startTime != "" && endTime != "" {
+		cond += fmt.Sprintf(" AND %s = ? AND (%s BETWEEN ? AND ?)", dataOb.Cols().IndexCode, dataOb.Cols().DataTime)
+		pars = append(pars, indexCode, startTime, endTime)
+	}
+	if startTime != "" && endTime == "" {
+		cond += fmt.Sprintf(" AND %s = ? AND %s > ?)", dataOb.Cols().IndexCode, dataOb.Cols().DataTime)
+		pars = append(pars, indexCode, startTime)
+	}
+	list, e := dataOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
+	if e != nil {
+		err = fmt.Errorf("获取指标数据失败-MySQL, %v", e)
+		return
+	}
+	for _, v := range list {
+		dataList = append(dataList, v.Format2Item())
+	}
+	return
+}
+
+// getThsHfBaseIndexDataByMongo
+func getThsHfBaseIndexDataByMongo(indexCode, startTime, endTime string) (dataList []*data_manage.BaseFromThsHfDataItem, err error) {
+	dataList = make([]*data_manage.BaseFromThsHfDataItem, 0)
+
+	mogDataObj := mgo.BaseFromThsHfData{}
+	// 构建查询条件
+	queryConditions := bson.M{
+		"index_code": indexCode,
+	}
+
+	// 时间区间
+	if startTime != "" && endTime != "" {
+		st, e := time.ParseInLocation(utils.FormatDateTime, startTime, time.Local)
+		if e != nil {
+			err = fmt.Errorf("start time parse err: %v", e)
+			return
+		}
+		ed, e := time.ParseInLocation(utils.FormatDateTime, endTime, time.Local)
+		if e != nil {
+			err = fmt.Errorf("end time parse err: %v", e)
+			return
+		}
+		queryConditions["data_time"] = bson.M{
+			"$gte": st,
+			"$lte": ed,
+		}
+	}
+	if startTime != "" && endTime == "" {
+		st, e := time.ParseInLocation(utils.FormatDateTime, startTime, time.Local)
+		if e != nil {
+			err = fmt.Errorf("start time parse err: %v", e)
+			return
+		}
+		queryConditions["data_time"] = bson.M{
+			"$gte": st,
+		}
+	}
+
+	// 获取列表数据
+	list, e := mogDataObj.GetAllDataList(queryConditions, []string{"-data_time"})
+	if e != nil {
+		err = fmt.Errorf("GetAllDataList err: %v", e)
+		return
+	}
+	for _, v := range list {
+		dataList = append(dataList, formatMgoBaseThsHfData2Item(v))
+	}
+	return
+}
+
+func formatMgoBaseThsHfData2Item(origin *mgo.BaseFromThsHfData) (item *data_manage.BaseFromThsHfDataItem) {
+	if origin == nil {
+		return
+	}
+	item = new(data_manage.BaseFromThsHfDataItem)
+	item.DataId = int(origin.BaseFromThsHfDataId)
+	item.IndexId = int(origin.BaseFromThsHfIndexId)
+	item.IndexCode = origin.IndexCode
+	item.DataTime = utils.TimeTransferString(utils.FormatDateTime, origin.DataTime)
+	item.Value = origin.Value
+	item.UniqueCode = origin.UniqueCode
+	return
+}

+ 526 - 0
services/data/base_from_ths_hf_classify.go

@@ -0,0 +1,526 @@
+package data
+
+import (
+	"errors"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// GetThsHfClassifyTreeRecursive 递归获取分类树形结构
+func GetThsHfClassifyTreeRecursive(list []*data_manage.BaseFromThsHfClassifyItem, parentId int) []*data_manage.BaseFromThsHfClassifyItem {
+	res := make([]*data_manage.BaseFromThsHfClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			t := GetThsHfClassifyTreeRecursive(list, v.ClassifyId)
+			v.Children = nil // 这一步是方便前端组件判断null...
+			if len(t) > 0 {
+				v.Children = t
+			}
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+// ThsHfMoveClassify 移动指标分类
+func ThsHfMoveClassify(req data_manage.BaseFromThsHfClassifyMoveReq, 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.BaseFromThsHfClassify
+	if parentClassifyId > 0 {
+		parentEdbClassifyInfo, err = data_manage.GetThsHfClassifyById(parentClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上级分类信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		edbClassifyInfo *data_manage.BaseFromThsHfClassify
+		prevClassify    *data_manage.BaseFromThsHfClassify
+		nextClassify    *data_manage.BaseFromThsHfClassify
+
+		edbInfo     *data_manage.BaseFromThsHfIndex
+		prevEdbInfo *data_manage.BaseFromThsHfIndex
+		nextEdbInfo *data_manage.BaseFromThsHfIndex
+		prevSort    int
+		nextSort    int
+	)
+
+	// 移动对象为分类, 判断权限
+	if itemId == 0 {
+		edbClassifyInfo, err = data_manage.GetThsHfClassifyById(classifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				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 && e.Error() != utils.ErrNoRow() {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取父级分类下的同名分类失败, Err: %s", e.Error())
+			return
+		}
+		if exists != nil {
+			errMsg = "移动失败,分类名称已存在"
+			return
+		}
+
+	} else {
+		edbInfo, err = data_manage.GetThsHfIndexById(req.ItemId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "当前指标不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			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.GetThsHfClassifyById(prevClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevClassify.Sort
+	} else if prevItemId > 0 {
+		prevEdbInfo, err = data_manage.GetThsHfIndexById(prevItemId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevEdbInfo.Sort
+	}
+
+	if nextClassifyId > 0 {
+		//下一个兄弟节点
+		nextClassify, err = data_manage.GetThsHfClassifyById(nextClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextClassify.Sort
+	} else if nextItemId > 0 {
+		//下一个兄弟节点
+		nextEdbInfo, err = data_manage.GetThsHfIndexById(nextItemId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextEdbInfo.Sort
+	}
+
+	err, errMsg = thsHfMoveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify, edbInfo, prevEdbInfo, nextEdbInfo, parentClassifyId, prevSort, nextSort)
+	return
+}
+
+// thsHfMoveEdbClassify 移动指标分类
+func thsHfMoveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify *data_manage.BaseFromThsHfClassify, edbInfo, prevEdbInfo, nextEdbInfo *data_manage.BaseFromThsHfIndex, parentClassifyId int, prevSort, nextSort int) (err error, errMsg string) {
+	updateCol := make([]string, 0)
+
+	// 移动对象为分类, 判断分类是否存在
+	if edbClassifyInfo != nil {
+		oldParentId := edbClassifyInfo.ParentId
+		oldLevel := edbClassifyInfo.Level
+		var classifyIds []int
+		if oldParentId != parentClassifyId {
+			//更新子分类对应的level
+			childList, e, m := GetThsHfChildClassifyByClassifyId(edbClassifyInfo.BaseFromThsHfClassifyId)
+			if e != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询子分类失败,Err:" + e.Error() + m)
+				return
+			}
+
+			if len(childList) > 0 {
+				for _, v := range childList {
+					if v.BaseFromThsHfClassifyId == edbClassifyInfo.BaseFromThsHfClassifyId {
+						continue
+					}
+					classifyIds = append(classifyIds, v.BaseFromThsHfClassifyId)
+				}
+			}
+		}
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId != 0 {
+			if edbClassifyInfo.Level != parentEdbClassifyInfo.Level+1 { //禁止层级调整
+				errMsg = "移动失败"
+				err = errors.New("不支持目录层级变更")
+				return
+			}
+			edbClassifyInfo.ParentId = parentEdbClassifyInfo.BaseFromThsHfClassifyId
+			edbClassifyInfo.RootId = parentEdbClassifyInfo.RootId
+			edbClassifyInfo.Level = parentEdbClassifyInfo.Level + 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			// 更改层级路径
+			edbClassifyInfo.LevelPath = fmt.Sprintf("%s,%d", parentEdbClassifyInfo.LevelPath, edbClassifyInfo.BaseFromThsHfClassifyId)
+			updateCol = append(updateCol, "ParentId", "RootId", "Level", "ModifyTime", "LevelPath")
+		} 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.UpdateThsHfClassifySortByParentId(parentClassifyId, prevClassify.BaseFromThsHfClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromThsHfIndexId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, prevClassify.BaseFromThsHfClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromThsHfIndexId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateThsHfIndexSortByClassifyId(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 = GetThsHfClassifyMaxSort(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.GetFirstThsHfClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, firstClassify.BaseFromThsHfClassifyId-1, 0, updateSortStr)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstThsHfIndexByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, 0, firstEdb.BaseFromThsHfIndexId-1, updateSortStr)
+					_ = data_manage.UpdateThsHfClassifySortByParentId(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.UpdateThsHfClassifyChildByParentClassifyId(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.BaseFromThsHfClassifyId != parentClassifyId {
+			edbInfo.BaseFromThsHfClassifyId = parentClassifyId
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "BaseFromThsHfClassifyId", "ModifyTime")
+		}
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, prevClassify.BaseFromThsHfClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromThsHfIndexId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, prevClassify.BaseFromThsHfClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromThsHfIndexId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+					}
+				}
+			}
+
+			edbInfo.Sort = prevSort + 1
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevEdbInfo == nil && nextEdbInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			classifyOb := new(data_manage.BaseFromThsHfClassify)
+			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, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstThsHfClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, firstClassify.BaseFromThsHfClassifyId-1, 0, updateSortStr)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstThsHfIndexByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateThsHfIndexSortByClassifyId(parentClassifyId, 0, firstEdb.BaseFromThsHfIndexId-1, updateSortStr)
+					_ = data_manage.UpdateThsHfClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
+				}
+			}
+
+			edbInfo.Sort = 0 //那就是排在第一位
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = edbInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+func GetThsHfChildClassifyByClassifyId(targetClassifyId int) (targetList []*data_manage.BaseFromThsHfClassify, err error, errMsg string) {
+	//判断是否是挂在顶级目录下
+	targetClassify, err := data_manage.GetThsHfClassifyById(targetClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "当前分类不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		errMsg = "获取失败"
+		err = errors.New("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	orderStr := ` order by level asc, sort asc, base_from_ths_hf_classify_id asc`
+	tmpList, err := data_manage.GetThsHfClassifyByRootIdLevel(targetClassify.RootId, orderStr)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		err = errors.New("获取数据失败,Err:" + err.Error())
+		return
+	}
+	idMap := make(map[int]struct{})
+	if len(tmpList) > 0 {
+		for _, v := range tmpList {
+			if v.BaseFromThsHfClassifyId == targetClassify.BaseFromThsHfClassifyId {
+				idMap[v.BaseFromThsHfClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ParentId]; ok {
+				idMap[v.BaseFromThsHfClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.BaseFromThsHfClassifyId]; ok {
+				targetItem := new(data_manage.BaseFromThsHfClassify)
+				targetItem.BaseFromThsHfClassifyId = v.BaseFromThsHfClassifyId
+				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 GetThsHfClassifyMaxSort(parentId int) (maxSort int, err error) {
+	//获取该层级下最大的排序数
+	classifyOb := new(data_manage.BaseFromThsHfClassify)
+	classifyMaxSort, err := classifyOb.GetSortMax(parentId)
+	if err != nil {
+		return
+	}
+	maxSort = classifyMaxSort
+	edbMaxSort, err := data_manage.GetThsHfIndexMaxSortByClassifyId(parentId)
+	if err != nil {
+		return
+	}
+	if maxSort < edbMaxSort {
+		maxSort = edbMaxSort
+	}
+	return
+}

+ 4 - 1
services/data/chart_info.go

@@ -2264,7 +2264,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 		err = errors.New("保存失败,Err:" + err.Error())
 		return
 	}
-
+	// 添加指标引用记录
+	_ = SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
 	//添加es数据
 	go EsAddOrEditChartInfo(chartInfo.ChartInfoId)
 
@@ -2616,6 +2617,8 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang
 	resp.UniqueCode = chartItem.UniqueCode
 	resp.ChartType = req.ChartType
 
+	// todo 添加指标引用记录
+	_ = SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
 	//添加es数据
 	go EsAddOrEditChartInfo(chartItem.ChartInfoId)
 	//修改my eta es数据

+ 4 - 0
services/data/correlation/chart_info.go

@@ -983,6 +983,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, source int, sysUser *system.A
 		return
 	}
 
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfoId)
 
@@ -1224,6 +1226,8 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang
 	resp.UniqueCode = chartItem.UniqueCode
 	resp.ChartType = req.ChartType
 
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
 	//修改my eta es数据

+ 9 - 3
services/data/cross_variety/chart.go

@@ -650,6 +650,7 @@ func AddChartInfo(req request.AddChartReq, sysUser *system.Admin, lang string) (
 		}
 	}
 
+	edbInfoIdArr := make([]int, 0)
 	// 数据校验(品种、标签、指标)
 	{
 		// 标签m
@@ -691,6 +692,7 @@ func AddChartInfo(req request.AddChartReq, sysUser *system.Admin, lang string) (
 			isSendEmail = false
 			return
 		}
+		edbInfoIdArr = edbInfoIdList
 		mappingList, tmpErr := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
 		if tmpErr != nil {
 			errMsg = "获取指标信息失败"
@@ -793,7 +795,9 @@ func AddChartInfo(req request.AddChartReq, sysUser *system.Admin, lang string) (
 		err = errors.New("新增相关性图表失败, Err: " + e.Error())
 		return
 	}
-
+	chartInfo.ChartInfoId = chartInfoId
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfoId)
 
@@ -905,7 +909,7 @@ func EditChartInfo(req request.EditChartReq, sysUser *system.Admin, lang string)
 		isSendEmail = false
 		return
 	}
-	
+	edbInfoIdArr := make([]int, 0)
 	// 数据校验(品种、标签、指标)
 	{
 		// 标签m
@@ -947,6 +951,7 @@ func EditChartInfo(req request.EditChartReq, sysUser *system.Admin, lang string)
 			isSendEmail = false
 			return
 		}
+		edbInfoIdArr = edbInfoIdList
 		mappingList, tmpErr := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdList)
 		if tmpErr != nil {
 			errMsg = "获取指标信息失败"
@@ -1032,7 +1037,8 @@ func EditChartInfo(req request.EditChartReq, sysUser *system.Admin, lang string)
 	resp.ChartInfoId = chartItem.ChartInfoId
 	resp.UniqueCode = chartItem.UniqueCode
 	//resp.ChartType = req.ChartType
-
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
 	//修改my eta es数据

+ 59 - 0
services/data/edb_classify.go

@@ -127,6 +127,52 @@ func GetFullClassifyByClassifyId(targetClassifyId int) (targetList []*data_manag
 	return
 }
 
+// GetFullClassifyByRootId 查询指标列表里的分类信息
+func GetFullClassifyByRootId(targetClassify *data_manage.EdbClassify, tmpList []*data_manage.EdbClassifyItems) (targetList []*data_manage.EdbClassifyIdItems, err error, errMsg string) {
+	if targetClassify.ParentId == 0 {
+		targetItem := new(data_manage.EdbClassifyIdItems)
+		targetItem.ClassifyId = targetClassify.ClassifyId
+		targetItem.ParentId = targetClassify.ParentId
+		targetItem.RootId = targetClassify.RootId
+		targetItem.UniqueCode = targetClassify.UniqueCode
+		targetItem.Level = targetClassify.Level
+		targetItem.ClassifyName = targetClassify.ClassifyName
+		targetItem.ClassifyName = targetClassify.ClassifyName
+		targetItem.IsJoinPermission = targetClassify.IsJoinPermission
+		targetList = append(targetList, targetItem)
+		return
+	}
+
+	idMap := make(map[int]struct{})
+	if len(tmpList) > 0 {
+		for _, v := range tmpList {
+			if v.ClassifyId == targetClassify.ClassifyId {
+				idMap[v.ClassifyId] = struct{}{}
+				idMap[v.ParentId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ClassifyId]; ok {
+				idMap[v.ParentId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ClassifyId]; ok {
+				targetItem := new(data_manage.EdbClassifyIdItems)
+				targetItem.ClassifyId = v.ClassifyId
+				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 GetChildClassifyByClassifyId(targetClassifyId int) (targetList []*data_manage.EdbClassifyIdItems, err error, errMsg string) {
 	//判断是否是挂在顶级目录下
 	targetClassify, err := data_manage.GetEdbClassifyById(targetClassifyId)
@@ -858,6 +904,19 @@ func Delete(classifyId, edbInfoId int, sysUser *system.Admin, requestBody, reque
 			return
 		}
 
+		// 如果是同花顺高频数据或类似数据, 还需要删除base_from_edb_mapping对应关系
+		baseMappingOb := new(data_manage.BaseFromEdbMapping)
+		{
+			cond := fmt.Sprintf(" %s = ? AND %s = ? AND %s = ?", baseMappingOb.Cols().EdbCode, baseMappingOb.Cols().Source, baseMappingOb.Cols().SubSource)
+			pars := make([]interface{}, 0)
+			pars = append(pars, edbInfo.EdbCode, edbInfo.Source, edbInfo.SubSource)
+			if e := baseMappingOb.RemoveByCondition(cond, pars); e != nil {
+				errMsg = "删除失败"
+				err = fmt.Errorf("删除源指标映射失败, %v", e)
+				return
+			}
+		}
+
 		go data_stat.AddEdbDeleteLog(edbInfo, sysUser)
 
 		// 如果删除的指标是自定义分析的来源,那么还需要删除指标与excel的关系

+ 48 - 1
services/data/edb_data.go

@@ -409,9 +409,12 @@ func GetPageData(edbInfoId, source, subSource int, endDataTime string, startSize
 	dataList = make([]*data_manage.EdbData, 0)
 
 	// 自有数据需要额外处理(从mongo获取)
-	if source == utils.DATA_SOURCE_BUSINESS {
+	if source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 		return getPageDataByMongo(edbInfoId, source, subSource, endDataTime, startSize, pageSize)
 	}
+	if source == utils.DATA_SOURCE_THS && subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+		return getThsHfPageDataByMongo(edbInfoId, source, subSource, endDataTime, startSize, pageSize)
+	}
 
 	// 默认走mysql
 	return getPageDataByMysql(edbInfoId, source, subSource, endDataTime, startSize, pageSize)
@@ -515,3 +518,47 @@ func getPageDataByMongo(edbInfoId, source, subSource int, endDataTime string, st
 
 	return
 }
+
+func getThsHfPageDataByMongo(edbInfoId, source, subSource int, endDataTime string, startSize, pageSize int) (dataCount int, dataList []*data_manage.EdbData, err error) {
+	dataList = make([]*data_manage.EdbData, 0)
+
+	mogDataObj := mgo.EdbDataThsHf{}
+	// 构建查询条件
+	queryConditions := bson.M{
+		"edb_info_id": edbInfoId,
+	}
+
+	// 结束日期
+	dateCondition, err := mgo.BuildDateCondition("", endDataTime)
+	if err != nil {
+		return
+	}
+	if len(dateCondition) > 0 {
+		queryConditions["data_time"] = dateCondition
+	}
+
+	// 获取数据总量
+	tmpCount, tmpErr := mogDataObj.GetCountDataList(queryConditions)
+	if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+		err = tmpErr
+		return
+	}
+	dataCount = int(tmpCount)
+
+	// 获取列表数据
+	tmpDataList, tmpErr := mogDataObj.GetPageDataList(queryConditions, int64(startSize), int64(pageSize), []string{"-data_time"})
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	for k, v := range tmpDataList {
+		dataList = append(dataList, &data_manage.EdbData{
+			EdbDataId: k + 1,
+			EdbInfoId: v.EdbInfoId,
+			DataTime:  v.DataTime.Format(utils.FormatDate),
+			Value:     v.Value,
+		})
+	}
+
+	return
+}

+ 17 - 3
services/data/edb_info.go

@@ -3,6 +3,7 @@ package data
 import (
 	"encoding/json"
 	"errors"
+	"eta/eta_api/cache"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/services/alarm_msg"
@@ -103,8 +104,10 @@ func EdbInfoRefreshAllFromBaseV3(edbInfoIdList []int, refreshAll, isSync, isRefr
 	}
 
 	// 需要刷新的指标数量
-	totalEdbInfo := len(newBaseEdbInfoArr) + len(calculateArr) + len(predictCalculateArr)
-
+	totalEdbInfo := len(newBaseEdbInfoArr) + len(calculateArr) + len(predictCalculateArr) + len(newBasePredictEdbInfoArr)
+	if totalEdbInfo == 0 {
+		return
+	}
 	if totalEdbInfo <= 20 || isSync {
 		err = edbInfoRefreshAll(refreshAll, newBaseEdbInfoArr, newBasePredictEdbInfoArr, newCalculateMap, newPredictCalculateMap, calculateArr, predictCalculateArr)
 	} else {
@@ -173,6 +176,9 @@ func getEdbInfoIdList(edbInfoIdList []int) (newBaseEdbInfoArr, newBasePredictEdb
 		// 普通计算指标
 		for _, edbInfo := range tmpCalculateMap {
 			if _, ok := newCalculateMap[edbInfo.EdbInfoId]; !ok {
+				if edbInfo.NoUpdate == 1 {
+					continue
+				}
 				newCalculateMap[edbInfo.EdbInfoId] = edbInfo
 				calculateArr = append(calculateArr, edbInfo.EdbInfoId)
 			}
@@ -1903,6 +1909,12 @@ func EdbInfoAdd(source, subSource, classifyId int, edbCode, edbName, frequency,
 	//添加es
 	AddOrEditEdbInfoToEs(int(edbInfoId))
 
+	// 更新钢联化工状态为启用
+	if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+		// 启动钢联的刷新
+		_ = data_manage.UpdateMysteelChemicalRefreshStatus(edbCode, 0)
+	}
+
 	return
 }
 
@@ -2434,7 +2446,8 @@ func EdbInfoReplace(oldEdbInfo, newEdbInfo *data_manage.EdbInfo, sysAdminId int,
 	if err != nil {
 		return
 	}
-
+	//加入到缓存队列中处理
+	go cache.AddReplaceEdbInfo(oldEdbInfo, newEdbInfo)
 	// 更新所有的关联指标
 	err, _ = EdbInfoRefreshAllFromBaseV3(relationEdbInfoIdList, true, true, true)
 	return
@@ -2574,6 +2587,7 @@ func EdbInfoWsdAdd(item *data_manage.EdbInfo) (edbInfo *data_manage.EdbInfo, err
 	edbInfo.IndicatorCode = item.IndicatorCode
 	edbInfo.StockCode = item.StockCode
 	edbInfo.TerminalCode = item.TerminalCode
+	edbInfo.Extra = item.Extra
 	edbInfoId, err := data_manage.AddEdbInfo(edbInfo)
 	if err != nil {
 		errMsg = "保存失败"

+ 680 - 0
services/data/edb_info_relation.go

@@ -0,0 +1,680 @@
+package data
+
+import (
+	"eta/eta_api/models/data_manage"
+	excelModel "eta/eta_api/models/data_manage/excel"
+	"eta/eta_api/models/fe_calendar"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/services/sandbox"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// SaveChartEdbInfoRelation 添加/编辑图表指标引用关联记录
+func SaveChartEdbInfoRelation(edbInfoIds []int, chartInfo *data_manage.ChartInfo) (err error) {
+	//更新指标刷新状态为启用
+	err = saveEdbInfoRelation(edbInfoIds, chartInfo.ChartInfoId, utils.EDB_RELATION_CHART, chartInfo.Source)
+	return
+}
+
+// saveEdbInfoRelation 添加/编辑图表指标引用关联记录
+func saveEdbInfoRelation(edbInfoIds []int, objectId, objectType, objectSubType int) (err error) {
+	// 实现添加引用记录的逻辑
+	if len(edbInfoIds) == 0 {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tips := "实现添加引用记录的逻辑-添加/编辑图表指标引用关联记录失败, ErrMsg:\n" + err.Error()
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+	refreshIds := make([]int, 0)
+	indexCodeList := make([]string, 0)
+	// 查询指标信息
+	edbInfoListTmp, e := data_manage.GetEdbInfoByIdList(edbInfoIds)
+	if e != nil {
+		err = fmt.Errorf("查询指标信息失败,%s", e.Error())
+		return
+	}
+	// 过滤预测指标
+	edbInfoList := make([]*data_manage.EdbInfo, 0)
+	for _, v := range edbInfoListTmp {
+		if v.EdbInfoType == 0 {
+			edbInfoList = append(edbInfoList, v)
+		}
+	}
+	// 查询计算指标信息,并且建立关联关系
+	// 查询间接引用的指标信息
+	calculateEdbMappingListMap, calculateEdbMappingIdsMap, e := GetEdbListByEdbInfoId(edbInfoList)
+	if e != nil {
+		err = fmt.Errorf("查询计算指标信息失败,%s", e.Error())
+		return
+	}
+	// 只统计钢联化工和wind来源的指标
+	for _, edbInfo := range edbInfoList {
+		/*if edbInfo.Source != utils.DATA_SOURCE_WIND && edbInfo.Source != utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			continue
+		}*/
+		refreshIds = append(refreshIds, edbInfo.EdbInfoId)
+		if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			indexCodeList = append(indexCodeList, edbInfo.EdbCode)
+		}
+	}
+	// 循转组装引用
+	// 查询已有的引用关系
+	existList, e := data_manage.GetEdbInfoRelationByReferObjectId(objectId, objectType)
+	if e != nil {
+		err = fmt.Errorf("查询已有的引用关系失败,%s", e.Error())
+		return
+	}
+	deleteMap := make(map[int]bool)
+	relationMap := make(map[int]bool)
+	for _, exist := range existList {
+		deleteMap[exist.EdbInfoId] = true
+		relationMap[exist.EdbInfoId] = true
+	}
+	// 新增不存在的引用关系
+	// 删除不再需要的引用关系
+	nowTime := time.Now()
+	addList := make([]*data_manage.EdbInfoRelation, 0)
+	deleteEdbInfoIds := make([]int, 0)
+	for _, edbInfo := range edbInfoList {
+		/*if edbInfo.Source != utils.DATA_SOURCE_WIND && edbInfo.Source != utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			continue
+		}*/
+		if _, ok := relationMap[edbInfo.EdbInfoId]; ok {
+			delete(deleteMap, edbInfo.EdbInfoId)
+		} else {
+			tmp := &data_manage.EdbInfoRelation{
+				ReferObjectId:      objectId,
+				ReferObjectType:    objectType,
+				ReferObjectSubType: objectSubType,
+				EdbInfoId:          edbInfo.EdbInfoId,
+				EdbName:            edbInfo.EdbName,
+				Source:             edbInfo.Source,
+				EdbCode:            edbInfo.EdbCode,
+				CreateTime:         nowTime,
+				ModifyTime:         nowTime,
+				RelationTime:       nowTime,
+			}
+			tmp.RelationCode = fmt.Sprintf("%d_%d_%d_%d", tmp.EdbInfoId, tmp.ReferObjectId, tmp.ReferObjectType, tmp.ReferObjectSubType)
+			addList = append(addList, tmp)
+			if edbInfo.EdbType == 2 && edbInfo.EdbInfoType == 0 {
+				childEdbMappingIds, ok1 := calculateEdbMappingIdsMap[edbInfo.EdbInfoId]
+				if !ok1 {
+					continue
+				}
+				for _, childEdbMappingId := range childEdbMappingIds {
+					childEdbMapping, ok2 := calculateEdbMappingListMap[childEdbMappingId]
+					if !ok2 {
+						continue
+					}
+
+					if childEdbMapping.FromSource == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+						indexCodeList = append(indexCodeList, childEdbMapping.FromEdbCode)
+					}
+					tmp1 := &data_manage.EdbInfoRelation{
+						ReferObjectId:      objectId,
+						ReferObjectType:    objectType,
+						ReferObjectSubType: objectSubType,
+						EdbInfoId:          childEdbMapping.FromEdbInfoId,
+						EdbName:            childEdbMapping.FromEdbName,
+						Source:             childEdbMapping.FromSource,
+						EdbCode:            childEdbMapping.FromEdbCode,
+						CreateTime:         nowTime,
+						ModifyTime:         nowTime,
+						RelationTime:       nowTime,
+						RelationType:       1,
+						RootEdbInfoId:      edbInfo.EdbInfoId,
+						ChildEdbInfoId:     childEdbMapping.EdbInfoId,
+						RelationCode:       tmp.RelationCode,
+					}
+					addList = append(addList, tmp1)
+					refreshIds = append(refreshIds, childEdbMapping.FromEdbInfoId)
+					// todo 防止重复
+				}
+			}
+
+		}
+	}
+
+	// 删除不再需要的引用关系
+	for deleteId, _ := range deleteMap {
+		deleteEdbInfoIds = append(deleteEdbInfoIds, deleteId)
+	}
+	//更新指标刷新状态为启用
+	err = data_manage.AddOrUpdateEdbInfoRelation(objectId, objectType, addList, deleteEdbInfoIds, refreshIds, indexCodeList)
+	if err != nil {
+		err = fmt.Errorf("删除不再需要的引用关系失败,%s", err.Error())
+		return
+	}
+	return
+}
+
+// SaveSandBoxEdbInfoRelation 添加/编辑 eta逻辑图指标引用
+func SaveSandBoxEdbInfoRelation(sandBoxId int, sandBoxContent string) (err error) {
+	edbInfoIds, err := sandbox.GetSandBoxEdbIdsByContent(sandBoxContent)
+	if err != nil {
+		return
+	}
+	if len(edbInfoIds) == 0 {
+		return
+	}
+	//更新指标刷新状态为启用
+	err = saveEdbInfoRelation(edbInfoIds, sandBoxId, utils.EDB_RELATION_SANDBOX, 0)
+	return
+}
+
+// SaveCalendarEdbInfoRelation 添加/编辑 事件日历指标引用
+func SaveCalendarEdbInfoRelation(chartPermissionId int, matterDate string, editMatters, removeMatters []*fe_calendar.FeCalendarMatter) (err error) {
+	defer func() {
+		if err != nil {
+			err = fmt.Errorf("添加/编辑 事件日历指标引用失败,%s", err.Error())
+			go alarm_msg.SendAlarmMsg(err.Error(), 3)
+		}
+	}()
+	//整理相关的事件ID
+	matterIds := make([]int, 0)
+	updateMatterMap := make(map[int]*fe_calendar.FeCalendarMatter)
+	deleteMatterMap := make(map[int]struct{})
+	for _, matter := range removeMatters {
+		deleteMatterMap[matter.FeCalendarMatterId] = struct{}{}
+		matterIds = append(matterIds, matter.FeCalendarMatterId)
+	}
+	for _, matter := range editMatters {
+		updateMatterMap[matter.FeCalendarMatterId] = matter
+		matterIds = append(matterIds, matter.FeCalendarMatterId)
+	}
+
+	//删除ID,先删除
+	deleteObjectIds := make([]int, 0)
+	if len(matterIds) > 0 {
+		relationList, e := data_manage.GetEdbInfoRelationAllByReferObjectIds(matterIds, utils.EDB_RELATION_CALENDAR)
+		if e != nil {
+			err = fmt.Errorf("查询事件日历指标引用失败,%s", e.Error())
+			return
+		}
+		for _, relation := range relationList {
+			if _, ok := deleteMatterMap[relation.ReferObjectId]; ok {
+				deleteObjectIds = append(deleteObjectIds, relation.ReferObjectId)
+			}
+			if newMatter, ok := updateMatterMap[relation.ReferObjectId]; ok {
+				if relation.EdbInfoId != newMatter.EdbInfoId {
+					deleteObjectIds = append(deleteObjectIds, relation.ReferObjectId)
+				}
+			}
+		}
+	}
+	if len(deleteObjectIds) > 0 {
+		err = data_manage.DeleteEdbRelationByObjectIds(deleteObjectIds, utils.EDB_RELATION_CALENDAR)
+		if err != nil {
+			err = fmt.Errorf("删除事件日历指标引用失败,%s", err.Error())
+			return
+		}
+	}
+
+	// 获取已有事项
+	matterOb := new(fe_calendar.FeCalendarMatter)
+	cond := fmt.Sprintf(` AND %s = ? AND %s = ?`, fe_calendar.FeCalendarMatterCols.ChartPermissionId, fe_calendar.FeCalendarMatterCols.MatterDate)
+	pars := make([]interface{}, 0)
+	pars = append(pars, chartPermissionId, matterDate)
+	order := fmt.Sprintf(`%s ASC`, fe_calendar.FeCalendarMatterCols.Sort)
+	matters, e := matterOb.GetItemsByCondition(cond, pars, []string{}, order)
+	if e != nil {
+		err = fmt.Errorf("查询事件日历事项失败,%s", e.Error())
+		return
+	}
+	// 循环查询matters
+	edbInfoIds := make([]int, 0)
+	refreshIds := make([]int, 0)
+	indexCodeList := make([]string, 0)
+
+	newMatterIds := make([]int, 0)
+	for _, matter := range matters {
+		newMatterIds = append(newMatterIds, matter.FeCalendarMatterId)
+		edbInfoIds = append(edbInfoIds, matter.EdbInfoId)
+	}
+
+	// 查询指标信息
+	edbInfoListTmp, e := data_manage.GetEdbInfoByIdList(edbInfoIds)
+	if e != nil {
+		err = fmt.Errorf("查询指标信息失败,%s", e.Error())
+		return
+	}
+	// 过滤预测指标
+	edbInfoList := make([]*data_manage.EdbInfo, 0)
+	for _, v := range edbInfoListTmp {
+		if v.EdbInfoType == 0 {
+			edbInfoList = append(edbInfoList, v)
+		}
+	}
+	// 查询计算指标信息,并且建立关联关系
+	// 查询间接引用的指标信息
+	calculateEdbMappingListMap, calculateEdbMappingIdsMap, e := GetEdbListByEdbInfoId(edbInfoList)
+	if e != nil {
+		err = fmt.Errorf("查询计算指标信息失败,%s", e.Error())
+		return
+	}
+	addEdbInfoIdMap := make(map[int]*data_manage.EdbInfo)
+	for _, edbInfo := range edbInfoList {
+		/*if edbInfo.Source != utils.DATA_SOURCE_WIND && edbInfo.Source != utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			continue
+		}*/
+		refreshIds = append(refreshIds, edbInfo.EdbInfoId)
+		addEdbInfoIdMap[edbInfo.EdbInfoId] = edbInfo
+		if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+			indexCodeList = append(indexCodeList, edbInfo.EdbCode)
+		}
+	}
+
+	relationMap := make(map[int]struct{})
+	if len(newMatterIds) > 0 {
+		//查询已有的matters
+		relationList, e := data_manage.GetEdbInfoRelationByReferObjectIds(newMatterIds, utils.EDB_RELATION_CALENDAR)
+		if e != nil {
+			err = fmt.Errorf("查询事件日历指标引用失败,%s", e.Error())
+			return
+		}
+		for _, relation := range relationList {
+			relationMap[relation.ReferObjectId] = struct{}{}
+		}
+	}
+	addList := make([]*data_manage.EdbInfoRelation, 0)
+	nowTime := time.Now()
+	for _, matter := range matters {
+		_, ok1 := relationMap[matter.FeCalendarMatterId]
+		edbInfo, ok2 := addEdbInfoIdMap[matter.EdbInfoId]
+		if !ok1 && ok2 {
+			tmp := &data_manage.EdbInfoRelation{
+				ReferObjectId:      matter.FeCalendarMatterId,
+				ReferObjectType:    utils.EDB_RELATION_CALENDAR,
+				ReferObjectSubType: 0,
+				EdbInfoId:          edbInfo.EdbInfoId,
+				EdbName:            edbInfo.EdbName,
+				Source:             edbInfo.Source,
+				EdbCode:            edbInfo.EdbCode,
+				CreateTime:         nowTime,
+				ModifyTime:         nowTime,
+				RelationTime:       matter.CreateTime,
+			}
+			tmp.RelationCode = fmt.Sprintf("%d_%d_%d_%d", tmp.EdbInfoId, tmp.ReferObjectId, tmp.ReferObjectType, tmp.ReferObjectSubType)
+			addList = append(addList, tmp)
+			//添加指标间接引用
+			if edbInfo.EdbType == 2 && edbInfo.EdbInfoType == 0 {
+				childEdbMappingIds, ok1 := calculateEdbMappingIdsMap[edbInfo.EdbInfoId]
+				if !ok1 {
+					continue
+				}
+				for _, childEdbMappingId := range childEdbMappingIds {
+					childEdbMapping, ok2 := calculateEdbMappingListMap[childEdbMappingId]
+					if !ok2 {
+						continue
+					}
+					if childEdbMapping.FromSource == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+						indexCodeList = append(indexCodeList, childEdbMapping.FromEdbCode)
+					}
+					tmp1 := &data_manage.EdbInfoRelation{
+						ReferObjectId:      matter.FeCalendarMatterId,
+						ReferObjectType:    utils.EDB_RELATION_CALENDAR,
+						ReferObjectSubType: 0,
+						EdbInfoId:          childEdbMapping.FromEdbInfoId,
+						EdbName:            childEdbMapping.FromEdbName,
+						Source:             childEdbMapping.FromSource,
+						EdbCode:            childEdbMapping.FromEdbCode,
+						CreateTime:         nowTime,
+						ModifyTime:         nowTime,
+						RelationTime:       nowTime,
+						RelationType:       1,
+						RootEdbInfoId:      edbInfo.EdbInfoId,
+						ChildEdbInfoId:     childEdbMapping.EdbInfoId,
+						RelationCode:       tmp.RelationCode,
+					}
+					addList = append(addList, tmp1)
+					refreshIds = append(refreshIds, childEdbMapping.FromEdbInfoId)
+					// todo 防止重复
+				}
+			}
+		}
+	}
+	//更新指标刷新状态为启用
+	err = data_manage.AddOrUpdateEdbInfoRelationMulti(addList, refreshIds, indexCodeList)
+	if err != nil {
+		err = fmt.Errorf("添加指标引用,%s", err.Error())
+		return
+	}
+	return
+}
+
+// GetEdbRelationList 获取指标引用列表
+func GetEdbRelationList(source, edbType int, classifyId, sysUserId, frequency, keyword, status string, startSize, pageSize int, sortParam, sortType string) (total int, list []*data_manage.BaseRelationEdbInfo, err error) {
+	var pars []interface{}
+	var condition string
+
+	list = make([]*data_manage.BaseRelationEdbInfo, 0)
+
+	isStop := -1
+	if status == `暂停` {
+		isStop = 1
+	} else if status == "启用" {
+		isStop = 0
+	}
+
+	switch source {
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL, utils.DATA_SOURCE_WIND:
+		condition += ` AND e.source = ? `
+		pars = append(pars, source)
+	}
+
+	if edbType == 2 { //计算指标
+		condition += ` AND e.edb_type = ? AND e.edb_info_type = 0`
+		pars = append(pars, edbType)
+	}
+
+	if isStop >= 0 {
+		condition += " AND e.no_update = ? "
+		pars = append(pars, isStop)
+	}
+
+	if classifyId != `` {
+		classifyIdSlice := strings.Split(classifyId, ",")
+		condition += ` AND e.classify_id IN (` + utils.GetOrmInReplace(len(classifyIdSlice)) + `)`
+		pars = append(pars, classifyIdSlice)
+	}
+	if sysUserId != `` {
+		sysUserIdSlice := strings.Split(sysUserId, ",")
+		condition += ` AND e.sys_user_id IN (` + utils.GetOrmInReplace(len(sysUserIdSlice)) + `)`
+		pars = append(pars, sysUserIdSlice)
+	}
+	if frequency != `` {
+		frequencySlice := strings.Split(frequency, ",")
+		condition += ` AND e.frequency IN (` + utils.GetOrmInReplace(len(frequencySlice)) + `)`
+		pars = append(pars, frequencySlice)
+	}
+	if keyword != `` {
+		keywordSlice := strings.Split(keyword, " ")
+		if len(keywordSlice) > 0 {
+			tmpConditionSlice := make([]string, 0)
+			tmpConditionSlice = append(tmpConditionSlice, ` e.edb_name like ? or e.edb_code like ? `)
+			pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+
+			for _, v := range keywordSlice {
+				if v == ` ` || v == `` {
+					continue
+				}
+				tmpConditionSlice = append(tmpConditionSlice, ` e.edb_name like ? or e.edb_code like ? `)
+				pars = utils.GetLikeKeywordPars(pars, v, 2)
+			}
+			condition += ` AND (` + strings.Join(tmpConditionSlice, " or ") + `)`
+
+		} else {
+			condition += ` AND (e.edb_name like ? or e.edb_code like ? )`
+			pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+		}
+	}
+
+	sortStr := ``
+	if sortParam != `` {
+		sortStr = fmt.Sprintf("%s %s,e.edb_info_id desc ", sortParam, sortType)
+	}
+
+	total, list, err = data_manage.GetEdbInfoRelationList(condition, pars, sortStr, startSize, pageSize)
+
+	return
+}
+
+// 查找当前计算指标的所有溯源指标
+func GetEdbListByEdbInfoId(edbInfoList []*data_manage.EdbInfo) (edbMappingListMap map[int]*data_manage.EdbInfoCalculateMapping, edbInfoMappingRootIdsMap map[int][]int, err error) {
+	if len(edbInfoList) == 0 {
+		return
+	}
+	edbInfoIds := make([]int, 0)
+	for _, v := range edbInfoList {
+		if v.EdbType == 2 && v.EdbInfoType == 0 { //普通计算指标,排除预算指标
+			edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+		}
+	}
+	if len(edbInfoIds) == 0 {
+		return
+	}
+	//查询指标信息
+	allEdbMappingMap := make(map[int][]*data_manage.EdbInfoCalculateMappingInfo, 0)
+	allMappingList, e := data_manage.GetEdbInfoCalculateMappingListByEdbInfoIds(edbInfoIds)
+	if e != nil {
+		err = fmt.Errorf("GetEdbInfoCalculateMappingListByEdbInfoIds err: %s", e.Error())
+		return
+	}
+	for _, v := range allMappingList {
+		if _, ok := allEdbMappingMap[v.EdbInfoId]; !ok {
+			allEdbMappingMap[v.EdbInfoId] = make([]*data_manage.EdbInfoCalculateMappingInfo, 0)
+		}
+		allEdbMappingMap[v.EdbInfoId] = append(allEdbMappingMap[v.EdbInfoId], v)
+	}
+	//查询指标映射
+	//查询所有指标数据
+	//查询这个指标相关的mapping信息放到数组里,
+	//将得到的指标ID信息放到数组里
+	hasFindMap := make(map[int]struct{})
+	edbInfoIdMap := make(map[int]struct{})
+	edbMappingList := make([]*data_manage.EdbInfoCalculateMapping, 0)
+	edbInfoMappingRootIdsMap = make(map[int][]int, 0)
+	edbMappingMap := make(map[int]struct{})
+	for _, edbInfo := range edbInfoList {
+		if edbInfo.EdbType == 2 && edbInfo.EdbInfoType == 0 {
+			edbInfoId := edbInfo.EdbInfoId
+			edbMappingList, err = getCalculateEdbInfoByEdbInfoId(allEdbMappingMap, edbInfoId, hasFindMap, edbInfoIdMap, edbMappingList, edbMappingMap, edbInfoMappingRootIdsMap, edbInfoId)
+			if err != nil {
+				err = fmt.Errorf(" GetCalculateEdbInfoByEdbInfoId err: %s", err.Error())
+				return
+			}
+		}
+	}
+	if len(edbMappingList) == 0 {
+		return
+	}
+	// 查询指标信息
+	// 指标信息map
+	edbInfoIdList := make([]int, 0)
+	for k, _ := range edbInfoIdMap {
+		edbInfoIdList = append(edbInfoIdList, k)
+	}
+	edbMappingListMap = make(map[int]*data_manage.EdbInfoCalculateMapping)
+
+	if len(edbMappingList) > 0 {
+		for _, v := range edbMappingList {
+			edbMappingListMap[v.EdbInfoCalculateMappingId] = v
+		}
+	}
+	return
+}
+
+// getCalculateEdbInfoByEdbInfoId 计算指标追溯
+func getCalculateEdbInfoByEdbInfoId(allEdbMappingMap map[int][]*data_manage.EdbInfoCalculateMappingInfo, edbInfoId int, hasFindMap map[int]struct{}, edbInfoIdMap map[int]struct{}, edbMappingList []*data_manage.EdbInfoCalculateMapping, edbMappingMap map[int]struct{}, edbInfoMappingRootIdsMap map[int][]int, rootEdbInfoId int) (newEdbMappingList []*data_manage.EdbInfoCalculateMapping, err error) {
+	newEdbMappingList = edbMappingList
+	_, ok := hasFindMap[edbInfoId]
+	if ok {
+		return
+	}
+
+	if _, ok1 := edbInfoIdMap[edbInfoId]; !ok1 {
+		edbInfoIdMap[edbInfoId] = struct{}{}
+	}
+	edbInfoMappingList := make([]*data_manage.EdbInfoCalculateMappingInfo, 0)
+	edbInfoMappingList, ok = allEdbMappingMap[edbInfoId]
+	if !ok {
+		edbInfoMappingList, err = data_manage.GetEdbInfoCalculateMappingListByEdbInfoId(edbInfoId)
+		if err != nil {
+			err = fmt.Errorf("GetEdbInfoCalculateMappingListByEdbInfoId err: %s", err.Error())
+			return
+		}
+	}
+	hasFindMap[edbInfoId] = struct{}{}
+	if len(edbInfoMappingList) > 0 {
+		fromEdbInfoIdList := make([]int, 0)
+		edbInfoMappingIdList := make([]int, 0)
+		for _, v := range edbInfoMappingList {
+			fromEdbInfoIdList = append(fromEdbInfoIdList, v.FromEdbInfoId)
+			edbInfoMappingIdList = append(edbInfoMappingIdList, v.EdbInfoCalculateMappingId)
+			if _, ok1 := edbInfoIdMap[v.FromEdbInfoId]; !ok1 {
+				edbInfoIdMap[v.FromEdbInfoId] = struct{}{}
+			}
+			if _, ok2 := edbMappingMap[v.EdbInfoCalculateMappingId]; !ok2 {
+				edbMappingMap[v.EdbInfoCalculateMappingId] = struct{}{}
+				tmp := &data_manage.EdbInfoCalculateMapping{
+					EdbInfoCalculateMappingId: v.EdbInfoCalculateMappingId,
+					EdbInfoId:                 v.EdbInfoId,
+					Source:                    v.Source,
+					SourceName:                v.SourceName,
+					EdbCode:                   v.EdbCode,
+					FromEdbInfoId:             v.FromEdbInfoId,
+					FromEdbCode:               v.FromEdbCode,
+					FromEdbName:               v.FromEdbName,
+					FromSource:                v.FromSource,
+					FromSourceName:            v.FromSourceName,
+					FromTag:                   v.FromTag,
+					Sort:                      v.Sort,
+					CreateTime:                v.CreateTime,
+					ModifyTime:                v.ModifyTime,
+				}
+				newEdbMappingList = append(newEdbMappingList, tmp)
+
+			}
+
+			if edbInfoId != v.FromEdbInfoId && (v.FromEdbType == 2 || v.FromEdbInfoType == 1) {
+				// 查过了就不查了
+				if _, ok2 := hasFindMap[v.FromEdbInfoId]; !ok2 {
+					newEdbMappingList, err = getCalculateEdbInfoByEdbInfoId(allEdbMappingMap, v.FromEdbInfoId, hasFindMap, edbInfoIdMap, newEdbMappingList, edbMappingMap, edbInfoMappingRootIdsMap, rootEdbInfoId)
+					if err != nil {
+						err = fmt.Errorf("traceEdbInfoByEdbInfoId err: %s", err.Error())
+						return
+					}
+				}
+			}
+			hasFindMap[v.FromEdbInfoId] = struct{}{}
+		}
+		edbInfoMappingRootIdsMap[rootEdbInfoId] = append(edbInfoMappingRootIdsMap[rootEdbInfoId], edbInfoMappingIdList...)
+	}
+
+	return
+}
+
+// SaveExcelEdbInfoRelation 添加/编辑表格时同步修改指标引用关联记录
+func SaveExcelEdbInfoRelation(excelInfoId, source int, addChildExcel bool) (err error) {
+	defer func() {
+		if err != nil {
+			err = fmt.Errorf("添加/编辑表格时同步修改指标引用关联记录失败,%s", err.Error())
+			go alarm_msg.SendAlarmMsg(err.Error(), 3)
+		}
+	}()
+	//查询和指标相关的时间序列表格和混合表格
+	if !utils.InArrayByInt([]int{utils.TIME_TABLE, utils.MIXED_TABLE, utils.BALANCE_TABLE}, source) {
+		return
+	}
+	if addChildExcel && source == utils.BALANCE_TABLE {
+		//查询excel信息,
+		excelInfoList, e := excelModel.GetChildExcelInfoByParentId(excelInfoId)
+		if e != nil {
+			err = fmt.Errorf("查询excel信息失败,错误:%s", e.Error())
+			return
+		}
+
+		//查询
+		if len(excelInfoList) > 0 {
+			//汇总表格ID
+			excelIds := make([]int, 0)
+			for _, v := range excelInfoList {
+				if v.BalanceType == 0 {
+					excelIds = append(excelIds, v.ExcelInfoId)
+				}
+			}
+			mappingList, e := excelModel.GetAllExcelEdbMappingByExcelInfoIds(excelIds)
+			if e != nil {
+				err = fmt.Errorf("查询和指标相关的表格失败,错误:%s", e.Error())
+				return
+			}
+			//整理map
+			mappingMap := make(map[int][]int)
+			for _, v := range mappingList {
+				mappingMap[v.ExcelInfoId] = append(mappingMap[v.ExcelInfoId], v.EdbInfoId)
+			}
+
+			// 添加指标引用
+			for _, v := range excelInfoList {
+				edbInfoIds, ok := mappingMap[v.ExcelInfoId]
+				if !ok {
+					continue
+				}
+				err = saveEdbInfoRelation(edbInfoIds, v.ExcelInfoId, utils.EDB_RELATION_TABLE, source)
+			}
+			//更新
+		}
+	}
+	mappingList, err := excelModel.GetAllExcelEdbMappingByExcelInfoId(excelInfoId)
+	if err != nil {
+		err = fmt.Errorf("查询和指标相关的表格失败,错误:%s", err.Error())
+		return
+	}
+	//查询指标ID
+	edbInfoIds := make([]int, 0)
+	for _, v := range mappingList {
+		edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+	}
+	//查询指标引用关联记录
+	//更新指标刷新状态为启用
+	if len(edbInfoIds) == 0 {
+		return
+	}
+	err = saveEdbInfoRelation(edbInfoIds, excelInfoId, utils.EDB_RELATION_TABLE, source)
+	return
+}
+
+// GetCalculateEdbByFromEdbInfo 找到依赖于该基础指标的所有计算指标
+func GetCalculateEdbByFromEdbInfo(edbInfoIds []int, calculateEdbIds []int, hasFind map[int]struct{}) (newCalculateEdbIds []int, err error) {
+	if len(edbInfoIds) == 0 {
+		return
+	}
+	newCalculateEdbIds = calculateEdbIds
+	newEdbInfoIds := make([]int, 0)
+	for _, v := range edbInfoIds {
+		if _, ok := hasFind[v]; ok {
+			continue
+		}
+		newEdbInfoIds = append(newEdbInfoIds, v)
+	}
+	if len(newEdbInfoIds) == 0 {
+		return
+	}
+	var condition string
+	var pars []interface{}
+	// 关联指标
+	condition += ` AND b.from_edb_info_id in (` + utils.GetOrmInReplace(len(newEdbInfoIds)) + `)`
+	pars = append(pars, newEdbInfoIds)
+
+	//获取关联图表列表
+	list, err := data_manage.GetRelationEdbInfoListMappingByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("获取关联指标信息失败,Err:%s", err.Error())
+		return
+	}
+	calculateEdbIdsTmp := make([]int, 0)
+	for _, mapping := range list {
+		if mapping.EdbType == 2 && mapping.EdbInfoType == 0 { // 如果指标库里的计算指标,则加入,否则继续找
+			newCalculateEdbIds = append(newCalculateEdbIds, mapping.EdbInfoId)
+			calculateEdbIdsTmp = append(calculateEdbIdsTmp, mapping.EdbInfoId)
+		}
+	}
+	for _, v := range newEdbInfoIds {
+		hasFind[v] = struct{}{}
+	}
+	if len(calculateEdbIdsTmp) > 0 {
+		newCalculateEdbIds, err = GetCalculateEdbByFromEdbInfo(calculateEdbIdsTmp, newCalculateEdbIds, hasFind)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}

+ 4 - 1
services/data/excel/excel_op.go

@@ -2,6 +2,7 @@ package excel
 
 import (
 	"errors"
+	"eta/eta_api/models/data_manage"
 	excelModel "eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
@@ -98,7 +99,7 @@ func Delete(excelInfo *excelModel.ExcelInfo, sysUser *system.Admin) (err error,
 			err = fmt.Errorf("删除图表,指标信息失败 Err:%s", err.Error())
 			return
 		}
-
+		_ = data_manage.DeleteEdbRelationByObjectIds(excelIds, utils.EDB_RELATION_TABLE)
 		return
 	}
 
@@ -107,6 +108,8 @@ func Delete(excelInfo *excelModel.ExcelInfo, sysUser *system.Admin) (err error,
 	excelInfo.ModifyTime = time.Now()
 	err = excelInfo.Update([]string{"IsDelete", "ModifyTime"})
 
+	// 删除指标引用
+	_ = data_manage.DeleteEdbRelationByObjectIds([]int{excelInfo.ExcelInfoId}, utils.EDB_RELATION_TABLE)
 	return
 }
 

+ 5 - 0
services/data/line_equation/chart_info.go

@@ -547,6 +547,8 @@ func BatchAddChartInfo(batchAddChartReq []request.AddChart, lineChartInfoConfig
 
 	// 更改es
 	for _, v := range batchAddChartList {
+		// 添加指标引用记录
+		_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, v.ChartInfo)
 		//添加es数据
 		go data.EsAddOrEditChartInfo(v.ChartInfo.ChartInfoId)
 	}
@@ -787,6 +789,9 @@ func BatchSaveChartInfo(multipleGraphConfigId int, batchAddChartReq []request.Ad
 
 	// 更改es
 	for _, v := range batchAddChartList {
+		// 添加指标引用记录
+		_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, v.ChartInfo)
+
 		//添加es数据
 		go data.EsAddOrEditChartInfo(v.ChartInfo.ChartInfoId)
 	}

+ 5 - 1
services/data/line_feature/chart_info.go

@@ -532,7 +532,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, edbInfoMapping *data_manage.C
 		err = errors.New("新增相关性图表失败, Err: " + e.Error())
 		return
 	}
-
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfoId)
 
@@ -659,6 +660,9 @@ func EditChartInfo(req data_manage.EditChartInfoReq, edbInfoMapping *data_manage
 	resp.UniqueCode = chartItem.UniqueCode
 	resp.ChartType = req.ChartType
 
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
+
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
 	//修改my eta es数据

+ 274 - 0
services/edb_info_replace.go

@@ -7,9 +7,11 @@ import (
 	excelModel "eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/data_manage/excel/request"
 	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/services/data"
 	"eta/eta_api/services/sandbox"
 	"eta/eta_api/utils"
 	"fmt"
+	"strconv"
 	"time"
 )
 
@@ -31,6 +33,7 @@ func DealReplaceEdbCache() {
 			}
 			oldEdbInfo := record.OldEdbInfo
 			newEdbInfo := record.NewEdbInfo
+			utils.FileLog.Info(fmt.Sprintf("指标开始替换 DealReplaceEdbCache: 旧指标ID:%d,新指标ID:%d", oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId))
 			deleteCache := true
 			setNxKey := fmt.Sprintf("EDB_INFO_REPLACE:%d-%d", oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId)
 			defer func() {
@@ -70,6 +73,9 @@ func DealReplaceEdbCache() {
 				err = fmt.Errorf("替换逻辑图中的指标失败,errmsg:%s", err.Error())
 				return
 			}
+
+			// todo 重置指标引用表
+			ReplaceEdbInRelation(oldEdbInfo, newEdbInfo)
 		})
 	}
 }
@@ -252,3 +258,271 @@ func replaceEdbInTimeExcel(oldEdbInfo, newEdbInfo *data_manage.EdbInfo, excelInf
 	newExcelInfo = excelInfo
 	return
 }
+
+func ReplaceEdbInRelation(oldEdbInfo, newEdbInfo *data_manage.EdbInfo) {
+	var err error
+	var logMsg string
+	var replaceTotal int
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf(" 替换指标引用表中的指标,并修改引用时间 replaceEdbInRelation  err: %v", err)
+			utils.FileLog.Info(msg)
+			fmt.Println(msg)
+			go alarm_msg.SendAlarmMsg(msg, 3)
+		}
+		if logMsg != `` {
+			utils.FileLog.Info(fmt.Sprintf("替换指标引用表中的指标记录 替换总数%d,旧的指标id:%d,新的指标id:%d;涉及到的引用id:%s", replaceTotal, oldEdbInfo.EdbInfoId, newEdbInfo.EdbInfoId, logMsg))
+		}
+	}()
+
+	calculateEdbMappingListMap := make(map[int]*data_manage.EdbInfoCalculateMapping)
+	calculateEdbMappingIdsMap := make(map[int][]int)
+	childEdbMappingIds := make([]int, 0)
+	//indexCodeList := make([]string, 0)
+	//refreshIds := make([]int, 0)
+
+	//分页查询,每次处理500条记录
+	pageSize := 500
+
+	// 替换直接引用中的指标
+	if newEdbInfo.EdbType == 2 {
+		edbInfoList := make([]*data_manage.EdbInfo, 0)
+		edbInfoList = append(edbInfoList, newEdbInfo)
+		calculateEdbMappingListMap, calculateEdbMappingIdsMap, err = data.GetEdbListByEdbInfoId(edbInfoList)
+		if err != nil {
+			err = fmt.Errorf("查询指标关联指标列表失败 Err:%s", err)
+			return
+		}
+		var ok bool
+		childEdbMappingIds, ok = calculateEdbMappingIdsMap[newEdbInfo.EdbInfoId]
+		if !ok {
+			err = fmt.Errorf("查询%d指标关联指标列表为空", newEdbInfo.EdbInfoId)
+			return
+		}
+	}
+	total, err := data_manage.GetReplaceEdbInfoRelationTotal(oldEdbInfo.EdbInfoId)
+	if err != nil {
+		err = fmt.Errorf("查询引用表中关联的指标总数失败 err: %v", err)
+		return
+	}
+	totalPage := 0
+	if total > 0 {
+		totalPage = (total + pageSize - 1) / pageSize // 使用整数除法,并添加一页以防有余数
+		//查询图表列表
+		for i := 0; i < totalPage; i += 1 {
+			startSize := i * pageSize
+			list, e := data_manage.GetReplaceEdbInfoRelationList(oldEdbInfo.EdbInfoId, startSize, pageSize)
+			if e != nil {
+				err = fmt.Errorf("查询图表关联指标列表失败 Err:%s", e)
+				return
+			}
+			if len(list) == 0 {
+				break
+			}
+			replaceTotal1, logMsg1, e := replaceEdbInRelation(oldEdbInfo, newEdbInfo, list, childEdbMappingIds, calculateEdbMappingListMap)
+			if e != nil {
+				err = e
+				return
+			}
+			replaceTotal += replaceTotal1
+			logMsg += logMsg1
+		}
+	}
+
+	// 更新间接引用中的指标
+	//查询相关的记录总数
+	total, err = data_manage.GetReplaceChildEdbInfoRelationTotal(oldEdbInfo.EdbInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		} else {
+			err = fmt.Errorf("查询引用表中关联的指标总数失败 err: %v", err)
+			return
+		}
+	}
+	if total > 0 {
+		totalPage = (total + pageSize - 1) / pageSize // 使用整数除法,并添加一页以防有余数
+		//查询列表
+		for i := 0; i < totalPage; i += 1 {
+			startSize := i * pageSize
+			tmpList, e := data_manage.GetReplaceChildEdbInfoRelationList(oldEdbInfo.EdbInfoId, startSize, pageSize)
+			if e != nil {
+				err = fmt.Errorf("查询图表关联指标列表失败 Err:%s", e)
+				return
+			}
+			// 查询直接引用
+			relationIds := make([]int, 0)
+			for _, v := range tmpList {
+				relationIds = append(relationIds, v.ParentRelationId)
+			}
+			if len(relationIds) > 0 {
+				list, e := data_manage.GetEdbInfoRelationByRelationIds(relationIds)
+				if e != nil {
+					err = fmt.Errorf("查询图表关联指标列表失败 Err:%s", e)
+					return
+				}
+				//查询直接引用指标关联关系
+				edbInfoListMap := make(map[int]struct{})
+				edbInfoIds := make([]int, 0)
+				for _, v := range list {
+					if _, ok := edbInfoListMap[v.EdbInfoId]; !ok {
+						edbInfoListMap[v.EdbInfoId] = struct{}{}
+						edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+					}
+				}
+				edbInfoList := make([]*data_manage.EdbInfo, 0)
+				if len(edbInfoIds) > 0 {
+					// 查询指标信息
+					edbInfoList, err = data_manage.GetEdbInfoByIdList(edbInfoIds)
+					if err != nil {
+						err = fmt.Errorf("查询指标信息失败 Err:%s", err)
+						return
+					}
+				}
+				calculateEdbMappingListMap, calculateEdbMappingIdsMap, err = data.GetEdbListByEdbInfoId(edbInfoList)
+				if err != nil {
+					err = fmt.Errorf("查询指标关联指标列表失败 Err:%s", err)
+					return
+				}
+
+				//如何过滤掉只有间接引用,没有直接引用的
+				replaceTotal1, logMsg1, e := UpdateSecondEdbInRelation(list, calculateEdbMappingListMap, calculateEdbMappingIdsMap, edbInfoList)
+				if e != nil {
+					err = e
+					return
+				}
+				replaceTotal += replaceTotal1
+				logMsg += logMsg1
+			}
+		}
+	}
+
+	return
+}
+
+// 完成直接引用中的指标替换工程
+func replaceEdbInRelation(oldEdbInfo, newEdbInfo *data_manage.EdbInfo, list []*data_manage.EdbInfoRelation, childEdbMappingIds []int, calculateEdbMappingListMap map[int]*data_manage.EdbInfoCalculateMapping) (replaceTotal int, logMsg string, err error) {
+	replaceEdbIds := make([]int, 0)
+	//calculateEdbMappingListMap := make(map[int]*data_manage.EdbInfoCalculateMapping)
+	//calculateEdbMappingIdsMap := make(map[int][]int)
+	//childEdbMappingIds := make([]int, 0)
+	indexCodeList := make([]string, 0)
+	addList := make([]*data_manage.EdbInfoRelation, 0)
+	refreshIds := make([]int, 0)
+	nowTime := time.Now()
+	for _, v := range list {
+		replaceEdbIds = append(replaceEdbIds, v.EdbInfoRelationId)
+		if newEdbInfo.EdbType == 2 {
+			for _, childEdbMappingId := range childEdbMappingIds {
+				childEdbMapping, ok2 := calculateEdbMappingListMap[childEdbMappingId]
+				if !ok2 {
+					continue
+				}
+
+				if childEdbMapping.FromSource == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+					indexCodeList = append(indexCodeList, childEdbMapping.FromEdbCode)
+				}
+				tmp1 := &data_manage.EdbInfoRelation{
+					ReferObjectId:      v.ReferObjectId,
+					ReferObjectType:    v.ReferObjectType,
+					ReferObjectSubType: v.ReferObjectSubType,
+					EdbInfoId:          childEdbMapping.FromEdbInfoId,
+					EdbName:            childEdbMapping.FromEdbName,
+					Source:             childEdbMapping.FromSource,
+					EdbCode:            childEdbMapping.FromEdbCode,
+					CreateTime:         nowTime,
+					ModifyTime:         nowTime,
+					RelationTime:       nowTime,
+					RelationType:       1,
+					RootEdbInfoId:      newEdbInfo.EdbInfoId,
+					ChildEdbInfoId:     childEdbMapping.EdbInfoId,
+				}
+				tmp1.RelationCode = fmt.Sprintf("%d_%d_%d_%d", tmp1.RootEdbInfoId, tmp1.ReferObjectId, tmp1.ReferObjectType, tmp1.ReferObjectSubType)
+				addList = append(addList, tmp1)
+				refreshIds = append(refreshIds, childEdbMapping.FromEdbInfoId)
+				// todo 防止重复
+			}
+		}
+		logMsg += strconv.Itoa(v.EdbInfoRelationId) + ";"
+	}
+	if len(replaceEdbIds) > 0 {
+		err = data_manage.ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo, replaceEdbIds, addList, refreshIds, indexCodeList)
+		if err != nil {
+			logMsg = ""
+			err = fmt.Errorf("替换指标引用表中的指标ID失败 Err:%s", err)
+			return
+		}
+		replaceTotal = len(replaceEdbIds)
+	}
+	return
+}
+
+func UpdateSecondEdbInRelation(list []*data_manage.EdbInfoRelation, calculateEdbMappingListMap map[int]*data_manage.EdbInfoCalculateMapping, calculateEdbMappingIdsMap map[int][]int, edbInfoList []*data_manage.EdbInfo) (replaceTotal int, logMsg string, err error) {
+	nowTime := time.Now()
+	edbInfoRelationIds := make([]int, 0)
+	indexCodeList := make([]string, 0)
+	addList := make([]*data_manage.EdbInfoRelation, 0)
+	refreshIds := make([]int, 0)
+	edbInfoMap := make(map[int]*data_manage.EdbInfo)
+	for _, v := range edbInfoList {
+		edbInfoMap[v.EdbInfoId] = v
+	}
+	// 查询所有的直接引用,删除所有的间接引用,添加所有直接引用的间接引用
+	for _, v := range list {
+		if v.RelationType == 0 {
+			edbInfoRelationIds = append(edbInfoRelationIds, v.EdbInfoRelationId)
+			edbInfo, ok := edbInfoMap[v.EdbInfoId]
+			if !ok {
+				err = fmt.Errorf("查询指标信息失败 EdbInfoId:%d", v.EdbInfoId)
+				return
+			}
+			if edbInfo.EdbType == 2 { //计算指标
+				childEdbMappingIds, ok := calculateEdbMappingIdsMap[edbInfo.EdbInfoId]
+				if !ok {
+					err = fmt.Errorf("查询%d指标关联指标列表为空", edbInfo.EdbInfoId)
+					return
+				}
+				for _, childEdbMappingId := range childEdbMappingIds {
+					childEdbMapping, ok2 := calculateEdbMappingListMap[childEdbMappingId]
+					if !ok2 {
+						continue
+					}
+
+					if childEdbMapping.FromSource == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+						indexCodeList = append(indexCodeList, childEdbMapping.FromEdbCode)
+					}
+					tmp1 := &data_manage.EdbInfoRelation{
+						ReferObjectId:      v.ReferObjectId,
+						ReferObjectType:    v.ReferObjectType,
+						ReferObjectSubType: v.ReferObjectSubType,
+						EdbInfoId:          childEdbMapping.FromEdbInfoId,
+						EdbName:            childEdbMapping.FromEdbName,
+						Source:             childEdbMapping.FromSource,
+						EdbCode:            childEdbMapping.FromEdbCode,
+						CreateTime:         nowTime,
+						ModifyTime:         nowTime,
+						RelationTime:       nowTime,
+						RelationType:       1,
+						RootEdbInfoId:      edbInfo.EdbInfoId,
+						ChildEdbInfoId:     childEdbMapping.EdbInfoId,
+					}
+					tmp1.RelationCode = fmt.Sprintf("%d_%d_%d_%d", tmp1.RootEdbInfoId, tmp1.ReferObjectId, tmp1.ReferObjectType, tmp1.ReferObjectSubType)
+					addList = append(addList, tmp1)
+					refreshIds = append(refreshIds, childEdbMapping.FromEdbInfoId)
+				}
+			}
+			logMsg += strconv.Itoa(v.EdbInfoRelationId) + ";"
+		}
+	}
+
+	if len(edbInfoRelationIds) > 0 {
+		err = data_manage.UpdateSecondRelationEdbInfoId(edbInfoRelationIds, addList, refreshIds, indexCodeList)
+		if err != nil {
+			logMsg = ""
+			err = fmt.Errorf("替换指标引用表中的指标ID失败 Err:%s", err)
+			return
+		}
+		replaceTotal = len(edbInfoRelationIds)
+	}
+	return
+}

+ 3 - 1
services/eta_forum/eta_forum_hub.go

@@ -267,8 +267,10 @@ func GetEdbListByEdbInfoId(edbInfoIds []int) (edbInfoList []*data_manage.EdbInfo
 
 	for _, v := range edbInfoList {
 		var dataList []*data_manage.EdbDataBase
-		if v.Source == utils.DATA_SOURCE_BUSINESS {
+		if v.Source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
 			dataList, err = data_manage.GetEdbDataBaseMongoByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
+		} else if v.Source == utils.DATA_SOURCE_THS && v.SubSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+			dataList, err = data_manage.GetThsHfEdbDataBaseMongoByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
 		} else {
 			dataList, err = data_manage.GetEdbDataBaseByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
 		}

+ 41 - 0
services/sandbox/sandbox.go

@@ -601,6 +601,27 @@ type ContentStruct struct {
 	} `json:"cells"`
 }
 
+type SendBoxNodeData struct {
+	linkData []SandBoxLinkData `json:"linkData"`
+	linkFold bool              `json:"linkFold"`
+}
+
+type SandBoxLinkData struct {
+	RId          string              `json:"RId"`
+	Id           int                 `json:"Id"`
+	Name         string              `json:"Name"`
+	Type         int                 `json:"Type"`
+	Editing      bool                `json:"editing"`
+	DatabaseType int                 `json:"databaseType"`
+	DetailParams SandBoxDetailParams `json:"detailParams"`
+}
+
+type SandBoxDetailParams struct {
+	Code       string `json:"code"`
+	Id         int    `json:"id"`
+	ClassifyId int    `json:"classifyId"`
+}
+
 // checkoutContent 校验内容是否变更
 func checkoutContent(oldContent, reqContent string) (isUpdate bool) {
 	defer func() {
@@ -855,6 +876,26 @@ func sandboxClassifyHaveChildV2(allNode []*sandbox.SandboxClassifyItems, node *s
 	return
 }
 
+func GetSandBoxEdbIdsByContent(content string) (edbInfoIds []int, err error) {
+	var contentInfo sandbox.ContentDataStruct
+	err = json.Unmarshal([]byte(content), &contentInfo)
+	if err != nil {
+		err = fmt.Errorf("json.Unmarshal err:%s", err.Error())
+		return
+	}
+	// 遍历所有节点
+	for _, node := range contentInfo.Cells {
+		if node.Data == nil {
+			continue
+		}
+		for _, v := range node.Data.LinkData {
+			if v.Type == 1 {
+				edbInfoIds = append(edbInfoIds, v.Id)
+			}
+		}
+	}
+	return
+}
 func ReplaceEdbInSandbox(oldEdbInfoId, newEdbInfoId int) (err error) {
 	updateTotal := 0
 	logMsg := ""

+ 4 - 0
utils/config.go

@@ -267,6 +267,10 @@ var (
 	ViperConfig *viper.Viper
 )
 
+var (
+	UseMongo bool // 是否使用mongo
+)
+
 func init() {
 	tmpRunMode, err := web.AppConfig.String("run_mode")
 	if err != nil {

+ 13 - 2
utils/constants.go

@@ -374,13 +374,15 @@ const DIR_MOD fs.FileMode = 0766 // Unix permission bits
 
 // 子数据来源渠道
 const (
-	DATA_SUB_SOURCE_EDB  = iota //经济数据库
-	DATA_SUB_SOURCE_DATE        //日期序列
+	DATA_SUB_SOURCE_EDB            = iota //经济数据库
+	DATA_SUB_SOURCE_DATE                  //日期序列
+	DATA_SUB_SOURCE_HIGH_FREQUENCY        //高频数据
 )
 
 const (
 	WindDbWsd = "wsd"
 	ThsDs     = "thsds"
+	ThsHf     = "thshf"
 )
 
 const (
@@ -434,6 +436,14 @@ const (
 	EnLangVersion = "en" // 英文语言版本
 )
 
+// 指标引用对象
+const (
+	EDB_RELATION_CHART    = 1 // 图表
+	EDB_RELATION_SANDBOX  = 2 // ETA逻辑
+	EDB_RELATION_CALENDAR = 3 // 事件日历
+	EDB_RELATION_TABLE    = 4 // 表格
+)
+
 // 指标计算方式
 const (
 	EdbBaseCalculateLjzzy                = 1  // 累计值转月->1
@@ -452,4 +462,5 @@ const (
 	EdbBaseCalculateLjzNczj              = 14 // 累计值年初至今->14
 	EdbBaseCalculateExponentialSmoothing = 15 // 指数修匀->15
 	EdbBaseCalculateRjz                  = 16 // 日均值->16
+
 )