瀏覽代碼

Merge remote-tracking branch 'origin/master' into ETA_2.4.0

# Conflicts:
#	controllers/data_manage/excel/excel_info.go
#	models/data_manage/excel/referenced_excel_config.go
#	models/db.go
#	services/ppt.go
Roc 3 月之前
父節點
當前提交
9620f2e963
共有 100 個文件被更改,包括 14895 次插入1349 次删除
  1. 15 6
      controllers/banner.go
  2. 1 1
      controllers/commodity_trade_base_index.go
  3. 23 1
      controllers/data_manage/ai_predict_model/classify.go
  4. 668 0
      controllers/data_manage/ai_predict_model/framework.go
  5. 66 2
      controllers/data_manage/ai_predict_model/index.go
  6. 101 71
      controllers/data_manage/base_from_ths_hf.go
  7. 77 15
      controllers/data_manage/bloomberg_data.go
  8. 43 17
      controllers/data_manage/business_data.go
  9. 25 3
      controllers/data_manage/chart_framework.go
  10. 13 5
      controllers/data_manage/chart_info.go
  11. 1653 0
      controllers/data_manage/clarksons_data.go
  12. 6 2
      controllers/data_manage/correlation/correlation_chart_info.go
  13. 8 3
      controllers/data_manage/cross_variety/chart_info.go
  14. 161 3
      controllers/data_manage/edb_classify.go
  15. 237 3
      controllers/data_manage/edb_info.go
  16. 10 1
      controllers/data_manage/edb_info_refresh.go
  17. 35 7
      controllers/data_manage/edb_info_relation.go
  18. 1 0
      controllers/data_manage/eia_steo.go
  19. 5 0
      controllers/data_manage/excel/balance_table.go
  20. 11 0
      controllers/data_manage/excel/custom_analysis.go
  21. 253 1
      controllers/data_manage/excel/excel_info.go
  22. 51 28
      controllers/data_manage/fenwei_data.go
  23. 8 3
      controllers/data_manage/future_good/future_good_chart_info.go
  24. 59 10
      controllers/data_manage/gl_data.go
  25. 1045 0
      controllers/data_manage/gpr_risk_data.go
  26. 22 0
      controllers/data_manage/jiayue_edb_source.go
  27. 6 2
      controllers/data_manage/line_equation/line_chart_info.go
  28. 6 2
      controllers/data_manage/line_feature/chart_info.go
  29. 45 11
      controllers/data_manage/manual_edb.go
  30. 9 0
      controllers/data_manage/my_chart.go
  31. 2 2
      controllers/data_manage/predict_edb_classify.go
  32. 186 70
      controllers/data_manage/predict_edb_info.go
  33. 6 2
      controllers/data_manage/range_analysis/chart_info.go
  34. 50 25
      controllers/data_manage/smm_api.go
  35. 3 0
      controllers/data_manage/smm_data.go
  36. 6 1
      controllers/data_manage/stl/stl.go
  37. 272 0
      controllers/data_source/data_source.go
  38. 256 0
      controllers/eta_forum/eta_forum.go
  39. 1908 0
      controllers/material/material.go
  40. 133 110
      controllers/report.go
  41. 101 119
      controllers/report_chapter.go
  42. 35 15
      controllers/report_v2.go
  43. 286 0
      controllers/residual_analysis/residual_analysis_controller.go
  44. 280 227
      controllers/resource.go
  45. 35 20
      controllers/sandbox/sandbox.go
  46. 4 1
      controllers/sys_department.go
  47. 9 2
      controllers/sys_group.go
  48. 5 2
      controllers/sys_role.go
  49. 93 3
      controllers/trade_analysis/trade_analysis.go
  50. 719 0
      controllers/trade_analysis/trade_analysis_correlation.go
  51. 842 0
      controllers/trade_analysis/trade_analysis_table.go
  52. 8 4
      controllers/trade_analysis/warehouse.go
  53. 366 0
      models/ai_predict_model/ai_predict_model_framework.go
  54. 144 0
      models/ai_predict_model/ai_predict_model_framework_node.go
  55. 2 0
      models/ai_predict_model/ai_predict_model_index.go
  56. 46 38
      models/business_conf.go
  57. 80 0
      models/common/common_classify.go
  58. 233 0
      models/data_manage/base_from_clarksons_classify.go
  59. 81 0
      models/data_manage/base_from_clarksons_data.go
  60. 424 0
      models/data_manage/base_from_clarksons_index.go
  61. 1 0
      models/data_manage/base_from_eia_stero.go
  62. 16 0
      models/data_manage/base_from_fenwei.go
  63. 280 0
      models/data_manage/base_from_gpr_risk.go
  64. 149 0
      models/data_manage/base_from_gpr_risk_classify.go
  65. 21 14
      models/data_manage/base_from_mtjh.go
  66. 1 1
      models/data_manage/base_from_smm_classify.go
  67. 7 0
      models/data_manage/chart_framework.go
  68. 4 3
      models/data_manage/chart_info.go
  69. 9 1
      models/data_manage/data_manage_permission/excel.go
  70. 2 0
      models/data_manage/edb_classify.go
  71. 6 6
      models/data_manage/edb_data_wind.go
  72. 51 35
      models/data_manage/edb_info.go
  73. 11 11
      models/data_manage/edb_info_relation.go
  74. 32 3
      models/data_manage/excel/excel_info.go
  75. 8 0
      models/data_manage/excel/referenced_excel_config.go
  76. 6 0
      models/data_manage/excel/response/excel_info.go
  77. 33 7
      models/data_manage/gl_data.go
  78. 1 0
      models/data_manage/my_chart.go
  79. 10 10
      models/data_manage/mysteel_chemical_index.go
  80. 3 0
      models/data_manage/predict_edb_conf.go
  81. 74 0
      models/data_manage/request/clarksons_data.go
  82. 12 0
      models/data_manage/request/edb_info.go
  83. 9 7
      models/data_manage/request/predict_edb_info.go
  84. 35 0
      models/data_manage/response/clarksons_data.go
  85. 3 2
      models/data_manage/smm_data.go
  86. 10 0
      models/data_manage/trade_analysis/base_from_trade_exchange.go
  87. 35 0
      models/data_manage/trade_analysis/request/trade_analysis_correlation.go
  88. 37 0
      models/data_manage/trade_analysis/request/trade_analysis_table.go
  89. 17 0
      models/data_manage/trade_analysis/response/trade_analysis_correlation.go
  90. 18 0
      models/data_manage/trade_analysis/response/trade_analysis_table.go
  91. 246 359
      models/data_manage/trade_analysis/trade_analysis.go
  92. 43 0
      models/data_manage/trade_analysis/trade_analysis_correlation.go
  93. 41 0
      models/data_manage/trade_analysis/trade_analysis_table.go
  94. 188 0
      models/data_manage/trade_analysis/trade_analysis_table_column.go
  95. 13 13
      models/data_manage/trade_analysis/warehouse_process_classify.go
  96. 18 16
      models/data_source/base_from_sci99.go
  97. 2119 0
      models/data_source/data_source.go
  98. 2 0
      models/data_source/icpi.go
  99. 21 2
      models/db.go
  100. 25 21
      models/manual_edb.go

+ 15 - 6
controllers/banner.go

@@ -4,7 +4,6 @@ import (
 	"eta/eta_api/models"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
-	"github.com/h2non/filetype"
 	"io/ioutil"
 	"os"
 	"path"
@@ -53,14 +52,24 @@ func (this *BannerController) Upload() {
 		br.ErrMsg = "读取文件失败, Err: " + e.Error()
 		return
 	}
-	pass := filetype.IsImage(fileData)
-	if !pass {
-		br.Msg = "文件格式有误"
-		br.ErrMsg = "文件格式有误"
+	//pass := filetype.IsImage(fileData)
+	//if !pass {
+	//	br.Msg = "文件格式有误"
+	//	br.ErrMsg = "文件格式有误"
+	//	return
+	//}
+	ext := path.Ext(h.Filename)
+	if !utils.IsValidType(fileData, []utils.SourceType{
+		utils.Image,
+	}, []string{
+		"jpg",
+		"png",
+	}, ext) {
+		br.Msg = "文件格式不支持"
+		br.ErrMsg = "文件格式不支持"
 		return
 	}
 
-	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)

+ 1 - 1
controllers/commodity_trade_base_index.go

@@ -2741,7 +2741,7 @@ func (this *TradeCommonController) MtjhSingleData() {
 	}
 
 	modifyTime, err := data_manage.GetMtjhIndexLatestDate(indexCode)
-	if err != nil {
+	if err != nil && err.Error() != utils.ErrNoRow() {
 		br.Msg = "获取更新时间失败"
 		br.ErrMsg = "获取更新时间失败,Err:" + err.Error()
 		return

+ 23 - 1
controllers/data_manage/ai_predict_model/classify.go

@@ -6,7 +6,9 @@ import (
 	"eta/eta_api/models"
 	aiPredictModel "eta/eta_api/models/ai_predict_model"
 	"eta/eta_api/models/data_manage"
+	dataSourceModel "eta/eta_api/models/data_source"
 	"eta/eta_api/services"
+	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
 	"sort"
@@ -430,7 +432,7 @@ func (this *AiPredictModelClassifyController) Remove() {
 	// 删除标的
 	if req.IndexId > 0 {
 		indexOb := new(aiPredictModel.AiPredictModelIndex)
-		_, e := indexOb.GetItemById(req.IndexId)
+		aiIndex, e := indexOb.GetItemById(req.IndexId)
 		if e != nil {
 			if e.Error() == utils.ErrNoRow() {
 				br.Ret = 200
@@ -449,6 +451,26 @@ func (this *AiPredictModelClassifyController) Remove() {
 			br.ErrMsg = fmt.Sprintf("删除标的及数据失败, %v", e)
 			return
 		}
+
+		// ES标记删除
+		go func() {
+			indexItem := new(dataSourceModel.SearchDataSource)
+			indexItem.PrimaryId = aiIndex.AiPredictModelIndexId
+			indexItem.IndexName = aiIndex.IndexName
+			indexItem.IndexCode = aiIndex.IndexCode
+			indexItem.ClassifyId = aiIndex.ClassifyId
+			indexItem.Source = utils.DATA_SOURCE_AI_PREDICT_MODEL
+			indexItem.SourceName = "AI预测模型"
+			indexItem.IsDeleted = 1
+			indexItem.CreateTime = utils.TimeTransferString(utils.FormatDateTime, aiIndex.CreateTime)
+			indexItem.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, aiIndex.ModifyTime)
+
+			docId := fmt.Sprintf("%d-%d", indexItem.Source, indexItem.PrimaryId)
+			if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+				utils.FileLog.Info("AI预测模型-标记删除es失败, %v", e)
+				return
+			}
+		}()
 	}
 
 	br.Ret = 200

+ 668 - 0
controllers/data_manage/ai_predict_model/framework.go

@@ -0,0 +1,668 @@
+package ai_predict_model
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	aiPredictModel "eta/eta_api/models/ai_predict_model"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// AiPredictModelFrameworkController 模型框架
+type AiPredictModelFrameworkController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 列表
+// @Description 列表
+// @Param   AdminId		query	int		false	"创建人ID"
+// @Param   Visibility	query	int		false	"范围: 0-所有; 1-私有; 2-公开"
+// @Param   Keyword		query	string	false	"关键词"
+// @Success 200 Ret=200 获取成功
+// @router /framework/list [get]
+func (c *AiPredictModelFrameworkController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	adminId, _ := c.GetInt("AdminId")
+	keyword := c.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
+
+	frameworkOb := new(aiPredictModel.AiPredictModelFramework)
+
+	cond := ``
+	pars := make([]interface{}, 0)
+	if adminId > 0 {
+		cond += fmt.Sprintf(` AND %s = ?`, aiPredictModel.AiPredictModelFrameworkColumns.AdminId)
+		pars = append(pars, adminId)
+	}
+	if keyword != "" {
+		cond += fmt.Sprintf(` AND %s LIKE ?`, aiPredictModel.AiPredictModelFrameworkColumns.FrameworkName)
+		pars = append(pars, "%"+keyword+"%")
+	}
+
+	orderRule := `sort ASC, create_time DESC`
+	list, e := frameworkOb.GetItemsByCondition(cond, pars, []string{}, orderRule)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取框架列表失败, Err: " + e.Error()
+		return
+	}
+	resp := make([]*aiPredictModel.AiPredictModelFrameworkItem, 0)
+	for _, v := range list {
+		t := aiPredictModel.FormatAiPredictModelFramework2Item(v, make([]*aiPredictModel.AiPredictModelFrameworkNodeItem, 0))
+		if t.AdminId == sysUser.AdminId || utils.IsAdminRole(sysUser.RoleTypeCode) {
+			t.Button.OpButton = true
+			t.Button.DeleteButton = true
+			t.Button.MoveButton = true
+		}
+		resp = append(resp, t)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增框架
+// @Description 新增框架
+// @Param	request	body aiPredictModel.AiPredictModelFrameworkAddReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /framework/add [post]
+func (c *AiPredictModelFrameworkController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 参数校验
+	var req aiPredictModel.AiPredictModelFrameworkAddReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	req.FrameworkName = strings.TrimSpace(req.FrameworkName)
+	if req.FrameworkName == "" {
+		br.Msg = "框架名称不可为空"
+		return
+	}
+
+	// 重名校验
+	{
+		ob := new(aiPredictModel.AiPredictModelFramework)
+		cond := fmt.Sprintf(` AND %s = ? AND %s = ?`, aiPredictModel.AiPredictModelFrameworkColumns.FrameworkName, aiPredictModel.AiPredictModelFrameworkColumns.AdminId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.FrameworkName, sysUser.AdminId)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名框架失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "框架名称已存在,请重新输入"
+			return
+		}
+	}
+
+	now := time.Now().Local()
+	frameworkCode := utils.MD5(fmt.Sprint(now.UnixMilli()))
+	item := new(aiPredictModel.AiPredictModelFramework)
+	item.FrameworkName = req.FrameworkName
+	item.FrameworkCode = frameworkCode
+	item.FrameworkImg = req.FrameworkImg
+	item.FrameworkContent = req.FrameworkContent
+	item.AdminId = sysUser.AdminId
+	item.AdminName = sysUser.RealName
+	item.CreateTime = now
+	item.ModifyTime = now
+	nodes := make([]*aiPredictModel.AiPredictModelFrameworkNode, 0)
+	itemNodes := make([]*aiPredictModel.AiPredictModelFrameworkNodeItem, 0)
+	if len(req.Nodes) > 0 {
+		for _, v := range req.Nodes {
+			if v.AiPredictModelIndexId <= 0 {
+				continue
+			}
+			t := new(aiPredictModel.AiPredictModelFrameworkNode)
+			t.FrameworkName = req.FrameworkName
+			t.NodeId = v.NodeId
+			t.NodeName = v.NodeName
+			t.AiPredictModelIndexId = v.AiPredictModelIndexId
+			t.CreateTime = now
+			nodes = append(nodes, t)
+			num := 1
+			// 响应节点数据
+			td := aiPredictModel.FormatAiPredictModelFrameworkNode2Item(t, num, map[int]*aiPredictModel.AiPredictModelIndex{})
+			itemNodes = append(itemNodes, td)
+		}
+	}
+	if e := item.CreateFrameworkAndNodes(item, nodes); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "新增框架及节点失败, Err: " + e.Error()
+		return
+	}
+	detail := aiPredictModel.FormatAiPredictModelFramework2Item(item, itemNodes)
+
+	br.Data = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.IsAddLog = true
+}
+
+// Edit
+// @Title 编辑框架
+// @Description 编辑框架
+// @Param	request	body aiPredictModel.AiPredictModelFrameworkEditReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /framework/edit [post]
+func (c *AiPredictModelFrameworkController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 参数校验
+	var req aiPredictModel.AiPredictModelFrameworkEditReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.AiPredictModelFrameworkId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, AiPredictModelFrameworkId: %d", req.AiPredictModelFrameworkId)
+		return
+	}
+	req.FrameworkName = strings.TrimSpace(req.FrameworkName)
+	if req.FrameworkName == "" {
+		br.Msg = "框架名称不可为空"
+		return
+	}
+
+	frameworkOb := new(aiPredictModel.AiPredictModelFramework)
+	item, e := frameworkOb.GetItemById(req.AiPredictModelFrameworkId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "框架不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取框架失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作权限校验
+	if item.AdminId != sysUser.AdminId && !utils.IsAdminRole(sysUser.RoleTypeCode) {
+		br.Msg = "您没有权限操作该框架"
+		return
+	}
+
+	// 重名校验
+	{
+		ob := new(aiPredictModel.AiPredictModelFramework)
+		cond := fmt.Sprintf(` AND %s <> ? AND %s = ? AND %s = ?`, ob.PrimaryId(), aiPredictModel.AiPredictModelFrameworkColumns.FrameworkName, aiPredictModel.AiPredictModelFrameworkColumns.AdminId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.AiPredictModelFrameworkId, req.FrameworkName, sysUser.AdminId)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名框架失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "框架名称已存在,请重新输入"
+			return
+		}
+	}
+
+	now := time.Now().Local()
+	item.FrameworkName = req.FrameworkName
+	item.FrameworkImg = req.FrameworkImg
+	item.FrameworkContent = req.FrameworkContent
+	item.ModifyTime = now
+	updateCols := []string{"FrameworkName", "FrameworkImg", "FrameworkContent", "ModifyTime"}
+	nodes := make([]*aiPredictModel.AiPredictModelFrameworkNode, 0)
+	itemNodes := make([]*aiPredictModel.AiPredictModelFrameworkNodeItem, 0)
+	if len(req.Nodes) > 0 {
+		for _, v := range req.Nodes {
+			if v.AiPredictModelIndexId <= 0 {
+				continue
+			}
+			t := new(aiPredictModel.AiPredictModelFrameworkNode)
+			t.AiPredictModelFrameworkId = req.AiPredictModelFrameworkId
+			t.FrameworkName = req.FrameworkName
+			t.NodeId = v.NodeId
+			t.NodeName = v.NodeName
+			t.AiPredictModelIndexId = v.AiPredictModelIndexId
+			t.CreateTime = now
+			nodes = append(nodes, t)
+
+			// 响应节点数据
+			td := aiPredictModel.FormatAiPredictModelFrameworkNode2Item(t, 1, map[int]*aiPredictModel.AiPredictModelIndex{})
+			itemNodes = append(itemNodes, td)
+		}
+	}
+	if e := item.EditFrameworkAndNodes(item, updateCols, nodes); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "编辑框架及节点失败, Err: " + e.Error()
+		return
+	}
+	detail := aiPredictModel.FormatAiPredictModelFramework2Item(item, itemNodes)
+
+	br.Data = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.IsAddLog = true
+}
+
+// Remove
+// @Title 删除框架
+// @Description 删除视频
+// @Param	request	body aiPredictModel.AiPredictModelFrameworkRemoveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /framework/remove [post]
+func (c *AiPredictModelFrameworkController) Remove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 参数校验
+	var req aiPredictModel.AiPredictModelFrameworkRemoveReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.AiPredictModelFrameworkId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, AiPredictModelFrameworkId: %d", req.AiPredictModelFrameworkId)
+		return
+	}
+
+	ob := new(aiPredictModel.AiPredictModelFramework)
+	item, e := ob.GetItemById(req.AiPredictModelFrameworkId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取框架失败, Err: " + e.Error()
+		return
+	}
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN && item.AdminId != sysUser.AdminId {
+		br.Msg = "无权操作"
+		return
+	}
+
+	if e := item.RemoveFrameworkAndNodes(req.AiPredictModelFrameworkId); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "删除框架失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+	br.IsAddLog = true
+}
+
+// Rename
+// @Title 重命名框架
+// @Description 重命名框架
+// @Param	request	body aiPredictModel.AiPredictModelFrameworkRenameReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /framework/rename [post]
+func (c *AiPredictModelFrameworkController) Rename() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 参数校验
+	var req aiPredictModel.AiPredictModelFrameworkRenameReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.AiPredictModelFrameworkId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, AiPredictModelFrameworkId: %d", req.AiPredictModelFrameworkId)
+		return
+	}
+	req.FrameworkName = strings.TrimSpace(req.FrameworkName)
+	if req.FrameworkName == "" {
+		br.Msg = "框架名称不可为空"
+		return
+	}
+
+	frameworkOb := new(aiPredictModel.AiPredictModelFramework)
+	item, e := frameworkOb.GetItemById(req.AiPredictModelFrameworkId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "框架不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取框架失败, Err: " + e.Error()
+		return
+	}
+
+	// 操作权限校验
+	if item.AdminId != sysUser.AdminId && !utils.IsAdminRole(sysUser.RoleTypeCode) {
+		br.Msg = "您没有权限操作该框架"
+		return
+	}
+
+	// 重名校验
+	{
+		ob := new(aiPredictModel.AiPredictModelFramework)
+		cond := fmt.Sprintf(` AND %s <> ? AND %s = ? AND %s = ?`, ob.PrimaryId(), aiPredictModel.AiPredictModelFrameworkColumns.FrameworkName, aiPredictModel.AiPredictModelFrameworkColumns.AdminId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.AiPredictModelFrameworkId, req.FrameworkName, sysUser.AdminId)
+		exist, e := ob.GetItemByCondition(cond, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取重名框架失败, Err: " + e.Error()
+			return
+		}
+		if exist != nil {
+			br.Msg = "框架名称已存在,请重新输入"
+			return
+		}
+	}
+
+	now := time.Now().Local()
+	item.FrameworkName = req.FrameworkName
+	item.ModifyTime = now
+	updateCols := []string{"FrameworkName", "ModifyTime"}
+	if e := item.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "框架重命名失败, Err: " + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Move
+// @Title 移动排序
+// @Description 移动排序
+// @Param	request	body aiPredictModel.AiPredictModelFrameworkMoveReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /framework/move [post]
+func (c *AiPredictModelFrameworkController) Move() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 参数校验
+	var req aiPredictModel.AiPredictModelFrameworkMoveReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.AiPredictModelFrameworkId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, AiPredictModelFrameworkId: %d", req.AiPredictModelFrameworkId)
+		return
+	}
+
+	frameworkOb := new(aiPredictModel.AiPredictModelFramework)
+	item, e := frameworkOb.GetItemById(req.AiPredictModelFrameworkId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "框架不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取框架失败, Err: " + e.Error()
+		return
+	}
+
+	updateCols := make([]string, 0)
+	// 上一个兄弟节点
+	if req.PrevAiPredictModelFrameworkId > 0 {
+		prev, e := frameworkOb.GetItemById(req.PrevAiPredictModelFrameworkId)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取上一个兄弟节点失败, Err: " + e.Error()
+			return
+		}
+
+		// 两个兄弟节点之间
+		if req.NextAiPredictModelFrameworkId > 0 {
+			next, e := frameworkOb.GetItemById(req.PrevAiPredictModelFrameworkId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取下一个兄弟节点失败, Err: " + e.Error()
+				return
+			}
+			// 如果上一个与下一个排序权重是一致的, 那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2, 自己变成上一个兄弟的排序权重+1
+			if prev.Sort == next.Sort || prev.Sort == item.Sort {
+				strUpdate := `sort + 2`
+				_ = aiPredictModel.UpdateAiPredictModelFrameworkSort(sysUser.AdminId, prev.AiPredictModelFrameworkId, prev.Sort, strUpdate)
+			} else {
+				// 如果下一个排序权重正好是上一个节点的下一层, 那么需要再加一层了
+				if next.Sort-prev.Sort == 1 {
+					//变更兄弟节点的排序
+					strUpdate := `sort + 1`
+					_ = aiPredictModel.UpdateAiPredictModelFrameworkSort(sysUser.AdminId, 0, prev.Sort, strUpdate)
+				}
+			}
+		}
+
+		// 上一个兄弟节点sort+1
+		item.Sort = prev.Sort + 1
+		item.ModifyTime = time.Now()
+		updateCols = append(updateCols, "Sort", "ModifyTime")
+	} else {
+		first, err := aiPredictModel.GetFirstAiPredictModelFramework()
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "获取我的模型框架排首位的数据失败, Err:" + err.Error()
+			return
+		}
+		if first != nil && first.Sort == 0 {
+			strUpdate := ` sort + 1 `
+			_ = aiPredictModel.UpdateAiPredictModelFrameworkSort(sysUser.AdminId, first.AiPredictModelFrameworkId-1, 0, strUpdate)
+		}
+
+		// 排首位
+		item.Sort = 0
+		item.ModifyTime = time.Now()
+		updateCols = append(updateCols, "Sort", "ModifyTime")
+	}
+
+	if len(updateCols) > 0 {
+		if e := item.Update(updateCols); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新框架排序失败, Err: " + e.Error()
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 框架详情
+// @Description 框架详情
+// @Param   AiPredictModelFrameworkId  query  int  true  "框架ID"
+// @Success 200 Ret=200 操作成功
+// @router /framework/detail [get]
+func (c *AiPredictModelFrameworkController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	frameworkId, _ := c.GetInt("AiPredictModelFrameworkId")
+	if frameworkId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, AiPredictModelFrameworkId: %d", frameworkId)
+		return
+	}
+
+	frameworkOb := new(aiPredictModel.AiPredictModelFramework)
+	item, e := frameworkOb.GetItemById(frameworkId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "框架不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取框架失败, Err: " + e.Error()
+		return
+	}
+
+	// 获取节点
+	nodeOb := new(aiPredictModel.AiPredictModelFrameworkNode)
+	nodeCond := ` AND ai_predict_model_framework_id = ?`
+	nodePars := make([]interface{}, 0)
+	nodePars = append(nodePars, frameworkId)
+	nodes, e := nodeOb.GetItemsByCondition(nodeCond, nodePars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取框架节点失败, Err: " + e.Error()
+		return
+	}
+
+	// 模型标的map
+	aiPredictModelIndexMap := make(map[int]*aiPredictModel.AiPredictModelIndex)
+	{
+		aiPredictModelIndexIdList := make([]interface{}, 0)
+		for _, v := range nodes {
+			if v.AiPredictModelIndexId > 0 {
+				aiPredictModelIndexIdList = append(aiPredictModelIndexIdList, v.AiPredictModelIndexId)
+			}
+		}
+
+		indexIdNum := len(aiPredictModelIndexIdList)
+		if indexIdNum > 0 {
+			indexObj := aiPredictModel.AiPredictModelIndex{}
+			indexList, e := indexObj.GetItemsByCondition(` AND ai_predict_model_index_id in (`+utils.GetOrmInReplace(indexIdNum)+`)`, aiPredictModelIndexIdList, []string{}, "")
+			if e != nil {
+				if e.Error() == utils.ErrNoRow() {
+					br.Msg = "框架不存在, 请刷新页面"
+					return
+				}
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取标的列表失败, Err: " + e.Error()
+				return
+			}
+			for _, v := range indexList {
+				aiPredictModelIndexMap[v.AiPredictModelIndexId] = v
+			}
+		}
+	}
+
+	// 格式化响应数据
+	itemNodes := make([]*aiPredictModel.AiPredictModelFrameworkNodeItem, 0)
+	for _, v := range nodes {
+		if v.NodeId == "" {
+			continue
+		}
+		num := 1
+
+		itemNodes = append(itemNodes, aiPredictModel.FormatAiPredictModelFrameworkNode2Item(v, num, aiPredictModelIndexMap))
+	}
+	detail := aiPredictModel.FormatAiPredictModelFramework2Item(item, itemNodes)
+
+	br.Data = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 66 - 2
controllers/data_manage/ai_predict_model/index.go

@@ -5,8 +5,10 @@ import (
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	aiPredictModel "eta/eta_api/models/ai_predict_model"
+	dataSourceModel "eta/eta_api/models/data_source"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
+	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -28,6 +30,7 @@ type AiPredictModelIndexController struct {
 // @Param   PageSize   query   int   true   "每页数据条数"
 // @Param   CurrentIndex   query   int   true   "当前页页码,从1开始"
 // @Param   ClassifyId   query   int   false   "分类id"
+// @Param   IndexId   query   int   false   "模型标的ID"
 // @Param   Keyword   query   string   false   "搜索关键词"
 // @Success 200 {object} data_manage.ChartListResp
 // @router /index/list [get]
@@ -47,7 +50,11 @@ func (this *AiPredictModelIndexController) List() {
 	pageSize, _ := this.GetInt("PageSize")
 	currentIndex, _ := this.GetInt("CurrentIndex")
 	classifyId, _ := this.GetInt("ClassifyId")
+	indexId, _ := this.GetInt("IndexId")
 	keyword := this.GetString("KeyWord")
+	if keyword == "" {
+		keyword = this.GetString("Keyword")
+	}
 	keyword = strings.TrimSpace(keyword)
 	resp := new(aiPredictModel.AiPredictModelIndexPageListResp)
 
@@ -77,17 +84,47 @@ func (this *AiPredictModelIndexController) List() {
 	}
 
 	// 筛选条件
+	highlightMap := make(map[int]string)
 	indexOb := new(aiPredictModel.AiPredictModelIndex)
 	var cond string
 	var pars []interface{}
 	{
+		if indexId > 0 {
+			cond += fmt.Sprintf(" AND %s = ?", indexOb.Cols().PrimaryId)
+			pars = append(pars, indexId)
+		}
 		if classifyId > 0 {
 			cond += fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
 			pars = append(pars, classifyId)
 		}
+		//if keyword != "" {
+		//	cond += fmt.Sprintf(" AND %s LIKE ?", indexOb.Cols().IndexName)
+		//	pars = append(pars, fmt.Sprint("%", keyword, "%"))
+		//}
+
+		// 有关键词从es中搜索
 		if keyword != "" {
-			cond += fmt.Sprintf(" AND %s LIKE ?", indexOb.Cols().IndexName)
-			pars = append(pars, fmt.Sprint("%", keyword, "%"))
+			_, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, keyword, utils.DATA_SOURCE_AI_PREDICT_MODEL, 0, []int{}, []int{}, []string{}, startSize, pageSize)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("ES-搜索手工指标列表失败, %v", e)
+				return
+			}
+			if len(list) == 0 {
+				resp.List = make([]*aiPredictModel.AiPredictModelIndexItem, 0)
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				br.Data = resp
+				return
+			}
+			var ids []int
+			for _, v := range list {
+				ids = append(ids, v.PrimaryId)
+				highlightMap[v.PrimaryId] = v.SearchText
+			}
+			cond += fmt.Sprintf(` AND %s IN (%s)`, indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+			pars = append(pars, ids)
 		}
 	}
 
@@ -108,6 +145,12 @@ func (this *AiPredictModelIndexController) List() {
 	for _, v := range list {
 		t := v.Format2Item()
 		t.ClassifyName = classifyIdName[v.ClassifyId]
+		// 搜索高亮
+		t.SearchText = v.IndexName
+		s := highlightMap[v.AiPredictModelIndexId]
+		if s != "" {
+			t.SearchText = s
+		}
 		pageList = append(pageList, t)
 	}
 
@@ -564,6 +607,27 @@ func (this *AiPredictModelIndexController) Import() {
 		return
 	}
 
+	// 写入es
+	go func() {
+		for _, v := range importIndexes {
+			indexItem := new(dataSourceModel.SearchDataSource)
+			indexItem.PrimaryId = v.Index.AiPredictModelIndexId
+			indexItem.IndexName = v.Index.IndexName
+			indexItem.IndexCode = v.Index.IndexCode
+			indexItem.ClassifyId = v.Index.ClassifyId
+			indexItem.Source = utils.DATA_SOURCE_AI_PREDICT_MODEL
+			indexItem.SourceName = "AI预测模型"
+			indexItem.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.Index.CreateTime)
+			indexItem.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, v.Index.ModifyTime)
+
+			docId := fmt.Sprintf("%d-%d", indexItem.Source, indexItem.PrimaryId)
+			if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+				utils.FileLog.Info("AI预测模型-写入es失败, %v", e)
+				continue
+			}
+		}
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 101 - 71
controllers/data_manage/base_from_ths_hf.go

@@ -5,8 +5,10 @@ import (
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	dataSourceModel "eta/eta_api/models/data_source"
 	"eta/eta_api/models/mgo"
 	"eta/eta_api/services/data"
+	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -296,6 +298,7 @@ func (this *BaseFromThsHfController) List() {
 		br.ErrMsg = fmt.Sprintf("参数有误, SortType: %d", params.SortType)
 		return
 	}
+	params.Keywords = strings.TrimSpace(params.Keywords)
 	resp := new(data_manage.ThsHfIndexPageListResp)
 	resp.List = make([]*data_manage.BaseFromThsHfIndexItem, 0)
 
@@ -316,18 +319,30 @@ func (this *BaseFromThsHfController) List() {
 		//classifies = list
 	}
 
+	// 分页查询
+	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)
+
 	// 筛选项
 	var (
-		cond      string
-		pars      []interface{}
-		listOrder string
+		cond         string
+		pars         []interface{}
+		listOrder    string
+		classifyIds  []int
+		adminIds     []int
+		frequencyArr []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 {
@@ -344,65 +359,23 @@ func (this *BaseFromThsHfController) List() {
 			}
 			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)
+			frequencyArr = strings.Split(params.Frequency, ",")
+			if len(frequencyArr) == 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().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 {
@@ -410,15 +383,60 @@ func (this *BaseFromThsHfController) List() {
 				}
 			}
 			if len(adminIds) > 0 {
-				cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().SysUserId, utils.GetOrmInReplace(len(adminIds)))
-				pars = append(pars, adminIds)
+				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().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)
+			indexCodeCol := indexOb.Cols().IndexCode
+			indexNameCol := indexOb.Cols().IndexName
+			keywordArr := strings.Split(params.Keywords, " ")
+			if len(keywordArr) > 1 {
+				sliceArr := make([]string, 0)
+				sliceArr = append(sliceArr, fmt.Sprintf(` %s LIKE ? OR %s LIKE ? `, indexCodeCol, indexNameCol))
+				pars = utils.GetLikeKeywordPars(pars, params.Keywords, 2)
+				for _, v := range keywordArr {
+					if v == ` ` || v == `` {
+						continue
+					}
+					sliceArr = append(sliceArr, fmt.Sprintf(` %s LIKE ? OR %s LIKE ? `, indexCodeCol, indexNameCol))
+					pars = utils.GetLikeKeywordPars(pars, v, 2)
+				}
+				cond += ` AND (` + strings.Join(sliceArr, " OR ") + `)`
+			} else {
+				cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, indexCodeCol, indexNameCol)
+				pars = utils.GetLikeKeywordPars(pars, params.Keywords, 2)
+			}
+
+			// ES关键词搜索
+			//_, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, params.Keywords, utils.DATA_SOURCE_THS, utils.DATA_SUB_SOURCE_HIGH_FREQUENCY, []int{}, []int{}, []string{}, startSize, params.PageSize)
+			//if e != nil {
+			//	br.Msg = "获取失败"
+			//	br.ErrMsg = fmt.Sprintf("ES-搜索高频数据指标失败, %v", e)
+			//	return
+			//}
+			//if len(list) == 0 {
+			//	page := paging.GetPaging(params.CurrentIndex, params.PageSize, 0)
+			//	resp.Paging = page
+			//	br.Ret = 200
+			//	br.Success = true
+			//	br.Msg = "获取成功"
+			//	return
+			//}
+			//var indexIds []int
+			//for _, v := range list {
+			//	indexIds = append(indexIds, v.PrimaryId)
+			//}
+			//cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(indexIds)))
+			//pars = append(pars, indexIds)
 		}
 
 		// 排序
@@ -446,15 +464,6 @@ func (this *BaseFromThsHfController) List() {
 		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 = "获取失败"
@@ -1004,6 +1013,27 @@ func (this *BaseFromThsHfController) Remove() {
 		}
 	}
 
+	// ES标记删除
+	go func() {
+		indexItem := new(dataSourceModel.SearchDataSource)
+		indexItem.PrimaryId = item.BaseFromThsHfIndexId
+		indexItem.IndexName = item.IndexName
+		indexItem.IndexCode = item.IndexCode
+		indexItem.ClassifyId = item.BaseFromThsHfClassifyId
+		indexItem.Source = utils.DATA_SOURCE_THS
+		indexItem.SubSource = utils.DATA_SUB_SOURCE_HIGH_FREQUENCY
+		indexItem.SourceName = "同花顺高频"
+		indexItem.IsDeleted = 1
+		indexItem.CreateTime = utils.TimeTransferString(utils.FormatDateTime, item.CreateTime)
+		indexItem.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, item.ModifyTime)
+
+		docId := fmt.Sprintf("%d-%d", indexItem.Source, indexItem.PrimaryId)
+		if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+			utils.FileLog.Info("同花顺高频-标记删除es失败, %v", e)
+			return
+		}
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 77 - 15
controllers/data_manage/bloomberg_data.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data"
+	"eta/eta_api/services/elastic"
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
@@ -57,16 +58,68 @@ func (this *BloombergDataController) List() {
 		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)
+	dataResp := new(data_manage.BloombergSourceListResp)
+
 	cond := ``
 	pars := make([]interface{}, 0)
 	// 筛选项
 	{
 		params.Keywords = strings.TrimSpace(params.Keywords)
 		if params.Keywords != "" {
-			cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, data_manage.BaseFromBloombergIndexCols.IndexCode, data_manage.BaseFromBloombergIndexCols.IndexName)
-			kw := fmt.Sprint("%", params.Keywords, "%")
-			pars = append(pars, kw, kw)
+			// 空格分词搜
+			//indexCodeCol := data_manage.BaseFromBloombergIndexCols.IndexCode
+			//indexNameCol := data_manage.BaseFromBloombergIndexCols.IndexName
+			//keywordArr := strings.Split(params.Keywords, " ")
+			//if len(keywordArr) > 1 {
+			//	sliceArr := make([]string, 0)
+			//	sliceArr = append(sliceArr, fmt.Sprintf(` %s LIKE ? OR %s LIKE ? `, indexCodeCol, indexNameCol))
+			//	pars = utils.GetLikeKeywordPars(pars, params.Keywords, 2)
+			//
+			//	for _, v := range keywordArr {
+			//		if v == ` ` || v == `` {
+			//			continue
+			//		}
+			//		sliceArr = append(sliceArr, fmt.Sprintf(` %s LIKE ? OR %s LIKE ? `, indexCodeCol, indexNameCol))
+			//		pars = utils.GetLikeKeywordPars(pars, v, 2)
+			//	}
+			//	cond += ` AND (` + strings.Join(sliceArr, " OR ") + `)`
+			//} else {
+			//	cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, indexCodeCol, indexNameCol)
+			//	pars = utils.GetLikeKeywordPars(pars, params.Keywords, 2)
+			//}
+
+			// ES搜
+			_, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, params.Keywords, utils.DATA_SOURCE_BLOOMBERG, 0, []int{}, []int{}, []string{}, startSize, params.PageSize)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("ES-搜索Bloomberg指标失败, %v", e)
+				return
+			}
+			if len(list) == 0 {
+				dataResp.Paging = paging.GetPaging(params.CurrentIndex, params.PageSize, 0)
+				dataResp.List = make([]*data_manage.BaseFromBloombergIndexItem, 0)
+				br.Data = dataResp
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				return
+			}
+			var indexIds []int
+			for _, v := range list {
+				indexIds = append(indexIds, v.PrimaryId)
+			}
+			cond += fmt.Sprintf(" AND %s IN (%s)", data_manage.BaseFromBloombergIndexCols.BaseFromBloombergIndexId, utils.GetOrmInReplace(len(indexIds)))
+			pars = append(pars, indexIds)
 		}
+
 		if params.Frequency != "" {
 			cond += fmt.Sprintf(` AND %s = ?`, data_manage.BaseFromBloombergIndexCols.Frequency)
 			pars = append(pars, params.Frequency)
@@ -81,14 +134,6 @@ func (this *BloombergDataController) List() {
 		br.ErrMsg = "获取Bloomberg原始指标列表总数失败, Err: " + e.Error()
 		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)
 
 	// 排序, 默认创建时间倒序
 	orderFields := map[int]string{
@@ -143,7 +188,6 @@ func (this *BloombergDataController) List() {
 		respList = append(respList, t)
 	}
 	page := paging.GetPaging(params.CurrentIndex, params.PageSize, total)
-	dataResp := new(data_manage.BloombergSourceListResp)
 	dataResp.Paging = page
 	dataResp.List = respList
 
@@ -479,9 +523,27 @@ func (this *BloombergDataController) AddCheck() {
 	// 筛选项
 	req.Keywords = strings.TrimSpace(req.Keywords)
 	if req.Keywords != "" {
-		cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, data_manage.BaseFromBloombergIndexCols.IndexCode, data_manage.BaseFromBloombergIndexCols.IndexName)
-		kw := fmt.Sprint("%", req.Keywords, "%")
-		pars = append(pars, kw, kw)
+		// 空格分词搜
+		indexCodeCol := data_manage.BaseFromBloombergIndexCols.IndexCode
+		indexNameCol := data_manage.BaseFromBloombergIndexCols.IndexName
+		keywordArr := strings.Split(req.Keywords, " ")
+		if len(keywordArr) > 1 {
+			sliceArr := make([]string, 0)
+			sliceArr = append(sliceArr, fmt.Sprintf(` %s LIKE ? OR %s LIKE ? `, indexCodeCol, indexNameCol))
+			pars = utils.GetLikeKeywordPars(pars, req.Keywords, 2)
+
+			for _, v := range keywordArr {
+				if v == ` ` || v == `` {
+					continue
+				}
+				sliceArr = append(sliceArr, fmt.Sprintf(` %s LIKE ? OR %s LIKE ? `, indexCodeCol, indexNameCol))
+				pars = utils.GetLikeKeywordPars(pars, v, 2)
+			}
+			cond += ` AND (` + strings.Join(sliceArr, " OR ") + `)`
+		} else {
+			cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, indexCodeCol, indexNameCol)
+			pars = utils.GetLikeKeywordPars(pars, req.Keywords, 2)
+		}
 	}
 	if req.Frequency != "" {
 		cond += fmt.Sprintf(` AND %s = ?`, data_manage.BaseFromBloombergIndexCols.Frequency)

+ 43 - 17
controllers/data_manage/business_data.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/data_manage/request"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data"
+	"eta/eta_api/services/elastic"
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
@@ -90,26 +91,51 @@ func (c *EdbBusinessController) List() {
 	var condition string
 	var pars []interface{}
 
+	keywords = strings.TrimSpace(keywords)
 	if keywords != "" {
-		keywordSlice := strings.Split(keywords, " ")
-		if len(keywordSlice) > 0 {
-			tmpConditionSlice := make([]string, 0)
-			tmpConditionSlice = append(tmpConditionSlice, ` a.index_name like ? or a.index_code like ? `)
-			pars = utils.GetLikeKeywordPars(pars, keywords, 2)
-
-			for _, v := range keywordSlice {
-				if v == ` ` || v == `` {
-					continue
-				}
-				tmpConditionSlice = append(tmpConditionSlice, ` a.index_name like ? or a.index_code like ? `)
-				pars = utils.GetLikeKeywordPars(pars, v, 2)
+		//keywordSlice := strings.Split(keywords, " ")
+		//if len(keywordSlice) > 0 {
+		//	tmpConditionSlice := make([]string, 0)
+		//	tmpConditionSlice = append(tmpConditionSlice, ` a.index_name like ? or a.index_code like ? `)
+		//	pars = utils.GetLikeKeywordPars(pars, keywords, 2)
+		//
+		//	for _, v := range keywordSlice {
+		//		if v == ` ` || v == `` {
+		//			continue
+		//		}
+		//		tmpConditionSlice = append(tmpConditionSlice, ` a.index_name like ? or a.index_code like ? `)
+		//		pars = utils.GetLikeKeywordPars(pars, v, 2)
+		//	}
+		//	condition += ` AND (` + strings.Join(tmpConditionSlice, " or ") + `)`
+		//
+		//} else {
+		//	condition += ` a.index_name like ? or a.index_code like ? `
+		//	pars = utils.GetLikeKeywordPars(pars, keywords, 2)
+		//}
+
+		// ES搜
+		_, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, keywords, utils.DATA_SOURCE_BUSINESS, 0, []int{}, []int{}, []string{}, startSize, req.PageSize)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("ES-搜索自有数据指标失败, %v", e)
+			return
+		}
+		if len(list) == 0 {
+			br.Data = data_manage.BusinessIndexListResp{
+				List:   make([]*data_manage.BaseFromBusinessIndexItem, 0),
+				Paging: paging.GetPaging(req.CurrentIndex, req.PageSize, 0),
 			}
-			condition += ` AND (` + strings.Join(tmpConditionSlice, " or ") + `)`
-
-		} else {
-			condition += ` a.index_name like ? or a.index_code like ? `
-			pars = utils.GetLikeKeywordPars(pars, keywords, 2)
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			return
+		}
+		var indexIds []int
+		for _, v := range list {
+			indexIds = append(indexIds, v.PrimaryId)
 		}
+		condition += fmt.Sprintf(" AND a.base_from_business_index_id IN (%s)", utils.GetOrmInReplace(len(indexIds)))
+		pars = append(pars, indexIds)
 	}
 
 	if frequency != "" {

+ 25 - 3
controllers/data_manage/chart_framework.go

@@ -9,6 +9,7 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"strings"
 	"time"
 )
@@ -43,6 +44,9 @@ func (this *ChartFrameworkController) List() {
 		return
 	}
 
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
 	adminId, _ := this.GetInt("AdminId")
 	visibility, _ := this.GetInt("Visibility")
 	if visibility == 1 && adminId <= 0 {
@@ -68,19 +72,37 @@ func (this *ChartFrameworkController) List() {
 		pars = append(pars, visibilityArr[visibility])
 	}
 
+	var total, startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize15
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	t, e := frameworkOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取框架列表总数失败, Err: " + e.Error()
+		return
+	}
+	total = t
 	orderRule := `sort ASC, create_time DESC`
-	list, e := frameworkOb.GetItemsByCondition(cond, pars, []string{}, orderRule)
+	list, e := frameworkOb.GetPageItemsByCondition(cond, pars, []string{}, orderRule, startSize, pageSize)
 	if e != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取框架列表失败, Err: " + e.Error()
 		return
 	}
-	resp := make([]*data_manage.ChartFrameworkItem, 0)
+	resp := new(data_manage.ChartFrameworkListResp)
+	resp.List = make([]*data_manage.ChartFrameworkItem, 0)
 	for _, v := range list {
 		t := data_manage.FormatChartFramework2Item(v, make([]*data_manage.ChartFrameworkNodeItem, 0))
-		resp = append(resp, t)
+		resp.List = append(resp.List, t)
 	}
 
+	resp.Paging = paging.GetPaging(currentIndex, pageSize, total)
 	br.Data = resp
 	br.Ret = 200
 	br.Success = true

+ 13 - 5
controllers/data_manage/chart_info.go

@@ -1606,7 +1606,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 	var dateMax time.Time
 	if dateType == utils.DateTypeNYears {
 		for _, v := range mappingList {
-			if v.LatestDate != "" {
+			if v.LatestDate != "" && v.LatestDate != "0000-00-00" {
 				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
 				if tErr != nil {
 					br.Msg = "获取失败"
@@ -2227,7 +2227,7 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 		showSysId = sysUser.AdminId
 	}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -2336,7 +2336,7 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
@@ -2346,7 +2346,10 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 			if currClassify, ok := chartClassifyMap[v.ChartClassifyId]; ok {
 				tmp.HaveOperaAuth = data_manage_permission.CheckChartPermissionByPermissionIdList(v.IsJoinPermission, currClassify.IsJoinPermission, v.ChartInfoId, v.ChartClassifyId, permissionChartIdList, permissionClassifyIdList)
 			}
-
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}
@@ -4677,7 +4680,12 @@ func (this *ChartInfoController) ChartList() {
 		}
 	}
 	if keyWord != "" {
-		condition += ` AND  ( chart_name LIKE '%` + keyWord + `%' OR chart_name_en LIKE '%` + keyWord + `%' )`
+		keyWordArr := strings.Split(keyWord, " ")
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				condition += ` AND CONCAT(chart_name,chart_name_en) LIKE '%` + v + `%'`
+			}
+		}
 	}
 	if sysUserIds != "" {
 		adminIds := strings.Split(sysUserIds, ",")

+ 1653 - 0
controllers/data_manage/clarksons_data.go

@@ -0,0 +1,1653 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/request"
+	"eta/eta_api/models/data_manage/response"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/tealeg/xlsx"
+)
+
+type ClarksonsDataController struct {
+	controllers.BaseAuthController
+}
+
+// @Title 克拉克森数据分类
+// @Description 克拉克森数据分类接口
+// @Success 200 {object} data_manage.SciClassify
+// @router /clarksons/classify [get]
+func (this *ClarksonsDataController) Classify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	classifyList, err := data_manage.GetClarksonsClassifyAll()
+	if err != nil {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败, Err:" + err.Error()
+		return
+	}
+
+	initClassify := &data_manage.BaseFromClarksonsClassifyItem{
+		BaseFromClassifyId: 0,
+		ClassifyName:       "未分类",
+		ClassifyNameEn:     "Unclassified",
+		UniqueCode:         "0",
+		ParentId:           0,
+		Level:              1,
+		Sort:               0,
+		Children:           nil,
+	}
+	finalList := make([]*data_manage.BaseFromClarksonsClassifyItem, 0)
+	classifyTree := getClarksonsClassifyTree(classifyList, 0)
+	finalList = append(finalList, initClassify)
+	finalList = append(finalList, classifyTree...)
+
+	br.Msg = "查询成功"
+	br.Data = finalList
+	br.Success = true
+	br.Ret = 200
+}
+
+// AddSciClassify
+// @Title 新增分类
+// @Description 新增分类接口
+// @Param	request	body data_manage.AddBaseFromSciClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /clarksons/classify/add [post]
+func (this *ClarksonsDataController) AddClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req request.AddBaseFromClarksonsClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "操作异常"
+		return
+	}
+	ok, msg, err := data.AddClarksonsClassify(req.ClassifyName, req.ParentId)
+	if err != nil {
+		br.Msg = "添加失败"
+		br.ErrMsg = "添加失败,Err:" + err.Error()
+		return
+	}
+	if !ok {
+		if msg != "" {
+			br.Msg = msg
+			return
+		}
+		br.Msg = "添加失败"
+		return
+	}
+	br.Msg = "添加成功"
+	br.Success = true
+	br.Ret = 200
+}
+
+// DelClassify
+// @Title 新增分类
+// @Description 新增分类接口
+// @Param	request	body data_manage.AddBaseFromSciClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /clarksons/classify/del [post]
+func (this *ClarksonsDataController) DelClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req request.DelBaseFromClarksonsClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.BaseFromClassifyId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	err = data.DelClarksonsClassify(req.BaseFromClassifyId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// EditClassify
+// @Title 修改分类
+// @Description 修改分类接口
+// @Param	request	body data_manage.EditBaseFromMysteelChemicalClassifyReq true "type json string"
+// @Success 200 Ret=200 修改成功
+// @router /clarksons/classify/edit [post]
+func (this *ClarksonsDataController) EditClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req request.EditBaseFromClarksonsClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BaseFromClassifyId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+	classify, err := data_manage.GetClarksonsClassifyById(req.BaseFromClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在"
+			return
+		}
+		br.Msg = "编辑失败"
+		br.ErrMsg = "获取分类失败,Err:" + err.Error()
+		return
+	}
+
+	if classify.ClassifyName != req.ClassifyName {
+		count, err := data_manage.GetClarksonsClassifyCountByName(req.ClassifyName)
+		if err != nil {
+			br.Msg = "编辑失败"
+			br.ErrMsg = "获取分类失败,Err:" + err.Error()
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在"
+			return
+		}
+		classify.ClassifyName = req.ClassifyName
+		classify.ModifyTime = time.Now()
+		err = classify.Update([]string{"classify_name", "modify_time"})
+		if err != nil {
+			br.Msg = "编辑失败"
+			br.ErrMsg = "编辑失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	br.Msg = "编辑成功"
+	br.Success = true
+	br.Ret = 200
+	br.IsAddLog = true
+}
+
+// MoveClassify
+// @Title 分类移动接口
+// @Description 分类移动接口
+// @Success 200 {object} data_manage.MoveBaseFromMysteelChemicalClassifyReq
+// @router /clarksons/classify/move [post]
+func (this *ClarksonsDataController) MoveClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req request.MoveBaseFromClarksonsClassifyReq
+	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BaseFromClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id小于等于0"
+		br.IsSendEmail = false
+		return
+	}
+
+	err, errMsg := data.MoveClarksonsClassify(req.BaseFromClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId)
+	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.IsAddLog = true
+	br.Msg = "移动成功"
+}
+
+// IndexList
+// @Title 克拉克森指标列表
+// @Description 克拉克森数据指标列表接口
+// @Param   BaseFromClassifyId   query   int  true       "分类id"
+// @Success 200 {object} data_manage.BaseFromMysteelChemicalIndexResp
+// @router /clarksons/index/list [get]
+func (this *ClarksonsDataController) IndexList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	classifyId, _ := this.GetInt("BaseFromClassifyId", 0)
+	indexList, err := data_manage.GetClarksonsIndexBaseInfoByClassifyId(classifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = indexList
+}
+
+// GetClarksonsIndexInfo
+// @Title 添加指标-根据条件获取指标信息
+// @Description 添加指标-根据条件获取指标信息
+// @Param   KeyWord   query   string  false       "关键字"
+// @Param   ClassifyIds   query   string  false       "分类id"
+// @Param   Frequencies   query   string  false       "频率"
+// @Param   PageSize   query   int  false       "每页数据条数"
+// @Param   CurrentIndex   query   int  false       "当前页页码,从1开始"
+// @Success 200 {object} data_manage.BaseFromRzdIndexPage
+// @router /clarksons/get/index/info [post]
+func (this *ClarksonsDataController) GetClarksonsIndexInfo() {
+	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 request.ClarksonsDataBatchListReq
+	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	keyWord := req.KeyWord
+	classifyIds := req.ClassifyIds
+	frequencies := req.Frequencies
+
+	pageSize := req.PageSize
+	currentIndex := req.CurrentIndex
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var classifyIdList []string
+	var frequencyList []string
+	if classifyIds != "" {
+		classifyIdList = strings.Split(classifyIds, ",")
+	}
+	if frequencies != "" {
+		frequencyList = strings.Split(frequencies, ",")
+	}
+	indexInfoPage, err := data.GetClarksonsIndexInfo(keyWord, classifyIdList, frequencyList, currentIndex, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = indexInfoPage
+}
+
+// SearchList
+// @Title 克拉克森模糊搜索
+// @Description 克拉克森模糊搜索
+// @Param   Keyword   query   string  true       "关键字搜索"
+// @Success 200 {object} models.BaseResponse
+// @router /clarksons/search_list [get]
+func (this *ClarksonsDataController) SearchList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	//关键字
+	keyword := this.GetString("Keyword")
+	var condition string
+	var pars []interface{}
+	if keyword != "" {
+		condition += ` AND (index_code LIKE ? OR index_name LIKE ?)`
+		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+	} else {
+		br.Msg = "请输入指标ID/指标名称"
+		return
+	}
+
+	list, err := data_manage.GetClarksonsIndexBaseInfoByCondition(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// SingleData
+// @Title 获取克拉克森据
+// @Description 获取克拉克森单条数据接口
+// @Param   BaseFromClarksonsIndexId   query   string  true       "指标唯一编码"
+// @Success 200 {object} models.BaseResponse
+// @router /clarksons/single_data [get]
+func (this *ClarksonsDataController) SingleData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	baseFromClarksonsIndexId, _ := this.GetInt("BaseFromClarksonsIndexId")
+	indexInfo, err := data_manage.GetClarksonsIndexByIndexId(baseFromClarksonsIndexId)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+	edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_CLARKSONS, indexInfo.IndexCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取指标数据失败"
+		br.ErrMsg = "获取指标库数据失败,Err:" + err.Error()
+		return
+	}
+	var edbInfoId int
+	if edbInfo != nil {
+		edbInfoId = edbInfo.EdbInfoId
+	}
+	dataList, err := data_manage.GetClarksonsDataByIndexId(baseFromClarksonsIndexId)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	var ret response.ClarksonsSingleDataResp
+	ret.ClassifyId = indexInfo.ClassifyId
+	ret.BaseFromClarksonsIndexId = indexInfo.BaseFromClarksonsIndexId
+	ret.IndexCode = indexInfo.IndexCode
+	ret.EdbInfoId = edbInfoId
+	ret.IndexName = indexInfo.IndexName
+	ret.Frequency = indexInfo.Frequency
+	ret.CreateTime = indexInfo.CreateTime.Format(utils.FormatDateTime)
+	ret.ModifyTime = indexInfo.ModifyTime.Format(utils.FormatDateTime)
+	ret.Unit = indexInfo.Unit
+	ret.Data = dataList
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// MoveClarksonsData
+// @Title 克拉克森指标移动接口
+// @Description 克拉克森指标移动接口
+// @Success 200 {object} request.MoveBaseFromClarksonsReq
+// @router /clarksons/move [post]
+func (this *ClarksonsDataController) MoveClarksonsData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.MoveBaseFromClarksonsReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.BaseFromClarksonsIndexId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "指标id小于等于0"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.BaseFromClassifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		br.IsSendEmail = false
+		return
+	}
+
+	err, errMsg := data.MoveClarksonsData(req.BaseFromClarksonsIndexId, req.BaseFromClassifyId, req.PrevBaseFromClarksonsIndexId, req.NextBaseFromClarksonsIndexId)
+	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.IsAddLog = true
+	br.Msg = "移动成功"
+}
+
+// ResetClarksonsIndex
+// @Title 指标数据清除分类
+// @Description 指标数据清除分类
+// @Param	request	body data_manage.DelBaseFromSciReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /clarksons/reset [post]
+//func (this *ClarksonsDataController) ResetClarksonsIndex() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//	var req request.ResetBaseFromClarksonsReq
+//	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+//	if err != nil {
+//		br.Msg = "参数解析异常!"
+//		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	if req.BaseFromClarksonsIndexId < 0 {
+//		br.Msg = "参数错误"
+//		br.IsSendEmail = false
+//		return
+//	}
+//
+//	err = data.ResetClarksonsIndex(req.BaseFromClarksonsIndexId)
+//	if err != nil {
+//		br.Msg = "移动失败"
+//		br.ErrMsg = "移动失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	br.Ret = 200
+//	br.Msg = "操作成功"
+//	br.Success = true
+//	br.IsAddLog = true
+//}
+
+// AddEdbInfo
+// @Title 新增指标接口
+// @Description 新增指标接口
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /clarksons/edb_info/add [post]
+//func (this *ClarksonsDataController) AddEdbInfo() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//
+//	sysUser := this.SysUser
+//	if sysUser == nil {
+//		br.Msg = "请登录"
+//		br.ErrMsg = "请登录,SysUser Is Empty"
+//		br.Ret = 408
+//		return
+//	}
+//	deleteCache := true
+//	cacheKey := "CACHE_EDB_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+//	defer func() {
+//		if deleteCache {
+//			utils.Rc.Delete(cacheKey)
+//		}
+//	}()
+//	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+//		deleteCache = false
+//		br.Msg = "系统处理中,请稍后重试!"
+//		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+//		return
+//	}
+//	var req data_manage.AddEdbInfoReq
+//	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+//	if err != nil {
+//		br.Msg = "参数解析异常!"
+//		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	req.EdbName = strings.Trim(req.EdbName, " ")
+//	req.EdbCode = strings.Trim(req.EdbCode, " ")
+//
+//	if req.EdbCode == "" {
+//		br.Msg = "指标ID不能为空"
+//		return
+//	}
+//
+//	if req.EdbName == "" {
+//		br.Msg = "指标名称不能为空"
+//		return
+//	}
+//
+//	if req.Frequency == "" {
+//		br.Msg = "频率不能为空"
+//		return
+//	}
+//
+//	if req.Unit == "" {
+//		br.Msg = "单位不能为空"
+//		return
+//	}
+//
+//	if req.ClassifyId <= 0 {
+//		br.Msg = "请选择分类"
+//		return
+//	}
+//
+//	_, err = data_manage.GetClarksonsIndexByIndexCode(req.EdbCode)
+//	if err != nil {
+//		if err.Error() == utils.ErrNoRow() {
+//			br.Msg = "指标不存在"
+//			return
+//		}
+//		br.Msg = "获取失败"
+//		br.ErrMsg = "获取失败,Err:" + err.Error()
+//		return
+//	}
+//	source := utils.DATA_SOURCE_CLARKSONS
+//	// 指标入库
+//	edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(source, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, req.StartDate, req.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+//	if err != nil {
+//		br.Msg = "保存失败"
+//		if errMsg != `` {
+//			br.Msg = errMsg
+//		}
+//		br.ErrMsg = err.Error()
+//		br.IsSendEmail = isSendEmail
+//		return
+//	}
+//
+//	// 试用平台更新用户累计新增指标数
+//	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+//	if e != nil {
+//		br.Msg = "操作失败"
+//		br.ErrMsg = "获取系统用户数据失败,Err:" + e.Error()
+//		return
+//	}
+//	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+//		go func() {
+//			var r etaTrialService.EtaTrialUserReq
+//			r.Mobile = adminItem.Mobile
+//			_, _ = etaTrialService.UpdateUserIndexNum(r)
+//		}()
+//	}
+//
+//	//新增操作日志
+//	{
+//		// 添加钢联指标更新日志
+//		if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+//			go data_stat.AddEdbInfoUpdateLog(edbInfo.EdbInfoId, 1, "", sysUser, 2)
+//		}
+//
+//		edbLog := new(data_manage.EdbInfoLog)
+//		edbLog.EdbInfoId = edbInfo.EdbInfoId
+//		edbLog.SourceName = edbInfo.SourceName
+//		edbLog.Source = edbInfo.Source
+//		edbLog.EdbCode = edbInfo.EdbCode
+//		edbLog.EdbName = edbInfo.EdbName
+//		edbLog.ClassifyId = edbInfo.ClassifyId
+//		edbLog.SysUserId = sysUser.AdminId
+//		edbLog.SysUserRealName = sysUser.RealName
+//		edbLog.CreateTime = time.Now()
+//		edbLog.Content = string(this.Ctx.Input.RequestBody)
+//		edbLog.Status = "新增指标"
+//		edbLog.Method = this.Ctx.Input.URI()
+//		go data_manage.AddEdbInfoLog(edbLog)
+//	}
+//
+//	// 更新es
+//	go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+//
+//	resp := new(data_manage.AddEdbInfoResp)
+//	resp.EdbInfoId = edbInfo.EdbInfoId
+//	resp.UniqueCode = edbInfo.UniqueCode
+//	br.Ret = 200
+//	br.Success = true
+//	br.Msg = "保存成功"
+//	br.Data = resp
+//	br.IsAddLog = true
+//}
+
+// AddCheck
+// @Title 新增校验
+// @Description 新增校验
+// @Param	request	body request.BusinessDataBatchAddCheckReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /clarksons/edb_info/add_check [post]
+func (this *ClarksonsDataController) AddCheck() {
+	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 *request.ClarksonsDataBatchAddCheckReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	codeMax := 30
+	codeLen := len(req.IndexCodes)
+	if len(req.IndexCodes) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	if codeLen > codeMax {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMax)
+		return
+	}
+	// 获取指标库已有指标
+	existsEdb, e := data_manage.GetEdbCodesBySource(utils.DATA_SOURCE_CLARKSONS)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取自有数据已添加的指标失败, Err: " + e.Error()
+		return
+	}
+	existMap := make(map[string]*data_manage.EdbInfo)
+	for _, v := range existsEdb {
+		existMap[v.EdbCode] = v
+	}
+
+	// 查询选中的指标
+	cond := fmt.Sprintf(` AND index_code IN (%s)`, utils.GetOrmInReplace(codeLen))
+	pars := make([]interface{}, 0)
+	pars = append(pars, req.IndexCodes)
+	list, err := data_manage.GetClarksonsIndexAndEdbInfoByCondition(cond, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取克拉克森原始指标列表失败, Err: " + err.Error()
+		return
+	}
+
+	resp := make([]*data_manage.BaseFromClarksonsIndexView, 0)
+	for _, v := range list {
+		if v.EdbInfoId > 0 {
+			v.EdbExist = 1
+		}
+		resp = append(resp, v)
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// NameCheck
+// @Title 重名校验
+// @Description 批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /clarksons/edb_info/name_check [post]
+func (c *ClarksonsDataController) NameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req []*data_manage.NameCheckEdbInfoReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	codeMax := 30
+	codeLen := len(req)
+	if codeLen > codeMax {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMax)
+		return
+	}
+
+	type NameCheckResult struct {
+		EdbCode string
+		EdbName string
+		Exist   bool
+	}
+	indexNames := make([]string, 0)
+	resp := make([]*NameCheckResult, 0)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &NameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+		dataItems, err := data_manage.GetEdbDataAllByEdbCode(v.EdbCode, utils.DATA_SOURCE_CLARKSONS, 0, utils.EDB_DATA_LIMIT)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取克拉克森已存在信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataItems) <= 0 {
+			respItem, err := data.AddEdbData(utils.DATA_SOURCE_CLARKSONS, v.EdbCode, v.Frequency)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				return
+			}
+			if respItem.Ret != 200 {
+				br.Msg = "未搜索到该指标"
+				br.ErrMsg = respItem.ErrMsg + ";EdbCode:" + v.EdbCode
+				return
+			}
+		}
+	}
+
+	// 重名校验
+	edbList, e := data_manage.GetEdbInfoByNameArr(indexNames, utils.EDB_INFO_TYPE)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名指标失败, Err: " + e.Error()
+		return
+	}
+	nameExists := make(map[string]bool)
+	for _, v := range edbList {
+		nameExists[v.EdbName] = true
+	}
+	if len(nameExists) > 0 {
+		for _, v := range resp {
+			v.Exist = nameExists[v.EdbName]
+		}
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// BatchAdd
+// @Title 批量新增
+// @Description 批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /clarksons/edb_info/batch_add [post]
+func (this *ClarksonsDataController) BatchAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_CLARKSONS_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		v.Frequency = strings.TrimSpace(v.Frequency)
+		if v.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		v.Unit = strings.TrimSpace(v.Unit)
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+	}
+
+	// 限定同一时间最多批量新增30个指标
+	for _, v := range req {
+		var r data.ClarksonsIndexSource2EdbReq
+		r.EdbCode = v.EdbCode
+		r.EdbName = v.EdbName
+		r.Frequency = v.Frequency
+		r.Unit = v.Unit
+		r.ClassifyId = v.ClassifyId
+		r.AdminId = sysUser.AdminId
+		r.AdminRealName = sysUser.RealName
+
+		edbInfo, errMsg, skip, e := data.ClarksonsIndexSource2Edb(r, this.Lang)
+		if e != nil {
+			br.Msg = "操作失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = e.Error()
+			return
+		}
+		if skip {
+			continue
+		}
+
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(this.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = this.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+
+		// 更新es
+		go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// BatchDelete
+// @Title 批量删除
+// @Description 批量删除
+// @Param	request	body []*request.DelBaseFromClarksonsReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /clarksons/edb_info/batch_delete [post]
+func (this *ClarksonsDataController) BatchDelete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req []*request.DelBaseFromClarksonsReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	var deleteIds []int
+	for _, v := range req {
+		if v.BaseFromClarksonsIndexId <= 0 {
+			continue
+		}
+		deleteIds = append(deleteIds, v.BaseFromClarksonsIndexId)
+	}
+	existList, err := data.BatchDelClarksonsData(deleteIds)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Data = existList
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// BatchEdit
+// @Title 批量编辑
+// @Description 批量编辑
+// @Param	request	body []*request.DelBaseFromClarksonsReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /clarksons/edb_info/batch_edit [post]
+func (this *ClarksonsDataController) BatchEdit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req []*request.EditBaseFromClarksonsReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	indexIds := make([]int, 0)
+	classifyIds := make([]int, 0)
+	indexIdToclassifyId := make(map[int]int)
+	for _, v := range req {
+		if v.BaseFromClarksonsIndexId <= 0 {
+			continue
+		}
+		if v.BaseFromClassifyId <= 0 {
+			continue
+		}
+		indexIds = append(indexIds, v.BaseFromClarksonsIndexId)
+		classifyIds = append(classifyIds, v.BaseFromClassifyId)
+		indexIdToclassifyId[v.BaseFromClarksonsIndexId] = v.BaseFromClassifyId
+	}
+	if len(indexIds) == 0 {
+		br.Msg = "请选择指标或指定正确的分类"
+		return
+	}
+	indexList, err := data_manage.GetClarksonsIndexListByIndexIds(indexIds)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "获取指标失败,Err:" + err.Error()
+		return
+	}
+	classifySort, err := data_manage.GetClarksonsClassifyMaxSortByClassifyIds(classifyIds)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "获取分类最大排序失败,Err:" + err.Error()
+		return
+	}
+	classifySortMap := make(map[int]int)
+	for _, v := range classifySort {
+		classifySortMap[v.BaseFromClassifyId] = v.MaxSort
+	}
+	classifyList, err := data_manage.GetClarksonsClassifyListByIds(classifyIds)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "获取分类失败,Err:" + err.Error()
+		return
+	}
+	existClassifyList := make(map[int]struct{})
+	for _, v := range classifyList {
+		existClassifyList[v.BaseFromClassifyId] = struct{}{}
+	}
+	//编辑指标
+	updateIndexList := make([]*data_manage.BaseFromClarksonsIndex, 0)
+	for _, v := range indexList {
+		tmpClassifyId := indexIdToclassifyId[v.BaseFromClarksonsIndexId]
+		if _, ok := existClassifyList[tmpClassifyId]; ok {
+			v.ClassifyId = tmpClassifyId
+			v.Sort = classifySortMap[tmpClassifyId] + 1
+			classifySortMap[tmpClassifyId] += 1
+			updateIndexList = append(updateIndexList, v)
+		}
+	}
+	err = data_manage.BatchModifyClarksonsIndexClassify(updateIndexList)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑指标失败,Err:" + err.Error()
+		return
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+
+}
+
+// EditClarksons
+// @Title 编辑克拉克森指标
+// @Description 编辑克拉克森指标接口
+// @Param	request	body data_manage.AddEdbClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /clarksons/edit [post]
+func (this *ClarksonsDataController) EditClarksons() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req request.EditBaseFromClarksonsReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BaseFromClarksonsIndexId <= 0 {
+		br.Msg = "请选择指标"
+		br.IsSendEmail = false
+		return
+	}
+	if req.BaseFromClassifyId < 0 {
+		br.Msg = "请选择分类"
+		br.IsSendEmail = false
+		return
+	}
+
+	//编辑指标
+	sciIndexInfo, errMsg, err := data.EditClarksonsIndex(req.BaseFromClarksonsIndexId, req.BaseFromClassifyId)
+	if errMsg != `` {
+		br.Msg = errMsg
+		return
+	}
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑指标失败,Err:" + err.Error()
+		return
+	}
+
+	resp := response.EditClarksonsIndexInfoResp{
+		BaseFromClarksonsIndexId: sciIndexInfo.BaseFromClarksonsIndexId,
+		IndexCode:                sciIndexInfo.IndexCode,
+	}
+	br.Data = resp
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// DeleteClarksonsData
+// @Title 删除指标
+// @Description 删除指标接口
+// @Param	request	body data_manage.DelBaseFromSciReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /clarksons/del [post]
+func (this *ClarksonsDataController) DeleteClarksonsData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req request.DelBaseFromClarksonsReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.BaseFromClarksonsIndexId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	err, errMsg := data.DelClarksonsData(req.BaseFromClarksonsIndexId)
+	if errMsg != `` {
+		br.Msg = errMsg
+		br.ErrMsg = errMsg
+		if err != nil {
+			br.ErrMsg = errMsg + ";Err:" + err.Error()
+		} else {
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// ExportClarksonsList
+// @Title 导出Sci数据
+// @Description 导出Sci数据
+// @Param   BaseFromClassifyId   query   int  true       "关键字搜索"
+// @Param   IndexCode   query   string  true       "指标编码"
+// @Success 200  导出成功
+// @router /export/clarksonsList [get]
+func (this *ClarksonsDataController) ExportClarksonsList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	classifyId, _ := this.GetInt("BaseFromClassifyId")
+	indexCode := this.GetString("IndexCode")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	secNameList := make([]*data_manage.BaseFromClarksonsIndex, 0)
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downLoadnFilePath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	var condition string
+	var pars []interface{}
+	if classifyId > 0 {
+		childClassify, err := data_manage.GetChildClarksonsClassifyListById(classifyId)
+		if err != nil {
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取分类失败,Err:" + err.Error()
+			return
+		}
+		if len(childClassify) > 0 {
+			condition += `AND classify_id IN (` + utils.GetOrmInReplace(len(childClassify)) + `)`
+			for _, child := range childClassify {
+				pars = append(pars, child.BaseFromClassifyId)
+			}
+		} else {
+			condition += ` AND classify_id=?`
+			pars = append(pars, classifyId)
+		}
+	} else {
+		condition += ` AND classify_id=?`
+		pars = append(pars, classifyId)
+	}
+	if indexCode != "" {
+		condition += ` AND index_code=? `
+		pars = append(pars, indexCode)
+	}
+	frequencies, err := data_manage.GetClarksonsFrequencyByCondition(condition, pars)
+	if err != nil {
+		fmt.Println("GetSciFrequency err:", err.Error())
+		utils.FileLog.Info("GetSciFrequency err:" + err.Error())
+		return
+	}
+	for _, frequency := range frequencies {
+		//获取指标
+		secNameList, err = data_manage.GetClarksonsIndexByConditionAndFrequency(condition, *frequency, pars)
+		if err != nil {
+			fmt.Println("获取数据失败,Err:" + err.Error())
+			return
+		}
+		if len(secNameList) <= 0 {
+			fmt.Println("secNameList长度为0")
+			return
+		}
+		sheetNew, err := xlsxFile.AddSheet(*frequency)
+		if err != nil {
+			fmt.Println("新增Sheet失败", err.Error())
+			return
+		}
+		secNameRow := sheetNew.AddRow()
+		frequencyRow := sheetNew.AddRow()
+		unitRow := sheetNew.AddRow()
+		lastModifyDateRow := sheetNew.AddRow()
+
+		var indexIdList []int
+		for _, sv := range secNameList {
+			indexIdList = append(indexIdList, sv.BaseFromClarksonsIndexId)
+		}
+		dataTimeList, err := data_manage.GetClarksonsDataDataTimeByIndexId(indexIdList)
+		if err != nil {
+			fmt.Println("获取数据时间失败", err.Error())
+			return
+		}
+
+		// 添加excel左侧指标日期
+		setRowIndex := 4
+		for rk, dv := range dataTimeList {
+			rowIndex := setRowIndex + rk
+			row := sheetNew.Row(rowIndex)
+			displayDate, _ := time.Parse(utils.FormatDate, dv)
+			displayDateCell := row.AddCell()
+			style := new(xlsx.Style)
+			style.ApplyAlignment = true
+			style.Alignment.WrapText = true
+			displayDateCell.SetStyle(style)
+			displayDateCell.SetDate(displayDate)
+
+		}
+		for k, sv := range secNameList {
+			//获取数据
+			dataList, err := data_manage.GetClarksonsIndexDataByCode(sv.IndexCode)
+			if err != nil {
+				br.Msg = "获取数据失败"
+				br.ErrMsg = "获取数据失败,Err:" + err.Error()
+				return
+			}
+			if k == 0 {
+				secNameRow.AddCell().SetValue("指标名称")
+				frequencyRow.AddCell().SetValue("频率")
+				unitRow.AddCell().SetValue("单位")
+				lastModifyDateRow.AddCell().SetValue("更新时间")
+				min := k * 3
+				sheetNew.SetColWidth(min, min, 15)
+			}
+			if len(dataList) == 0 {
+				continue
+			}
+			secNameRow.AddCell().SetValue(sv.IndexName)
+			frequencyRow.AddCell().SetValue(sv.Frequency)
+			unitRow.AddCell().SetValue(sv.Unit)
+
+			lastModifyDateRow.AddCell().SetValue(sv.ModifyTime.Format(utils.FormatDate))
+			dataInfoMap := make(map[string]*data_manage.BaseFromClarksonsData)
+			for _, v := range dataList {
+				dataInfoMap[v.DataTime] = v
+			}
+
+			for rk, dtv := range dataTimeList {
+				rowIndex := setRowIndex + rk
+				row := sheetNew.Row(rowIndex)
+				displayDateCell := row.AddCell()
+				tmpData, ok := dataInfoMap[dtv]
+				if ok {
+					displayDateCell.SetValue(tmpData.Value)
+				}
+			}
+		}
+	}
+
+	err = xlsxFile.Save(downLoadnFilePath)
+	if err != nil {
+		//有指标无数据时先导出一遍空表
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		err = xlsxFile.Save(downLoadnFilePath)
+		if err != nil {
+			br.Msg = "保存文件失败"
+			br.ErrMsg = "保存文件失败"
+			return
+		}
+	}
+	fileName := `克拉克森`
+	if indexCode != "" && len(secNameList) == 1 {
+		fileName = secNameList[0].IndexName
+	}
+	fileName += time.Now().Format("2006.01.02") + `.xlsx` //文件名称
+	this.Ctx.Output.Download(downLoadnFilePath, fileName)
+	defer func() {
+		os.Remove(downLoadnFilePath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+}
+
+// getClarksonsClassifyTree 返回卓创红旗的树形结构
+func getClarksonsClassifyTree(items []*data_manage.BaseFromClarksonsClassifyItem, parentId int) []*data_manage.BaseFromClarksonsClassifyItem {
+	res := make([]*data_manage.BaseFromClarksonsClassifyItem, 0)
+	for _, item := range items {
+		if item.ParentId == parentId {
+			t := new(data_manage.BaseFromClarksonsClassifyItem)
+			t.BaseFromClassifyId = item.BaseFromClassifyId
+			t.ClassifyName = item.ClassifyName
+			t.ParentId = item.ParentId
+			t.Level = item.Level
+			t.Sort = item.Sort
+			t.ModifyTime = item.ModifyTime
+			t.CreateTime = item.CreateTime
+			t.ClassifyNameEn = item.ClassifyNameEn
+			if item.UniqueCode == "" {
+				t.UniqueCode = strconv.Itoa(item.BaseFromClassifyId)
+			}
+			t.Children = getClarksonsClassifyTree(items, item.BaseFromClassifyId)
+			res = append(res, t)
+		}
+	}
+	return res
+}
+
+// @Title 获取克拉克森数据
+// @Description 获取克拉克森数据接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   BaseFromSci99ClassifyId   query   int  true       "分类id"
+// @Param   KeyWord   query   string  true       "关键词"
+// @Success 200 {object} data_source.BaseFromSci99IndexView
+// @router /clarksons/index/data [get]
+func (this *ClarksonsDataController) ClarksonsData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	baseFromClarksonsClassifyId, _ := this.GetInt("BaseFromClarksonsClassifyId")
+	if baseFromClarksonsClassifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+
+	keyword := this.GetString("KeyWord")
+
+	//获取指标
+	var condition string
+	var pars []interface{}
+
+	if baseFromClarksonsClassifyId > 0 {
+		condition += ` AND classify_id=? `
+		pars = append(pars, baseFromClarksonsClassifyId)
+	}
+
+	if keyword != "" {
+		condition += ` AND (index_code =? OR index_name LIKE ?)  `
+		pars = append(pars, keyword)
+		pars = append(pars, "%"+keyword+"%")
+	}
+
+	sci99List, err := data_manage.GetClarksonsIndex(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	resultList := make([]*data_manage.BaseFromClarksonsIndexList, 0)
+	for _, v := range sci99List {
+		product := new(data_manage.BaseFromClarksonsIndexList)
+		product.BaseFromClarksonsIndexId = v.BaseFromClarksonsIndexId
+		product.ClassifyId = v.ClassifyId
+		product.IndexCode = v.IndexCode
+		product.IndexName = v.IndexName
+		product.Frequency = v.Frequency
+		product.Unit = v.Unit
+		product.ModifyTime = v.ModifyTime
+
+		modifyTime, err := data_manage.GetClarksonsIndexLatestDate(v.IndexCode)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取更新时间失败"
+			br.ErrMsg = "获取更新时间失败,Err:" + err.Error()
+			return
+		}
+		product.ModifyTime = modifyTime
+
+		total, err := data_manage.GetClarksonsIndexDataCount(v.IndexCode)
+		page := paging.GetPaging(currentIndex, pageSize, total)
+		dataList, err := data_manage.GetClarksonsIndexData(v.IndexCode, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+		if dataList == nil {
+			dataList = make([]*data_manage.BaseFromClarksonsData, 0)
+		}
+		product.DataList = dataList
+		product.Paging = page
+		resultList = append(resultList, product)
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resultList
+}
+
+// IndexPageList
+// @Title 克拉克森指标列表
+// @Description 克拉克森数据指标列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   int  true       "分类id"
+// @Param   IsListAll   query   int  true       "是否展示全部"
+// @Success 200 {object} data_manage.BaseFromMysteelChemicalIndexResp
+// @router /clarksons/index/page/list [get]
+func (this *ClarksonsDataController) IndexPageList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+	pageSize, _ := this.GetInt("PageSize")
+	currrentIndex, _ := this.GetInt("CurrentIndex")
+	isListAll, _ := this.GetBool("IsListAll")
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currrentIndex <= 0 {
+		currrentIndex = 1
+	}
+	startSize = utils.StartIndex(currrentIndex, pageSize)
+	var total int
+	var indexList []*data_manage.BaseFromClarksonsIndexView
+	if isListAll {
+		tmpTotal, err := data_manage.GetClarksonsIndexCount()
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		total = tmpTotal
+		tmpIndexList, err := data_manage.GetClarksonsIndexByPage(startSize, pageSize)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		indexList = tmpIndexList
+	} else {
+		var classifyIds []int
+		if classifyId > 0 {
+			tmpClassifyIds, err := data_manage.GetClarksonsChildClassifyIdsById(classifyId)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+				return
+			}
+			if len(tmpClassifyIds) > 0 {
+				classifyIds = append(classifyIds, tmpClassifyIds...)
+			}
+		}
+		classifyIds = append(classifyIds, classifyId)
+		tmpTotal, err := data_manage.GetClarksonsIndexCountByClassifyId(classifyIds)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		total = tmpTotal
+		tmpIndexList, err := data_manage.GetClarksonsIndexByClassifyId(classifyIds, startSize, pageSize)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		indexList = tmpIndexList
+	}
+
+	page := paging.GetPaging(currrentIndex, pageSize, total)
+	resp := new(response.ClarksonsIndexPageListResp)
+	resp.List = indexList
+	resp.Paging = page
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 6 - 2
controllers/data_manage/correlation/correlation_chart_info.go

@@ -1944,7 +1944,7 @@ func (this *CorrelationChartInfoController) SearchByEs() {
 		sourceList = append(sourceList, source)
 	}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1997,13 +1997,17 @@ func (this *CorrelationChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 8 - 3
controllers/data_manage/cross_variety/chart_info.go

@@ -105,7 +105,8 @@ func (c *ChartInfoController) List() {
 			for _, v := range keyWordArr {
 				if v != "" {
 					condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
-				}			}
+				}
+			}
 		}
 		condition = strings.TrimRight(condition, "OR")
 		condition += " ) "
@@ -1450,7 +1451,7 @@ func (c *ChartInfoController) SearchByEs() {
 
 	sourceList := []int{utils.CHART_SOURCE_CROSS_HEDGING}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1503,13 +1504,17 @@ func (c *ChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if _, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, []*data_manage.ChartEdbInfoMapping{}, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 161 - 3
controllers/data_manage/edb_classify.go

@@ -9,7 +9,10 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"sort"
+	"strconv"
+	"strings"
 )
 
 // EdbClassifyController 数据管理-分类模块
@@ -288,7 +291,7 @@ func (this *EdbClassifyController) EditEdbClassify() {
 		return
 	}
 
-	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ClassifyName, this.Lang, this.SysUser)
+	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ParentId, req.ClassifyName, this.Lang, this.SysUser)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -1109,7 +1112,7 @@ func (this *EdbClassifyController) ClassifyTree() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-
+	level, _ := this.GetInt(`Level`)
 	allList, err := data_manage.GetNormalEdbClassifyAll()
 	if err != nil && err.Error() != utils.ErrNoRow() {
 		br.Msg = "获取失败"
@@ -1135,7 +1138,7 @@ func (this *EdbClassifyController) ClassifyTree() {
 			button := data.GetEdbClassifyOpButton(this.SysUser, v.SysUserId, v.HaveOperaAuth)
 			allList[k].Button = button
 		}
-		nodeAll = data.GetClassifyTreeRecursive(allList, 0)
+		nodeAll = data.GetClassifyTreeRecursive(allList, 0, level)
 		//根据sort值排序
 		sortList = nodeAll
 		sort.Sort(sortList)
@@ -1221,3 +1224,158 @@ func (this *EdbClassifyController) ClassifyTree() {
 //	br.Success = true
 //	br.Msg = "移动成功"
 //}
+
+// EdbInfoList
+// @Title 批量编辑指标列表接口
+// @Description 批量编辑指标列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   EdbInfoId   query   int  true       "指标id"
+// @Param   KeyWord   query   string  false       "搜索关键词:指标ID/指标名称"
+// @Success 200 {object} data_manage.EdbInfoListResp
+// @router /classify/edb/list [get]
+func (this *EdbInfoController) ClassifyEdbInfoList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	subClassify, _ := this.GetBool("SubClassify")
+	sources := this.GetString("Sources")
+	keyWord := this.GetString("KeyWord")
+	sysUserIds := this.GetString("SysUserIds")
+	classifyIdsStr := this.GetString("ClassifyIds")
+	classifyIds := strings.Split(classifyIdsStr, ",")
+
+	var condition string
+	var pars []interface{}
+
+	// 已授权分类id
+	//permissionClassifyIdList, err := data_manage_permission.GetUserEdbClassifyPermissionList(this.SysUser.AdminId, 0)
+	//if err != nil {
+	//	br.Msg = "获取失败"
+	//	br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+	//	return
+	//}
+	classifyIdsArr := make([]int, 0)
+	for _, v := range classifyIds {
+		if v != `` {
+			id, _ := strconv.Atoi(v)
+			classifyIdsArr = append(classifyIdsArr, id)
+		}
+	}
+
+	//if len(permissionClassifyIdList) > 0 {
+	//	classifyIdsArr = utils.IntersectInt(permissionClassifyIdList, classifyIdsArr)
+	//}
+
+	condition += " AND edb_info_type = 0 "
+	if len(classifyIdsArr) > 0 {
+		if !subClassify {
+			condition += " AND classify_id IN(" + utils.GetOrmInReplace(len(classifyIdsArr)) + ") "
+			pars = append(pars, classifyIdsArr)
+		} else {
+			classifyAll, err := data_manage.GetNormalEdbClassifyAll()
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取数据失败,Err:" + err.Error()
+				return
+			}
+			finalClassifyIds := make([]int, 0)
+			parents := data.GetEdbClassifyChildrenRecursiveByParentIds(classifyAll, classifyIds)
+			sort.Slice(parents, func(i, j int) bool {
+				return parents[i].Level < parents[i].Level
+			})
+			for _, v := range parents {
+				finalClassifyIds = append(finalClassifyIds, v.ClassifyId)
+			}
+
+			condition += " AND classify_id IN(" + utils.GetOrmInReplace(len(finalClassifyIds)) + ") "
+			pars = append(pars, finalClassifyIds)
+		}
+	}
+
+	if keyWord != "" {
+		keyWordArr := strings.Split(keyWord, " ")
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				condition += ` AND edb_name LIKE '%` + v + `%' `
+			}
+		}
+	}
+	if sources != "" {
+		condition += " AND source IN(" + utils.GetOrmInReplace(len(strings.Split(sources, ","))) + ") "
+		pars = append(pars, strings.Split(sources, ","))
+	}
+	if sysUserIds != "" {
+		adminIds := strings.Split(sysUserIds, ",")
+		if len(adminIds) == 0 {
+			br.Msg = "请选择正确的创建人"
+			return
+		}
+		adminIdsSlice := make([]int, 0)
+		for _, adminId := range adminIds {
+			adminIdInt, e := strconv.Atoi(adminId)
+			if e != nil {
+				br.Msg = "请选择正确的创建人"
+				return
+			}
+			adminIdsSlice = append(adminIdsSlice, adminIdInt)
+		}
+		condition += "  AND sys_user_id in (" + utils.GetOrmInReplace(len(adminIds)) + ") "
+		pars = append(pars, adminIdsSlice)
+	}
+
+	// 获取当前账号的不可见指标
+	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
+	}
+	noPermissionEdbInfoIds := make([]int, 0)
+	for _, v := range confList {
+		noPermissionEdbInfoIds = append(noPermissionEdbInfoIds, v.EdbInfoId)
+	}
+	if len(noPermissionEdbInfoIds) > 0 {
+		condition += " AND edb_info_id NOT IN(" + utils.GetOrmInReplace(len(noPermissionEdbInfoIds)) + ") "
+		pars = append(pars, noPermissionEdbInfoIds)
+	}
+
+	count, err := data_manage.GetEdbInfoByConditionCount(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	page := paging.GetPaging(currentIndex, pageSize, count)
+
+	list, err := data_manage.GetEdbInfoListByCondition(condition, pars, startSize, pageSize, "")
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(data_manage.EdbInfoFilterDataResp)
+	resp.List = list
+	resp.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 237 - 3
controllers/data_manage/edb_info.go

@@ -21,6 +21,7 @@ import (
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
+	"sort"
 	"strconv"
 	"strings"
 	"sync"
@@ -3463,6 +3464,13 @@ func (this *EdbInfoController) EdbInfoFilterByEs() {
 		}
 	}
 
+	// 只看我的
+	var sysUserId int
+	mine, _ := this.GetBool("IsShowMe")
+	if mine {
+		sysUserId = this.SysUser.AdminId
+	}
+
 	// 是否走ES
 	isEs := false
 	if keyWord != "" {
@@ -3474,10 +3482,14 @@ func (this *EdbInfoController) EdbInfoFilterByEs() {
 
 		// 普通的搜索
 		if !isAddPredictEdb {
-			total, edbInfoList, err = elastic.SearchEdbInfoData(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, filterSource, source, 0, frequency, noPermissionEdbInfoIdList)
+			if mine {
+				total, edbInfoList, err = elastic.SearchEdbInfoDataByAdminId(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, filterSource, source, 0, frequency, sysUserId)
+			} else {
+				total, edbInfoList, err = elastic.SearchEdbInfoData(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, filterSource, source, 0, frequency, noPermissionEdbInfoIdList)
+			}
 		} else {
 			// 允许添加预测指标的搜索
-			total, edbInfoList, err = elastic.SearchAddPredictEdbInfoData(utils.DATA_INDEX_NAME, keyWord, noPermissionEdbInfoIdList, startSize, pageSize)
+			total, edbInfoList, err = elastic.SearchAddPredictEdbInfoData(utils.DATA_INDEX_NAME, keyWord, noPermissionEdbInfoIdList, startSize, pageSize, sysUserId)
 		}
 		isEs = true
 	} else {
@@ -3523,6 +3535,11 @@ func (this *EdbInfoController) EdbInfoFilterByEs() {
 			condition += ` AND frequency in ("日度","周度","月度") `
 		}
 
+		if mine {
+			condition += ` AND sys_user_id = ? `
+			pars = append(pars, sysUserId)
+		}
+
 		total, edbInfoList, err = data_manage.GetEdbInfoFilterList(condition, pars, startSize, pageSize)
 	}
 	if err != nil {
@@ -3537,6 +3554,15 @@ func (this *EdbInfoController) EdbInfoFilterByEs() {
 	for i := 0; i < edbInfoListLen; i++ {
 		edbInfoList[i].EdbNameAlias = edbInfoList[i].EdbName
 		classifyIdList = append(classifyIdList, edbInfoList[i].ClassifyId)
+		// 如果没有关键词,那么搜索结果字段取指标名,前端已统一用该字段显示搜索的列表内容
+		if keyWord == "" {
+			if this.Lang == utils.ZhLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbName
+			}
+			if this.Lang == utils.EnLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbNameEn
+			}
+		}
 	}
 
 	// 当前列表中的分类map
@@ -4896,6 +4922,9 @@ func (this *EdbInfoController) AllEdbInfoByEs() {
 		}
 	}
 
+	// 只看我的
+	mine, _ := this.GetBool("IsShowMe")
+
 	// 是否走ES
 	isEs := false
 	if keyWord != "" {
@@ -4906,7 +4935,12 @@ func (this *EdbInfoController) AllEdbInfoByEs() {
 		keyWordArr = append(keyWordArr, newKeyWord...)
 
 		// 普通的搜索
-		total, edbInfoList, err = elastic.SearchEdbInfoData(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, filterSource, source, -1, frequency, noPermissionEdbInfoIdList)
+		if mine {
+			total, edbInfoList, err = elastic.SearchEdbInfoDataByAdminId(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, filterSource, source, 1, frequency, this.SysUser.AdminId)
+		} else {
+			total, edbInfoList, err = elastic.SearchEdbInfoData(utils.DATA_INDEX_NAME, keyWord, startSize, pageSize, filterSource, source, -1, frequency, noPermissionEdbInfoIdList)
+		}
+
 		isEs = true
 	} else {
 		var condition string
@@ -4947,6 +4981,11 @@ func (this *EdbInfoController) AllEdbInfoByEs() {
 			condition += ` AND frequency in ("日度","周度","月度") `
 		}
 
+		if mine {
+			condition += ` AND sys_user_id = ? `
+			pars = append(pars, this.SysUser.AdminId)
+		}
+
 		total, edbInfoList, err = data_manage.GetEdbInfoFilterList(condition, pars, startSize, pageSize)
 	}
 	if err != nil {
@@ -4961,6 +5000,15 @@ func (this *EdbInfoController) AllEdbInfoByEs() {
 	for i := 0; i < edbInfoListLen; i++ {
 		edbInfoList[i].EdbNameAlias = edbInfoList[i].EdbName
 		classifyIdList = append(classifyIdList, edbInfoList[i].ClassifyId)
+		// 如果没有关键词,那么搜索结果字段取指标名,前端已统一用该字段显示搜索的列表内容
+		if keyWord == "" {
+			if this.Lang == utils.ZhLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbName
+			}
+			if this.Lang == utils.EnLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbNameEn
+			}
+		}
 	}
 
 	// 当前列表中的分类map
@@ -6719,3 +6767,189 @@ func (this *EdbInfoController) ChartImageSetBySvg() {
 	br.Success = true
 	br.Msg = "保存成功"
 }
+
+// ModifyChartList
+// @Title 批量修改图表
+// @Description 批量修改图表
+// @Param   SysUserIds   query   string  true       "根据创建人查询"
+// @Param   ChartClassifyIds   query   string  true       "图片分类id"
+// @Success 200 {object} models.ChartClassifyListResp
+// @router /modify/edbList [post]
+func (this *EdbInfoController) ModifyEdbList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req request.ModifyEdbListReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	classifyIds := strings.Split(req.EdbClassifyIds, ",")
+
+
+	if !req.SelectAll && req.EdbInfoIds == "" {
+		br.Msg = "请选择图表"
+		return
+	}
+	if req.ClassifyId < 0 {
+		br.Msg = "请选择新分类"
+		return
+	}
+
+	var condition string
+	var pars []interface{}
+	edbInfoIds := make([]int, 0)
+
+	if req.SelectAll {
+		// 已授权分类id
+		permissionClassifyIdList, err := data_manage_permission.GetUserEdbClassifyPermissionList(this.SysUser.AdminId, 0)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+			return
+		}
+		classifyIdsArr := make([]int, 0)
+		for _, v := range classifyIds {
+			if v != `` {
+				id, _ := strconv.Atoi(v)
+				classifyIdsArr = append(classifyIdsArr, id)
+			}
+		}
+
+		if len(permissionClassifyIdList) > 0 {
+			classifyIdsArr = utils.IntersectInt(permissionClassifyIdList, classifyIdsArr)
+		}
+
+		condition += " AND edb_info_type = 0 "
+		if len(classifyIdsArr) > 0 {
+			if !req.SubClassify {
+				condition += " AND classify_id IN(" + utils.GetOrmInReplace(len(classifyIdsArr)) + ") "
+				pars = append(pars, classifyIdsArr)
+			} else {
+				classifyAll, err := data_manage.GetNormalEdbClassifyAll()
+				if err != nil && err.Error() != utils.ErrNoRow() {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取数据失败,Err:" + err.Error()
+					return
+				}
+				finalClassifyIds := make([]int, 0)
+				parents := data.GetEdbClassifyChildrenRecursiveByParentIds(classifyAll, classifyIds)
+				sort.Slice(parents, func(i, j int) bool {
+					return parents[i].Level < parents[i].Level
+				})
+				for _, v := range parents {
+					finalClassifyIds = append(finalClassifyIds, v.ClassifyId)
+				}
+
+				condition += " AND classify_id IN(" + utils.GetOrmInReplace(len(finalClassifyIds)) + ") "
+				pars = append(pars, finalClassifyIds)
+			}
+		}
+		if req.KeyWord != "" {
+			condition += ` AND edb_name LIKE '%` + req.KeyWord + `%' `
+		}
+		if req.Sources != "" {
+			condition += " AND source IN(" + utils.GetOrmInReplace(len(strings.Split(req.Sources, ","))) + ") "
+			pars = append(pars, strings.Split(req.Sources, ","))
+		}
+		if req.SysUserIds != "" {
+			adminIds := strings.Split(req.SysUserIds, ",")
+			if len(adminIds) == 0 {
+				br.Msg = "请选择正确的创建人"
+				return
+			}
+			adminIdsSlice := make([]int, 0)
+			for _, adminId := range adminIds {
+				adminIdInt, e := strconv.Atoi(adminId)
+				if e != nil {
+					br.Msg = "请选择正确的创建人"
+					return
+				}
+				adminIdsSlice = append(adminIdsSlice, adminIdInt)
+			}
+			condition += "  AND sys_user_id in (" + utils.GetOrmInReplace(len(adminIds)) + ") "
+			pars = append(pars, adminIdsSlice)
+		}
+		// 获取当前账号的不可见指标
+		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
+		}
+		noPermissionEdbInfoIds := make([]int, 0)
+		for _, v := range confList {
+			noPermissionEdbInfoIds = append(noPermissionEdbInfoIds, v.EdbInfoId)
+		}
+		if len(noPermissionEdbInfoIds) > 0 {
+			condition += " AND edb_info_id NOT IN(" + utils.GetOrmInReplace(len(noPermissionEdbInfoIds)) + ") "
+			pars = append(pars, noPermissionEdbInfoIds)
+		}
+
+		count, err := data_manage.GetEdbInfoByConditionCount(condition, pars)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		if count > 100 {
+			br.Msg = "最多选择100个"
+			return
+		}
+
+		list, err := data_manage.GetEdbInfoListByCondition(condition, pars, 0, 0, "")
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+
+
+		for _, v := range list {
+			edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+		}
+	} else {
+		edbIds := strings.Split(req.EdbInfoIds, ",")
+		if len(edbIds) == 0 {
+			br.Msg = "请选择图表"
+			return
+		}
+		for _, id := range edbIds {
+			tmp, e := strconv.Atoi(id)
+			if e != nil {
+				br.Msg = "请选择正确的分类"
+				return
+			}
+			edbInfoIds = append(edbInfoIds, tmp)
+		}
+		if len(edbInfoIds) > 100 {
+			br.Msg = "最多只能选择100个图表"
+			return
+		}
+	}
+
+	err = data_manage.UpdateEdbClassifyIdByChartInfoId(edbInfoIds, req.ClassifyId)
+	if err != nil {
+		br.Msg = "更新失败"
+		br.ErrMsg = "更新图表分类失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 10 - 1
controllers/data_manage/edb_info_refresh.go

@@ -262,11 +262,20 @@ func buildTree(items []*edb_refresh.BaseClassifyItems, parentId int) []*edb_refr
 // @router /edb_info/refresh/edb_list [get]
 func (c *EdbInfoController) RefreshEdbList() {
 	br := new(models.BaseResponse).Init()
-
 	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
 		c.Data["json"] = br
 		c.ServeJSON()
 	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
 
 	source, _ := c.GetInt("Source")
 	subSource, _ := c.GetInt("SubSource")

+ 35 - 7
controllers/data_manage/edb_info_relation.go

@@ -144,6 +144,9 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 	// 查询事件日历
 	eventInfoIds := make([]int, 0)
 
+	// 预测指标
+	predictEdbIds := make([]int, 0)
+
 	for _, v := range relationList {
 		switch v.ReferObjectType {
 		case utils.EDB_RELATION_SANDBOX:
@@ -154,9 +157,11 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 			chartInfoIds = append(chartInfoIds, v.ReferObjectId)
 		case utils.EDB_RELATION_TABLE:
 			tableInfoIds = append(tableInfoIds, v.ReferObjectId)
+		case utils.EDB_RELATION_PREDICT_EDB:
+			predictEdbIds = append(predictEdbIds, v.ReferObjectId)
 		}
 	}
-	objectNameMap := make(map[int]string)
+	objectNameMap := make(map[string]string)
 	if len(sandboxIds) > 0 {
 		sandboxList, err := sandbox.GetSandboxNameByIds(sandboxIds)
 		if err != nil && err.Error() != utils.ErrNoRow() {
@@ -165,7 +170,8 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 			return
 		}
 		for _, v := range sandboxList {
-			objectNameMap[v.SandboxId] = v.Name
+			name := fmt.Sprintf("%d_%d", utils.EDB_RELATION_SANDBOX, v.SandboxId)
+			objectNameMap[name] = v.Name
 		}
 	}
 
@@ -182,7 +188,8 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 			return
 		}
 		for _, v := range eventList {
-			objectNameMap[v.FeCalendarMatterId] = fmt.Sprintf("%s, %s", v.MatterDate, v.ChartPermissionName)
+			name := fmt.Sprintf("%d_%d", utils.EDB_RELATION_CALENDAR, v.FeCalendarMatterId)
+			objectNameMap[name] = fmt.Sprintf("%s, %s", v.MatterDate, v.ChartPermissionName)
 		}
 	}
 
@@ -195,7 +202,8 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 			return
 		}
 		for _, v := range chartList {
-			objectNameMap[v.ChartInfoId] = v.ChartName
+			name := fmt.Sprintf("%d_%d", utils.EDB_RELATION_CHART, v.ChartInfoId)
+			objectNameMap[name] = v.ChartName
 		}
 	}
 
@@ -233,16 +241,32 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 			}
 		}
 		for _, v := range tableList {
+			name := fmt.Sprintf("%d_%d", utils.EDB_RELATION_TABLE, v.ExcelInfoId)
 			if v.ParentId > 0 {
 				parentName := excelParentName[v.ParentId]
-				objectNameMap[v.ExcelInfoId] = fmt.Sprintf("%s_%s", parentName, v.ExcelName)
+				objectNameMap[name] = fmt.Sprintf("%s_%s", parentName, v.ExcelName)
 			} else {
-				objectNameMap[v.ExcelInfoId] = v.ExcelName
+				objectNameMap[name] = v.ExcelName
 			}
 		}
 	}
+
+	// 查询预测指标名称
+	if len(predictEdbIds) > 0 {
+		predictList, err := data_manage.GetEdbInfoByIdList(predictEdbIds)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取预测指标信息失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range predictList {
+			name := fmt.Sprintf("%d_%d", utils.EDB_RELATION_PREDICT_EDB, v.EdbInfoId)
+			objectNameMap[name] = v.EdbName
+		}
+	}
 	for _, v := range relationList {
-		referObjectName, _ := objectNameMap[v.ReferObjectId]
+		name := fmt.Sprintf("%d_%d", v.ReferObjectType, v.ReferObjectId)
+		referObjectName, _ := objectNameMap[name]
 		tmp := &data_manage.EdbInfoRelationDetail{
 			EdbInfoRelationId:  v.EdbInfoRelationId,
 			EdbInfoId:          v.EdbInfoId,
@@ -271,6 +295,8 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 				tmp.ReferObjectTypeName = "跨品种分析"
 			case utils.CHART_SOURCE_FUTURE_GOOD, utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
 				tmp.ReferObjectTypeName = "商品价格曲线"
+			case utils.CHART_SOURCE_RANGE_ANALYSIS:
+				tmp.ReferObjectTypeName = "区间分析"
 			}
 		case utils.EDB_RELATION_TABLE:
 			switch v.ReferObjectSubType {
@@ -281,6 +307,8 @@ func (c *EdbInfoRelationController) RelationEdbListDetail() {
 			case utils.BALANCE_TABLE:
 				tmp.ReferObjectTypeName = "平衡表"
 			}
+		case utils.EDB_RELATION_PREDICT_EDB:
+			tmp.ReferObjectTypeName = "预测指标"
 		}
 		list = append(list, tmp)
 	}

+ 1 - 0
controllers/data_manage/eia_steo.go

@@ -47,6 +47,7 @@ func (this *EdbInfoController) EiaSteoClassify() {
 	childClassifyMap := make(map[int][]*data_manage.BaseFromEiaSteoClassifyView)
 	rootList := make([]*data_manage.BaseFromEiaSteoClassifyView, 0)
 	for _, v := range classifyList {
+		v.UniqueCode = strconv.Itoa(v.BaseFromEiaSteoClassifyId)
 		if v.Level == 1 {
 			rootList = append(rootList, v)
 		} else {

+ 5 - 0
controllers/data_manage/excel/balance_table.go

@@ -1147,6 +1147,11 @@ func (c *ExcelInfoController) AddStaticExcel() {
 	resp.ExcelInfoId = excelInfo.ExcelInfoId
 	resp.UniqueCode = excelInfo.UniqueCode
 
+	// 写入ES
+	go func() {
+		excel2.EsAddOrEditExcel(excelInfo.ExcelInfoId)
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"

+ 11 - 0
controllers/data_manage/excel/custom_analysis.go

@@ -13,6 +13,7 @@ import (
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/data/excel"
 	excel2 "eta/eta_api/services/data/excel"
+	excel3 "eta/eta_api/services/excel"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -224,6 +225,11 @@ func (c *CustomAnalysisController) Add() {
 	// 生成excel文件
 	go excel.UpdateExcelInfoFileUrl(excelInfo)
 
+	// 写入ES
+	go func() {
+		excel3.EsAddOrEditExcel(excelInfo.ExcelInfoId)
+	}()
+
 	//新增操作日志
 	//{
 	//	excelLog := &data_manage.ExcelInfoLog{
@@ -344,6 +350,11 @@ func (c *CustomAnalysisController) Save() {
 	// 生成excel文件
 	go excel.UpdateExcelInfoFileUrl(excelInfo)
 
+	// 写入ES
+	go func() {
+		excel3.EsAddOrEditExcel(excelInfo.ExcelInfoId)
+	}()
+
 	//新增操作日志
 	//{
 	//	excelLog := &data_manage.ExcelInfoLog{

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

@@ -6,6 +6,7 @@ import (
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	excelPermissionModel "eta/eta_api/models/data_manage/data_manage_permission"
 	excel3 "eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/data_manage/excel/request"
 	"eta/eta_api/models/data_manage/excel/response"
@@ -13,6 +14,7 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/data/data_manage_permission"
 	excel2 "eta/eta_api/services/data/excel"
+	"eta/eta_api/services/elastic"
 	"eta/eta_api/services/excel"
 	"eta/eta_api/utils"
 	"fmt"
@@ -336,6 +338,15 @@ func (c *ExcelInfoController) Add() {
 	//	go data_manage.AddExcelInfoLog(excelLog)
 	//}
 
+	// 写入ES
+	go func() {
+		excel.EsAddOrEditExcel(excelInfo.ExcelInfoId)
+
+		if childExcel != nil && childExcel.ExcelInfoId > 0 {
+			excel.EsAddOrEditExcel(childExcel.ExcelInfoId)
+		}
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -613,6 +624,10 @@ func (c *ExcelInfoController) List() {
 				// excel表格按钮权限
 				list[k].Button = excel2.GetBalanceExcelInfoOpButton(sysUser.AdminId, v.SysUserId, v.HaveOperaAuth, v.ExcelInfoId)
 			}
+			// 持仓分析表格
+			if v.Source == utils.TRADE_ANALYSIS_TABLE || v.Source == utils.TRADE_ANALYSIS_CORRELATION_TABLE {
+				list[k].Button = services.GetTradeAnalysisTableOpButton(v.SysUserId, sysUser.AdminId, sysUser.RoleTypeCode, v.HaveOperaAuth)
+			}
 		}
 
 	}
@@ -1110,6 +1125,11 @@ func (c *ExcelInfoController) Edit() {
 	//删除公共图库那边的缓存
 	_ = utils.Rc.Delete(utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + excelInfo.UniqueCode)
 
+	// 写入ES
+	go func() {
+		excel.EsAddOrEditExcel(excelInfo.ExcelInfoId)
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "修改成功"
@@ -1431,6 +1451,11 @@ func (c *ExcelInfoController) Delete() {
 	//删除公共图库那边的缓存
 	_ = utils.Rc.Delete(utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL)
 
+	// 写入ES
+	go func() {
+		excel.EsAddOrEditExcel(excelInfo.ExcelInfoId)
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true
@@ -1633,7 +1658,7 @@ func (c *ExcelInfoController) GetExcelTableData() {
 		}
 		excelSource = strings.Join(sourceNameList, ",")
 		excelSourceEn = strings.Join(sourceNameEnList, ",")
-	case utils.MIXED_TABLE:
+	case utils.MIXED_TABLE, utils.BALANCE_TABLE:
 		var result request.MixedTableReq
 		err = json.Unmarshal([]byte(excelInfo.Content), &result)
 		if err != nil {
@@ -3392,6 +3417,233 @@ func (c *ExcelInfoController) GetExcelRuleDetail() {
 	br.Success = true
 }
 
+// SearchByEs
+// @Title ES搜索
+// @Description ES搜索
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   Source   query   int  true       "格来源,1:excel插件的表格,2:自定义表格,3:混合表格,默认:1"
+// @Param   IsShowMe   query   bool  false       "是否只看我的,true、false"
+// @Param   IsShare   query   bool  false       "是否只看我的,true、false"
+// @Success 200 {object} response.ExcelListResp
+// @router /excel_info/search_by_es [get]
+func (c *ExcelInfoController) SearchByEs() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	source, _ := c.GetInt("Source")
+	if source <= 0 {
+		source = utils.EXCEL_DEFAULT
+	}
+	isShowMe, _ := c.GetBool("IsShowMe")
+	isShare, _ := c.GetBool("IsShare")
+	keyword := c.GetString("KeyWord")
+	if keyword == `` {
+		keyword = c.GetString("Keyword")
+	}
+
+	var total, startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize15
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	// 平衡表的查询条件
+	var condBalance string
+	var parsBalance []interface{}
+	if source == utils.BALANCE_TABLE {
+		condBalance += ` AND source = ? AND parent_id = 0 AND balance_type = 0` // 只显示动态表的一级表(不显示子表和静态表)
+		parsBalance = append(parsBalance, source)
+	}
+
+	// 可见性过滤
+	var queryIds, exceptIds []int
+	{
+		unauthorized, e := excelPermissionModel.GetExcelInfoDataNoPermissionByUserId(sysUser.AdminId, source)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取无权限表格失败, %v", e)
+			return
+		}
+		if len(unauthorized) > 0 {
+			for _, v := range unauthorized {
+				id, _ := strconv.Atoi(v.DataId)
+				if id == 0 {
+					continue
+				}
+				exceptIds = append(exceptIds, id)
+			}
+		}
+	}
+
+	// 自定义分析表
+	var queryAdminId int
+	if source == utils.CUSTOM_ANALYSIS_TABLE {
+		// 自定义分析共享表格
+		if isShare {
+			var kw string
+			if keyword != "" {
+				kw = fmt.Sprint("%", kw, "%")
+			}
+			// 查询我分享的/分享给我的表格
+			excels, e := excelPermissionModel.GetAdminAuthExcelInfoPermission(source, sysUser.AdminId, kw)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取我分享的/分享给我的表格失败, %v", e)
+				return
+			}
+			var excelIds []int
+			for _, v := range excels {
+				id := int(v.ExcelInfoId)
+				if !utils.InArrayByInt(excelIds, id) {
+					excelIds = append(excelIds, id)
+					continue
+				}
+			}
+			if len(excelIds) == 0 {
+				list := make([]*excel3.SearchExcelInfo, 0)
+				resp := response.SearchExcelListResp{
+					Paging: page,
+					List:   list,
+				}
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				br.Data = resp
+				return
+			}
+			queryIds = excelIds
+		} else {
+			// 非共享只看我的
+			isShowMe = true
+		}
+	}
+
+	// 获取所有有权限的指标和分类
+	permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserExcelAndClassifyPermissionList(c.SysUser.AdminId, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
+		return
+	}
+	hasCheck := make(map[int]bool)
+	if isShowMe {
+		// 平衡表查询有权限的表格IDs
+		if source == utils.BALANCE_TABLE {
+			excelIds, e := services.GetBalanceExcelIdsByAdminId(sysUser.AdminId, condBalance, parsBalance, permissionEdbIdList, permissionClassifyIdList)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取平衡表有权限的表格IDs失败, %v", e)
+				return
+			}
+			if len(excelIds) == 0 {
+				list := make([]*excel3.SearchExcelInfo, 0)
+				resp := response.SearchExcelListResp{
+					Paging: page,
+					List:   list,
+				}
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				br.Data = resp
+				return
+			}
+			queryIds = excelIds
+		} else {
+			queryAdminId = sysUser.AdminId
+		}
+	}
+
+	// es搜索表格
+	t, list, e := elastic.SearchExcelInfoData(utils.EsExcelIndexName, keyword, source, queryAdminId, queryIds, exceptIds, startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("搜索表格失败, %v", e)
+		return
+	}
+	total = int(t)
+
+	if len(list) > 0 {
+		classifyIdList := make([]int, 0)
+		for _, v := range list {
+			classifyIdList = append(classifyIdList, v.ExcelClassifyId)
+
+		}
+		classifyMap := make(map[int]*excel3.ExcelClassify)
+
+		// 分类信息
+		{
+			classifyList, err := excel3.GetClassifyByIdList(classifyIdList)
+			if err != nil {
+				br.Msg = "获取表格列表信息失败"
+				br.ErrMsg = "获取表格分类列表数据失败,Err:" + err.Error()
+				return
+			}
+			for _, v := range classifyList {
+				classifyMap[v.ExcelClassifyId] = v
+			}
+		}
+
+		for k, v := range list {
+			// 数据权限
+			if authCheck, ok1 := hasCheck[v.ExcelInfoId]; ok1 {
+				v.HaveOperaAuth = authCheck
+			} else {
+				if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
+					v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
+				}
+			}
+			if v.Source == utils.BALANCE_TABLE {
+				// 处理按钮权限和编辑状态
+				markStatus, err := services.UpdateExcelEditMark(v.ExcelInfoId, sysUser.AdminId, 2, sysUser.RealName)
+				if err != nil {
+					br.Msg = "查询标记状态失败"
+					br.ErrMsg = "查询标记状态失败,Err:" + err.Error()
+					return
+				}
+				if markStatus.Status == 0 {
+					list[k].CanEdit = true
+				} else {
+					list[k].Editor = markStatus.Editor
+				}
+
+				// excel表格按钮权限
+				list[k].Button = excel2.GetBalanceExcelInfoOpButton(sysUser.AdminId, v.SysUserId, v.HaveOperaAuth, v.ExcelInfoId)
+			}
+		}
+	}
+
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.SearchExcelListResp{
+		Paging: page,
+		List:   list,
+	}
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
 // GetExcelReferenceDetail
 // @Title 表格引用配置详情
 // @Description 表格引用配置详情

+ 51 - 28
controllers/data_manage/fenwei_data.go

@@ -173,36 +173,53 @@ func (this *EdbInfoController) FenweiIndexData() {
 	}
 
 	resultList := make([]*data_manage.BaseFromFenweiIndexList, 0)
-	for _, v := range indexes {
-		product := new(data_manage.BaseFromFenweiIndexList)
-		product.FenweiIndexId = v.FenweiIndexId
-		product.ClassifyId = v.ClassifyId
-		product.Unit = v.Unit
-		product.IndexCode = v.IndexCode
-		product.IndexName = v.IndexName
-		product.Frequency = v.Frequency
-		product.CreateTime = v.CreateTime
-		product.ModifyTime = v.ModifyTime
-
-		edbInfo := edbCodeMap[v.IndexCode]
-		if edbInfo != nil {
-			product.EdbInfoId = edbInfo.EdbInfoId
-		}
-
-		total := countMap[v.IndexCode]
-		page := paging.GetPaging(currentIndex, pageSize, total)
-		dataList, e := data_manage.GetFenweiIndexData(v.IndexCode, startSize, pageSize)
-		if e != nil {
-			br.Msg = "获取数据失败"
-			br.ErrMsg = "获取指标数据失败,Err:" + e.Error()
+	if indexes != nil {
+		// 获取指标数据最新更新时间
+		lastModifyTimeList, err := data_manage.GetFenWeiDataLastModifyTimeList(indexCodes)
+		if err != nil {
 			return
 		}
-		if dataList == nil {
-			dataList = make([]*data_manage.BaseFromFenweiData, 0)
+		lastModifyTimeMap := make(map[string]string)
+		for _, v := range lastModifyTimeList {
+			lastModifyTimeMap[v.IndexCode] = v.ModifyTime
+		}
+
+		for _, v := range indexes {
+			product := new(data_manage.BaseFromFenweiIndexList)
+			product.FenweiIndexId = v.FenweiIndexId
+			product.ClassifyId = v.ClassifyId
+			product.Unit = v.Unit
+			product.IndexCode = v.IndexCode
+			product.IndexName = v.IndexName
+			product.Frequency = v.Frequency
+			product.CreateTime = v.CreateTime
+
+			if lastModifyTimeMap[v.IndexCode] != "" {
+				product.ModifyTime = lastModifyTimeMap[v.IndexCode]
+			} else {
+				product.ModifyTime = ""
+			}
+
+			edbInfo := edbCodeMap[v.IndexCode]
+			if edbInfo != nil {
+				product.EdbInfoId = edbInfo.EdbInfoId
+			}
+
+			total := countMap[v.IndexCode]
+			page := paging.GetPaging(currentIndex, pageSize, total)
+			dataList, e := data_manage.GetFenweiIndexData(v.IndexCode, startSize, pageSize)
+			if e != nil {
+				br.Msg = "获取数据失败"
+				br.ErrMsg = "获取指标数据失败,Err:" + e.Error()
+				return
+			}
+			if dataList == nil {
+				dataList = make([]*data_manage.BaseFromFenweiData, 0)
+			}
+			product.DataList = dataList
+			product.Paging = page
+			resultList = append(resultList, product)
 		}
-		product.DataList = dataList
-		product.Paging = page
-		resultList = append(resultList, product)
 	}
 
 	br.Ret = 200
@@ -318,8 +335,14 @@ func (this *EdbInfoController) FenweiSingleData() {
 	ret.IndexName = indexInfo.IndexName
 	ret.Frequency = indexInfo.Frequency
 	ret.CreateTime = indexInfo.CreateTime.Format(utils.FormatDateTime)
-	ret.ModifyTime = indexInfo.ModifyTime.Format(utils.FormatDateTime)
 	ret.Unit = indexInfo.Unit
+	// 获取数据最新更新时间
+	lastModifyTimeList, err := data_manage.GetFenWeiDataLastModifyTimeList([]string{indexInfo.IndexCode})
+	if err != nil {
+		return
+	}
+	ret.ModifyTime = lastModifyTimeList[0].ModifyTime
+
 	for _, v := range dataTmpList {
 		tmp := &data_manage.FenweiSingleData{
 			Value:    v.Value,

+ 8 - 3
controllers/data_manage/future_good/future_good_chart_info.go

@@ -104,7 +104,8 @@ func (this *FutureGoodChartInfoController) ChartList() {
 			for _, v := range keyWordArr {
 				if v != "" {
 					condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
-				}			}
+				}
+			}
 		}
 		condition = strings.TrimRight(condition, "OR")
 		condition += " ) "
@@ -2390,7 +2391,7 @@ func (this *FutureGoodChartInfoController) ChartInfoSearchByEs() {
 		showSysId = sysUser.AdminId
 	}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -2443,13 +2444,17 @@ func (this *FutureGoodChartInfoController) ChartInfoSearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 59 - 10
controllers/data_manage/gl_data.go

@@ -1,12 +1,13 @@
 package data_manage
 
 import (
-	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"github.com/tealeg/xlsx"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
 	"os"
 	"path/filepath"
 	"time"
@@ -174,30 +175,78 @@ func (this *EdbInfoController) GlData() {
 func (this *EdbInfoController) GlSearchList() {
 	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 = "请重新登录"
 		return
 	}
 
-	//关键字
-	keyword := this.GetString("Keyword")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("KeyWord")
+	if keyword == `` {
+		keyword = this.GetString("Keyword")
+	}
 
-	list, err := data_manage.GetGlItemList(keyword)
-	if err != nil {
-		br.ErrMsg = "获取失败,Err:" + err.Error()
+	var total, startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize15
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	// es搜索
+	t, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, keyword, utils.DATA_SOURCE_GL, 0, []int{}, []int{}, []string{}, startSize, pageSize)
+	if e != nil {
 		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("ES-搜索数据源列表失败, %v", e)
 		return
 	}
+	total = int(t)
+	resp := new(data_manage.GlDataPageListResp)
+	resp.Paging = paging.GetPaging(currentIndex, pageSize, total)
+	if len(list) == 0 {
+		resp.List = make([]*data_manage.GlSearchIndex, 0)
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+	highlightMap := make(map[int]string)
+	for _, v := range list {
+		highlightMap[v.PrimaryId] = v.SearchText
+	}
+
+	var ids []int
+	for _, v := range list {
+		ids = append(ids, v.PrimaryId)
+	}
+	items, e := data_manage.GetGlItemListByIds(ids)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标列表失败, %v", e)
+		return
+	}
+	for _, v := range items {
+		v.Source = utils.DATA_SOURCE_GL
+		v.SourceName = "钢联原始指标库"
+		v.SearchText = highlightMap[v.Id]
+	}
+	resp.List = items
 
+	br.Data = resp
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
-	br.Data = list
 }
 
 // GlSingleData

+ 1045 - 0
controllers/data_manage/gpr_risk_data.go

@@ -0,0 +1,1045 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+)
+
+type BaseFromGprRiskController struct {
+	controllers.BaseAuthController
+}
+
+// GprRiskClassify
+// @Title GPR地缘风险指数数据分类
+// @Description GPR地缘风险指数数据分类接口
+// @Success 200 {object} data_manage.BaseFromGprRiskClassify
+// @router /gpr_risk/classify [get]
+func (this *BaseFromGprRiskController) GprRiskClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	classifyAll, err := data_manage.GetAllBaseFromGprRiskClassify()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	//组装一级分类
+	rootMap := make(map[int][]*data_manage.BaseFromGprRiskClassifyItems)
+	list := make([]*data_manage.BaseFromGprRiskClassifyItems, 0)
+	for _, classify := range classifyAll {
+		classify.UniqueCode = strconv.Itoa(classify.ClassifyId)
+		if classify.ParentId == 0 {
+			if _, ok := rootMap[classify.ClassifyId]; !ok {
+				rootMap[classify.ClassifyId] = make([]*data_manage.BaseFromGprRiskClassifyItems, 0)
+				list = append(list, classify)
+
+			}
+		} else {
+			child, ok := rootMap[classify.ParentId]
+			if ok {
+				child = append(child, classify)
+				rootMap[classify.ParentId] = child
+			}
+		}
+	}
+
+	for k, v := range list {
+		child, ok := rootMap[v.ClassifyId]
+		if ok {
+			list[k].Children = child
+		}
+	}
+	//组装二级分类
+	var ret data_manage.BaseFromGprRiskClassifyResp
+	ret.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// GprRiskIndexData
+// @Title 获取GPR地缘风险指数数据
+// @Description 获取GPR地缘风险指数数据接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   string  true       "分类id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /gpr_risk/index/data [get]
+func (this *BaseFromGprRiskController) GprRiskIndexData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+	// 增加频度请求入参
+	frequency := this.GetString("Frequency")
+
+	//获取指标
+	var condition string
+	var pars []interface{}
+
+	if classifyId > 0 {
+		condition += ` AND classify_id=? `
+		pars = append(pars, classifyId)
+	}
+	if frequency != "" {
+		condition += ` AND frequency=? `
+		pars = append(pars, frequency)
+	}
+
+	GprRiskList, err := data_manage.GetGprRiskIndex(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	edbCodeList := make([]string, 0)
+	for _, v := range GprRiskList {
+		edbCodeList = append(edbCodeList, v.IndexCode)
+	}
+	edbInfoMap := make(map[string]*data_manage.EdbInfo)
+	dataMap := make(map[string][]*data_manage.BaseFromGprRiskData)
+	total := 0
+	if len(edbCodeList) > 0 {
+		edbInfoList, err := data_manage.GetEdbInfoByEdbCodeList(utils.DATA_SOURCE_GPR_RISK, edbCodeList)
+		if err != nil {
+			br.Msg = "获取数据源失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range edbInfoList {
+			edbInfoMap[v.EdbCode] = v
+		}
+		// 首先对分类下的指标按照日期进行分页,再针对日期,进行排序
+		dataTimes, err := data_manage.GetGprRiskIndexDataTimePageByCodes(edbCodeList, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据日期信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataTimes) > 0 {
+			startDate := dataTimes[len(dataTimes)-1]
+			endDate := dataTimes[0]
+			// 把截止日往后加1天
+			endDateT, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+			endDate = endDateT.AddDate(0, 0, 1).Format(utils.FormatDate)
+			dataList, e := data_manage.GetGprRiskIndexDataByDataTime(edbCodeList, startDate, endDate)
+			if e != nil {
+				br.Msg = "获取数据失败"
+				br.ErrMsg = "获取指标数据失败,Err:" + e.Error()
+				return
+			}
+			//将数据按照指标进行分类
+			for _, v := range dataList {
+				dataMap[v.IndexCode] = append(dataMap[v.IndexCode], v)
+			}
+		}
+
+		total, err = data_manage.GetGprRiskIndexDataTimePageCount(edbCodeList)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	resultList := make([]*data_manage.BaseFromGprRiskIndexList, 0)
+
+	for _, v := range GprRiskList {
+		product := new(data_manage.BaseFromGprRiskIndexList)
+		product.BaseFromGprRiskIndexId = v.BaseFromGprRiskIndexId
+		product.Unit = v.Unit
+		product.IndexCode = v.IndexCode
+		product.IndexName = v.IndexName
+		product.Frequency = v.Frequency
+		product.ModifyTime = v.ModifyTime
+		product.ClassifyId = v.ClassifyId
+		if edb, ok := edbInfoMap[v.IndexCode]; ok {
+			product.EdbInfoId = edb.EdbInfoId
+			product.EdbExist = 1
+		}
+
+		dataListTmp, ok := dataMap[v.IndexCode]
+		if !ok {
+			dataListTmp = make([]*data_manage.BaseFromGprRiskData, 0)
+		}
+		product.DataList = dataListTmp
+		product.Paging = page
+		resultList = append(resultList, product)
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resultList
+}
+
+// GprRiskSearchList
+// @Title GprRisk模糊搜索
+// @Description GprRisk模糊搜索
+// @Param   Keyword   query   string  ture       "关键字搜索"
+// @Success 200 {object} models.BaseResponse
+// @router /gpr_risk/search_list [get]
+func (this *BaseFromGprRiskController) GprRiskSearchList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	list := make([]*data_manage.BaseFromGprRiskIndexSearchItem, 0)
+	var err error
+	//关键字
+	keyword := this.GetString("Keyword")
+	if keyword != "" {
+		keyWordArr := strings.Split(keyword, " ")
+
+		if len(keyWordArr) > 0 {
+			condition := ""
+			for _, v := range keyWordArr {
+				condition += ` AND CONCAT(index_name,index_code) LIKE '%` + v + `%'`
+			}
+			list, err = data_manage.GetGprRiskItemList(condition)
+			if err != nil {
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				br.Msg = "获取失败"
+				return
+			}
+		}
+
+	} else {
+		list, err = data_manage.GetGprRiskItemList("")
+		if err != nil {
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			br.Msg = "获取失败"
+			return
+		}
+	}
+	classifyIds := make([]int, 0)
+	for _, v := range list {
+		classifyIds = append(classifyIds, v.ClassifyId)
+	}
+	classifyList, err := data_manage.GetBaseFromGprRiskClassifyByIds(classifyIds)
+	if err != nil {
+		br.Msg = "搜索失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	classifyMap := make(map[int]int)
+	for _, v := range classifyList {
+		classifyMap[v.ClassifyId] = v.ParentId
+	}
+	for _, v := range list {
+		v.ParentClassifyId = classifyMap[v.ClassifyId]
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// GprRiskSingleData
+// @Title 获取GprRisk数据
+// @Description 获取GprRisk单条数据接口
+// @Param   IndexCode   query   string  true       "指标唯一编码"
+// @Success 200 {object} models.BaseResponse
+// @router /gpr_risk/single_data [get]
+func (this *BaseFromGprRiskController) GprRiskSingleData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	indexCode := this.GetString("IndexCode")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	var total int64
+	page := paging.GetPaging(currentIndex, pageSize, int(total))
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	indexInfo, err := data_manage.GetBaseFromGprRiskIndexByIndexCode(indexCode)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+	total, err = data_manage.GetGprRiskIndexDataTotalByCode(indexCode)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, int(total))
+	dataTmpList, err := data_manage.GetGprRiskIndexDataByCode(indexCode, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_GPR_RISK, indexCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取数据源失败"
+		br.ErrMsg = "获取数据源失败,Err:" + err.Error()
+		return
+	}
+
+	var ret data_manage.GprRiskSingleDataResp
+	var dataList []*data_manage.GprRiskSingleData
+
+	if edbInfo != nil {
+		ret.EdbInfoId = edbInfo.EdbInfoId
+		ret.EdbExist = 1
+	}
+	ret.ClassifyId = indexInfo.ClassifyId
+	ret.BaseFromGprRiskIndexId = indexInfo.BaseFromGprRiskIndexId
+	ret.IndexCode = indexInfo.IndexCode
+	ret.IndexName = indexInfo.IndexName
+	ret.Frequency = indexInfo.Frequency
+	ret.CreateTime = indexInfo.CreateTime.Format(utils.FormatDateTime)
+	ret.ModifyTime = indexInfo.ModifyTime.Format(utils.FormatDateTime)
+	ret.Unit = indexInfo.Unit
+	for _, v := range dataTmpList {
+		tmp := &data_manage.GprRiskSingleData{
+			Value:    v.Value,
+			DataTime: v.DataTime,
+		}
+		dataList = append(dataList, tmp)
+	}
+	ret.Data = dataList
+	ret.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// GprRiskIndexList
+// @Title GPR地缘风险指数指标列表
+// @Description GPR地缘风险指数指标列表
+// @Param   ClassifyId   query   int  true       "分类id"
+// @Success 200 {object} data_manage.BaseFromGprRiskClassifyResp
+// @router /gpr_risk/classify/index/list [get]
+func (this *BaseFromGprRiskController) GprRiskIndexList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+	indexList, err := data_manage.GetGprRiskIndexByClassifyId(classifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+
+	var ret data_manage.BaseFromGprRiskClassifyResp
+	list := make([]*data_manage.BaseFromGprRiskClassifyItems, 0)
+	for _, v := range indexList {
+		classify := new(data_manage.BaseFromGprRiskClassifyItems)
+		classify.ClassifyId = classifyId
+		classify.BaseFromGprRiskIndexId = v.BaseFromGprRiskIndexId
+		classify.IndexCode = v.IndexCode
+		classify.ClassifyName = v.IndexName
+		classify.UniqueCode = fmt.Sprintf("%d_%d", classifyId, v.BaseFromGprRiskIndexId)
+		list = append(list, classify)
+	}
+	ret.List = list
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// GprRiskNameCheck
+// @Title 加入指标库的重名检测
+// @Description 加入指标库的重名检测
+// @Param   ClassifyIds   query   string  true       "分类id, 多个分类用英文"
+// @Param   Keyword   query   string  true       "关键词, 指标ID/指标名称"
+// @Success 200 {object} NameCheckResult
+// @router /gpr_risk/edb_info/name_check [post]
+func (this *BaseFromGprRiskController) GprRiskNameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req []*data_manage.NameCheckEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req)
+	if codeLen > codeMaxT {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+
+	indexNames := make([]string, 0)
+	resp := make([]*data_manage.EdbNameCheckResult, 0)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &data_manage.EdbNameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+		dataItems, err := data_manage.GetEdbDataAllByEdbCode(v.EdbCode, utils.DATA_SOURCE_GPR_RISK, 0, utils.EDB_DATA_LIMIT)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取钢联已存在信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataItems) <= 0 {
+			respItem, err := data.AddEdbData(utils.DATA_SOURCE_GPR_RISK, v.EdbCode, v.Frequency)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				return
+			}
+			if respItem.Ret != 200 {
+				br.Msg = "未搜索到该指标"
+				br.ErrMsg = respItem.ErrMsg + ";EdbCode:" + v.EdbCode
+				return
+			}
+		}
+	}
+
+	// 重名校验
+	edbList, e := data_manage.GetEdbInfoByNameArr(indexNames, utils.EDB_INFO_TYPE)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名指标失败, Err: " + e.Error()
+		return
+	}
+	nameExists := make(map[string]bool)
+	for _, v := range edbList {
+		nameExists[v.EdbName] = true
+	}
+	if len(nameExists) > 0 {
+		for _, v := range resp {
+			v.Exist = nameExists[v.EdbName]
+		}
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// GprRiskAddCheck
+// @Title 加入指标库指标Id检测
+// @Description 加入指标库指标Id检测
+// @Param	request	body request.BatchAddCheckReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /gpr_risk/edb_info/add_check [post]
+func (c *BaseFromGprRiskController) GprRiskAddCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.BatchAddCheckReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req.IndexCodes)
+
+	// 获取指标库已有指标
+	existsEdb, e := data_manage.GetEdbCodesBySource(utils.DATA_SOURCE_GPR_RISK)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取自有数据已添加的指标失败, Err: " + e.Error()
+		return
+	}
+	existMap := make(map[string]*data_manage.EdbInfo)
+	for _, v := range existsEdb {
+		existMap[v.EdbCode] = v
+	}
+
+	if codeLen == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if codeLen > codeMaxT {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMaxT)
+		return
+	}
+
+	// 查询选中的指标
+	cond := fmt.Sprintf(` AND index_code IN (%s)`, utils.GetOrmInReplace(codeLen))
+	pars := make([]interface{}, 0)
+	pars = append(pars, req.IndexCodes)
+	list, err := data_manage.GetGprRiskIndex(cond, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取钢联已存在信息失败,Err:" + err.Error()
+		return
+	}
+
+	if len(list) > codeMaxT {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMaxT)
+		return
+	}
+
+	resp := make([]*data_manage.BaseFromGprRiskIndexList, 0)
+	for _, v := range list {
+		if edb, ok := existMap[v.IndexCode]; ok {
+			v.EdbInfoId = edb.EdbInfoId
+			v.EdbClassifyId = edb.ClassifyId
+			v.EdbUniqueCode = edb.UniqueCode
+			v.EdbExist = 1
+		}
+		resp = append(resp, v)
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// GprRiskEdbInfoAdd
+// @Title 新增指标接口
+// @Description 新增指标接口
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /gpr_risk/edb_info/add [post]
+func (this *BaseFromGprRiskController) GprRiskEdbInfoAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req data_manage.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	req.EdbCode = strings.Trim(req.EdbCode, " ")
+
+	if req.EdbCode == "" {
+		br.Msg = "指标ID不能为空"
+		return
+	}
+
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	count, err := data_manage.GetGprRiskIndexDataCount(req.EdbCode)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	if count == 0 {
+		br.Msg = "指标不存在"
+	}
+
+	// 指标入库
+	edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(utils.DATA_SOURCE_GPR_RISK, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, req.StartDate, req.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	// 试用平台更新用户累计新增指标数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + e.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserIndexNum(r)
+		}()
+	}
+
+	//新增操作日志
+	{
+		edbLog := new(data_manage.EdbInfoLog)
+		edbLog.EdbInfoId = edbInfo.EdbInfoId
+		edbLog.SourceName = edbInfo.SourceName
+		edbLog.Source = edbInfo.Source
+		edbLog.EdbCode = edbInfo.EdbCode
+		edbLog.EdbName = edbInfo.EdbName
+		edbLog.ClassifyId = edbInfo.ClassifyId
+		edbLog.SysUserId = sysUser.AdminId
+		edbLog.SysUserRealName = sysUser.RealName
+		edbLog.CreateTime = time.Now()
+		edbLog.Content = string(this.Ctx.Input.RequestBody)
+		edbLog.Status = "新增指标"
+		edbLog.Method = this.Ctx.Input.URI()
+		go data_manage.AddEdbInfoLog(edbLog)
+	}
+
+	// 更新es
+	go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+
+	resp := new(data_manage.AddEdbInfoResp)
+	resp.EdbInfoId = edbInfo.EdbInfoId
+	resp.UniqueCode = edbInfo.UniqueCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// ExportGprRiskList
+// @Title 导出GPR地缘风险指数数据
+// @Description 导出GPR地缘风险指数数据
+// @Param   ClassifyId   query   int  true       "关键字搜索"
+// @Param   IndexCode   query   string  true       "指标编码"
+// @Success 200  导出成功
+// @router /gpr_risk/export [get]
+func (this *BaseFromGprRiskController) ExportGprRiskList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	indexCode := this.GetString("IndexCode")
+
+	if classifyId <= 0 && indexCode == "" {
+		br.Msg = "请选择分类或者指标"
+		return
+	}
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downLoadnFilePath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	var condition string
+	var pars []interface{}
+	var classifyName string
+	if classifyId > 0 {
+		classifyInfo, err := data_manage.GetBaseFromGprRiskClassifyById(classifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "分类不存在"
+				return
+			}
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取分类失败,Err:" + err.Error()
+			return
+		}
+		classifyName = classifyInfo.ClassifyName
+		childClassify, err := data_manage.GetBaseFromGprRiskClassifyByParentId(classifyId)
+		if err != nil {
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取分类失败,Err:" + err.Error()
+			return
+		}
+
+		if len(childClassify) > 0 {
+			condition += `AND classify_id IN (` + utils.GetOrmInReplace(len(childClassify)) + `)`
+			for _, child := range childClassify {
+				pars = append(pars, child.ClassifyId)
+			}
+		} else {
+			condition += ` AND classify_id=?`
+			pars = append(pars, classifyId)
+		}
+	}
+	if indexCode != "" {
+		condition += ` AND index_code=? `
+		pars = append(pars, indexCode)
+	}
+
+	indexList, err := data_manage.GetGprRiskIndex(condition, pars)
+	if err != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "获取指标失败,Err:" + err.Error()
+		fmt.Println("获取数据失败,Err:" + err.Error())
+		return
+	}
+	if len(indexList) <= 0 {
+		fmt.Println("indexList 为空")
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "success"
+		return
+	}
+
+	codeList := make([]string, 0)
+	frequenciesMap := make(map[string][]*data_manage.BaseFromGprRiskIndexList)
+	for _, v := range indexList {
+		codeList = append(codeList, v.IndexCode)
+		frequenciesMap[v.Frequency] = append(frequenciesMap[v.Frequency], v)
+	}
+	dataListMap := make(map[string][]*data_manage.BaseFromGprRiskData)
+	if len(indexList) > 0 {
+		allDataList, e := data_manage.GetGprRiskIndexDataByCodes(codeList)
+		if e != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取数据失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range allDataList {
+			dataListMap[v.IndexCode] = append(dataListMap[v.IndexCode], v)
+		}
+	}
+	// 按照频率分组排序
+	frequencies := []string{
+		"日度", "周度", "旬度", "月度", "季度", "半年度", "年度",
+	}
+	for _, frequency := range frequencies {
+		//获取指标
+		indexCodeList, ok := frequenciesMap[frequency]
+		if !ok {
+			continue
+		}
+		if len(indexCodeList) <= 0 {
+			fmt.Printf("sheet:%s, 不存在指标", frequency)
+			return
+		}
+		var sheetName string
+		switch frequency {
+		case "日度":
+			sheetName = "日度(Daily)"
+		case "周度":
+			sheetName = "周度(Weekly)"
+		case "旬度":
+			sheetName = "旬度(ten-day)"
+		case "月度":
+			sheetName = "月度(Monthly)"
+		case "季度":
+			sheetName = "季度(Quarterly)"
+		case "半年度":
+			sheetName = "半年度(Semi-annual)"
+		case "年度":
+			sheetName = "年度(Annual)"
+		default:
+			sheetName = "其他数据"
+		}
+		sheetNew, err := xlsxFile.AddSheet(sheetName)
+		if err != nil {
+			fmt.Println("新增Sheet失败", err.Error())
+			return
+		}
+		secNameRow := sheetNew.AddRow()
+		frequencyRow := sheetNew.AddRow()
+		unitRow := sheetNew.AddRow()
+		lastModifyDateRow := sheetNew.AddRow()
+
+		var indexIdList []int
+		for _, idx := range frequenciesMap[frequency] {
+			indexIdList = append(indexIdList, idx.BaseFromGprRiskIndexId)
+		}
+		dataTimeList, err := data_manage.GetGprRiskDataDataTimeByIndexId(indexIdList)
+		if err != nil {
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取数据时间失败,Err:" + err.Error()
+			fmt.Println("获取数据时间失败", err.Error())
+			return
+		}
+
+		// 添加excel左侧指标日期
+		setRowIndex := 4
+		for rk, dv := range dataTimeList {
+			rowIndex := setRowIndex + rk
+			row := sheetNew.Row(rowIndex)
+			displayDate, _ := time.Parse(utils.FormatDate, dv)
+			displayDateCell := row.AddCell()
+			style := new(xlsx.Style)
+			style.ApplyAlignment = true
+			style.Alignment.WrapText = true
+			displayDateCell.SetStyle(style)
+			displayDateCell.SetDate(displayDate)
+
+		}
+		for k, icl := range indexCodeList {
+			// 获取数据
+			dataList, ok := dataListMap[icl.IndexCode]
+			if !ok {
+				continue
+			}
+			if k == 0 {
+				secNameRow.AddCell().SetValue("指标名称/Metric Name")
+				frequencyRow.AddCell().SetValue("频度/Frequency")
+				unitRow.AddCell().SetValue("单位/Unit")
+				lastModifyDateRow.AddCell().SetValue("更新时间/Update Time")
+				min := k * 3
+				sheetNew.SetColWidth(min, min, 15)
+			}
+			if len(dataList) == 0 {
+				continue
+			}
+			secNameRow.AddCell().SetValue(icl.IndexName)
+			frequencyRow.AddCell().SetValue(icl.Frequency)
+			unitRow.AddCell().SetValue(icl.Unit)
+
+			timeDate, err := time.Parse(utils.FormatDateTime, dataList[0].ModifyTime)
+			if err != nil {
+				continue
+			}
+			lastModifyDateRow.AddCell().SetValue(timeDate.Format(utils.FormatDate))
+			dataInfoMap := make(map[string]*data_manage.BaseFromGprRiskData)
+			for _, v := range dataList {
+				dataInfoMap[v.DataTime] = v
+			}
+
+			for rk, dtv := range dataTimeList {
+				rowIndex := setRowIndex + rk
+				row := sheetNew.Row(rowIndex)
+				displayDateCell := row.AddCell()
+				tmpData, ok := dataInfoMap[dtv]
+				if ok {
+					displayDateCell.SetValue(tmpData.Value)
+				}
+			}
+		}
+	}
+
+	err = xlsxFile.Save(downLoadnFilePath)
+	if err != nil {
+		//有指标无数据时先导出一遍空表
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		e := xlsxFile.Save(downLoadnFilePath)
+		if e != nil {
+			br.Msg = "保存文件失败"
+			br.ErrMsg = "保存文件失败"
+			return
+		}
+	}
+
+	fileName := classifyName
+	if indexCode != "" && len(indexList) == 1 {
+		fileName = indexList[0].IndexName
+	}
+	fileName = strings.Replace(fileName, ": ", "_", -1)
+	fileName = strings.Replace(fileName, ", ", "_", -1)
+	fileName = strings.Replace(fileName, " ", "_", -1)
+	fileName += time.Now().Format("06.01.02") + `.xlsx` //文件名称
+	fmt.Println(fileName)
+	this.Ctx.Output.Download(downLoadnFilePath, fileName)
+	defer func() {
+		os.Remove(downLoadnFilePath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+}
+
+// GetFrequency
+// @Title GPR地缘风险指数数据频度
+// @Description GPR地缘风险指数数据频度接口
+// @Param   ClassifyId   query   string  true       "分类Id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /gpr_risk/frequency [get]
+func (this *BaseFromGprRiskController) GetFrequency() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+
+	frequencyList, err := data_manage.GetGprRiskFrequencyByClassifyId(classifyId)
+	if err != nil {
+		br.Msg = "获取频度失败"
+		br.ErrMsg = "获取频度失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = frequencyList
+}

+ 22 - 0
controllers/data_manage/jiayue_edb_source.go

@@ -71,6 +71,15 @@ func (this *JiaYueEdbSourceController) FrequencyList() {
 		return
 	}
 
+	// 测试环境老在报错=_=!
+	if utils.RunMode == "debug" {
+		br.Data = make([]string, 0)
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
 	frequencies, e := data.GetJiaYueFrequencyListFromBridge()
 	if e != nil {
 		br.Msg = "获取失败"
@@ -141,6 +150,19 @@ func (this *JiaYueEdbSourceController) IndexPageList() {
 		currentIndex = 1
 	}
 
+	// 测试环境老在报错=_=!
+	if utils.RunMode == "debug" {
+		page := paging.GetPaging(currentIndex, pageSize, 0)
+		resp := new(data_manage.JiaYueIndexPageListResp)
+		resp.Paging = page
+		resp.List = make([]*data_manage.DictIndexItem, 0)
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
 	var params data_manage.BridgeJiaYuePageIndexReq
 	params.PageIndex = currentIndex
 	params.PageSize = pageSize

+ 6 - 2
controllers/data_manage/line_equation/line_chart_info.go

@@ -1614,7 +1614,7 @@ func (this *LineEquationChartInfoController) SearchByEs() {
 
 	sourceList := []int{utils.CHART_SOURCE_LINE_EQUATION}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1667,13 +1667,17 @@ func (this *LineEquationChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if _, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, []*data_manage.ChartEdbInfoMapping{}, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 6 - 2
controllers/data_manage/line_feature/chart_info.go

@@ -2757,7 +2757,7 @@ func (this *LineFeaturesChartInfoController) SearchByEs() {
 
 	sourceList := []int{utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE, utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -2810,13 +2810,17 @@ func (this *LineFeaturesChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if _, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, []*data_manage.ChartEdbInfoMapping{}, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 45 - 11
controllers/data_manage/manual_edb.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data"
+	"eta/eta_api/services/elastic"
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
@@ -274,19 +275,47 @@ func (c *ManualEdbController) EdbSearch() {
 
 	var startSize int
 	if pageSize <= 0 {
-		pageSize = utils.PageSize20
+		pageSize = utils.PageSize15
 	}
 	if currentIndex <= 0 {
 		currentIndex = 1
 	}
 	startSize = utils.StartIndex(currentIndex, pageSize)
+	resp := new(models.EdbListResp)
 
 	//关键字
 	keyword := c.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
 
 	var condition string
 	var pars []interface{}
 
+	// es搜索
+	highlightMap := make(map[string]string)
+	if keyword != "" {
+		_, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, keyword, utils.DATA_SOURCE_MANUAL, 0, []int{}, []int{}, []string{}, startSize, pageSize)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("ES-搜索手工指标列表失败, %v", e)
+			return
+		}
+		if len(list) == 0 {
+			resp.List = make([]*models.EdbInfoListItem, 0)
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			br.Data = resp
+			return
+		}
+		var codes []string
+		for _, v := range list {
+			codes = append(codes, v.IndexCode)
+			highlightMap[v.IndexCode] = v.SearchText
+		}
+		condition += fmt.Sprintf(` AND a.TRADE_CODE IN (%s)`, utils.GetOrmInReplace(len(list)))
+		pars = append(pars, codes)
+	}
+
 	//userId := sysUser.AdminId
 	//超管账号可以查看分类下的所有频度数据
 	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
@@ -304,10 +333,10 @@ func (c *ManualEdbController) EdbSearch() {
 
 	}
 
-	if keyword != "" {
-		condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
-		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
-	}
+	//if keyword != "" {
+	//	condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+	//	pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+	//}
 
 	total, err := models.GetCountEdbInfoList(condition, pars)
 	if err != nil {
@@ -315,22 +344,27 @@ func (c *ManualEdbController) EdbSearch() {
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-
 	list, err := models.GetEdbInfoList(condition, pars, startSize, pageSize)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-
 	for _, v := range list {
 		v.UniqueCode = utils.MD5(v.TradeCode)
+		// ES搜索信息
+		v.Source = utils.DATA_SOURCE_MANUAL
+		v.SourceName = "手工指标录入"
+		s := highlightMap[v.TradeCode]
+		if s == "" {
+			s = v.SecName
+		}
+		v.ClassifyUniqueCode = utils.MD5(strconv.Itoa(v.ClassifyId))
+		v.SearchText = s
 	}
 
-	resp := models.EdbListResp{
-		List:   list,
-		Paging: paging.GetPaging(currentIndex, pageSize, total),
-	}
+	resp.List = list
+	resp.Paging = paging.GetPaging(currentIndex, pageSize, total)
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"

+ 9 - 0
controllers/data_manage/my_chart.go

@@ -1249,6 +1249,7 @@ func (this *MyChartController) MyChartList() {
 
 	myChartId, _ := this.GetInt("MyChartId")
 	isShared, _ := this.GetBool("IsShared")
+	keyword := this.GetString("Keyword")
 
 	var total int
 	page := paging.GetPaging(currentIndex, pageSize, total)
@@ -1285,6 +1286,14 @@ func (this *MyChartController) MyChartList() {
 			chartAdminId = myClassify.AdminId
 		}
 	}
+	if keyword != "" {
+		if this.Lang == utils.LANG_EN {
+			condition += " AND (b.chart_name_en like ?) "
+		} else {
+			condition += " AND (b.chart_name like ?) "
+		}
+		pars = append(pars, "%"+keyword+"%")
+	}
 
 	condition += " AND (a.admin_id = ? OR d.is_public = 1)"
 	pars = append(pars, chartAdminId)

+ 2 - 2
controllers/data_manage/predict_edb_classify.go

@@ -259,7 +259,7 @@ func (this *PredictEdbClassifyController) Edit() {
 		return
 	}
 
-	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ClassifyName, this.Lang, this.SysUser)
+	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ParentId, req.ClassifyName, this.Lang, this.SysUser)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -869,7 +869,7 @@ func (this *PredictEdbClassifyController) ClassifyTree() {
 			button := data.GetPredictEdbClassifyOpButton(this.SysUser, v.SysUserId, v.HaveOperaAuth)
 			allList[k].Button = button
 		}
-		nodeAll = data.GetClassifyTreeRecursive(allList, 0)
+		nodeAll = data.GetClassifyTreeRecursive(allList, 0, 0)
 		//根据sort值排序
 		sortList = nodeAll
 		sort.Sort(sortList)

+ 186 - 70
controllers/data_manage/predict_edb_info.go

@@ -427,31 +427,56 @@ func (this *PredictEdbInfoController) Add() {
 	ruleList := make([]request.RuleConfig, 0)
 	{
 		ruleMap := make(map[string]request.RuleConfig)
+		ruleEndNumSortMap := make(map[int]request.RuleConfig)
 		dateIntList := make([]int64, 0)
+		endNumList := make([]int, 0)
 		for _, v := range req.RuleList {
-			confEndDate, err := time.Parse(utils.FormatDate, v.EndDate)
-			if err != nil {
-				br.Msg = "配置项中时间异常,请重新选择"
-				br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
-				br.IsSendEmail = false
-				return
+			if req.EndDateType == 0 {
+				confEndDate, err := time.Parse(utils.FormatDate, v.EndDate)
+				if err != nil {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
+					br.IsSendEmail = false
+					return
+				}
+				dateIntList = append(dateIntList, confEndDate.Unix())
+				ruleMap[v.EndDate] = v
+			} else {
+				if _, ok := ruleEndNumSortMap[v.EndNum]; ok {
+					br.Msg = "所选期数不能和其他时间段相同"
+					return
+				}
+				endNumList = append(endNumList, v.EndNum)
+				ruleEndNumSortMap[v.EndNum] = v
 			}
-			dateIntList = append(dateIntList, confEndDate.Unix())
-			ruleMap[v.EndDate] = v
-		}
-		sort.Slice(dateIntList, func(i, j int) bool {
-			return dateIntList[i] < dateIntList[j]
-		})
-		for _, dateInt := range dateIntList {
-			currDateTime := time.Unix(dateInt, 0)
-			item, ok := ruleMap[currDateTime.Format(utils.FormatDate)]
-			if !ok {
-				br.Msg = "配置项中时间异常,请重新选择"
-				br.ErrMsg = "配置项中时间异常,请重新选择"
-				br.IsSendEmail = false
-				return
+		}
+		if req.EndDateType == 0 {
+			sort.Slice(dateIntList, func(i, j int) bool {
+				return dateIntList[i] < dateIntList[j]
+			})
+			for _, dateInt := range dateIntList {
+				currDateTime := time.Unix(dateInt, 0)
+				item, ok := ruleMap[currDateTime.Format(utils.FormatDate)]
+				if !ok {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择"
+					br.IsSendEmail = false
+					return
+				}
+				ruleList = append(ruleList, item)
+			}
+		} else {
+			sort.Ints(endNumList)
+			for _, endNum := range endNumList {
+				item, ok := ruleEndNumSortMap[endNum]
+				if !ok {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择"
+					br.IsSendEmail = false
+					return
+				}
+				ruleList = append(ruleList, item)
 			}
-			ruleList = append(ruleList, item)
 		}
 	}
 	req.RuleList = ruleList
@@ -479,6 +504,19 @@ func (this *PredictEdbInfoController) Add() {
 	}
 	resp := respItem.Data
 
+	edbInfoIdArr := make([]int, 0)
+	//查询相关的指标
+	edbMappingList, err := data_manage.GetAllCalculateByEdbInfoIdV2(resp.EdbInfoId)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取引用的指标信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range edbMappingList {
+		edbInfoIdArr = append(edbInfoIdArr, v.FromEdbInfoId)
+	}
+	// 添加指标引用记录
+	_ = data.SavePredictEdbInfoRelation(edbInfoIdArr, resp.EdbInfoId)
 	//添加es
 	data.AddOrEditEdbInfoToEs(resp.EdbInfoId)
 
@@ -603,31 +641,56 @@ func (this *PredictEdbInfoController) Edit() {
 	ruleList := make([]request.RuleConfig, 0)
 	{
 		ruleMap := make(map[string]request.RuleConfig)
+		ruleEndNumSortMap := make(map[int]request.RuleConfig)
 		dateIntList := make([]int64, 0)
+		endNumList := make([]int, 0)
 		for _, v := range req.RuleList {
-			confEndDate, err := time.Parse(utils.FormatDate, v.EndDate)
-			if err != nil {
-				br.Msg = "配置项中时间异常,请重新选择"
-				br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
-				br.IsSendEmail = false
-				return
+			if req.EndDateType == 0 {
+				confEndDate, err := time.Parse(utils.FormatDate, v.EndDate)
+				if err != nil {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
+					br.IsSendEmail = false
+					return
+				}
+				dateIntList = append(dateIntList, confEndDate.Unix())
+				ruleMap[v.EndDate] = v
+			} else {
+				if _, ok := ruleEndNumSortMap[v.EndNum]; ok {
+					br.Msg = "所选期数不能和其他时间段相同"
+					return
+				}
+				endNumList = append(endNumList, v.EndNum)
+				ruleEndNumSortMap[v.EndNum] = v
 			}
-			dateIntList = append(dateIntList, confEndDate.Unix())
-			ruleMap[v.EndDate] = v
-		}
-		sort.Slice(dateIntList, func(i, j int) bool {
-			return dateIntList[i] < dateIntList[j]
-		})
-		for _, dateInt := range dateIntList {
-			currDateTime := time.Unix(dateInt, 0)
-			item, ok := ruleMap[currDateTime.Format(utils.FormatDate)]
-			if !ok {
-				br.Msg = "配置项中时间异常,请重新选择"
-				br.ErrMsg = "配置项中时间异常,请重新选择"
-				br.IsSendEmail = false
-				return
+		}
+		if req.EndDateType == 0 {
+			sort.Slice(dateIntList, func(i, j int) bool {
+				return dateIntList[i] < dateIntList[j]
+			})
+			for _, dateInt := range dateIntList {
+				currDateTime := time.Unix(dateInt, 0)
+				item, ok := ruleMap[currDateTime.Format(utils.FormatDate)]
+				if !ok {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择"
+					br.IsSendEmail = false
+					return
+				}
+				ruleList = append(ruleList, item)
+			}
+		} else {
+			sort.Ints(endNumList)
+			for _, endNum := range endNumList {
+				item, ok := ruleEndNumSortMap[endNum]
+				if !ok {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择"
+					br.IsSendEmail = false
+					return
+				}
+				ruleList = append(ruleList, item)
 			}
-			ruleList = append(ruleList, item)
 		}
 	}
 	req.RuleList = ruleList
@@ -681,6 +744,19 @@ func (this *PredictEdbInfoController) Edit() {
 	}
 	resp := respItem.Data
 
+	edbInfoIdArr := make([]int, 0)
+	//查询相关的指标
+	edbMappingList, err := data_manage.GetAllCalculateByEdbInfoIdV2(resp.EdbInfoId)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取引用的指标信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range edbMappingList {
+		edbInfoIdArr = append(edbInfoIdArr, v.FromEdbInfoId)
+	}
+	// 添加指标引用记录
+	_ = data.SavePredictEdbInfoRelation(edbInfoIdArr, resp.EdbInfoId)
 	//修改es
 	data.AddOrEditEdbInfoToEs(resp.EdbInfoId)
 
@@ -901,6 +977,7 @@ func (this *PredictEdbInfoController) Detail() {
 				ModifyTime:       v.ModifyTime,
 				CreateTime:       v.CreateTime,
 				CalculateList:    tmpPredictEdbConfCalculateMappingDetail,
+				EndNum:           v.EndNum,
 			}
 			predictEdbConfList = append(predictEdbConfList, tmp)
 		}
@@ -1176,6 +1253,15 @@ func (this *PredictEdbInfoController) FilterByEs() {
 	for i := 0; i < edbInfoListLen; i++ {
 		edbInfoList[i].EdbNameAlias = edbInfoList[i].EdbName
 		classifyIdList = append(classifyIdList, edbInfoList[i].ClassifyId)
+		// 如果没有关键词,那么搜索结果字段取指标名,前端已统一用该字段显示搜索的列表内容
+		if keyWord == "" {
+			if this.Lang == utils.ZhLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbName
+			}
+			if this.Lang == utils.EnLangVersion {
+				edbInfoList[i].SearchText = edbInfoList[i].EdbNameEn
+			}
+		}
 	}
 
 	// 当前列表中的分类map
@@ -1523,31 +1609,56 @@ func (this *PredictEdbInfoController) ChartDataList() {
 	ruleList := make([]request.RuleConfig, 0)
 	{
 		ruleMap := make(map[string]request.RuleConfig)
+		ruleEndNumSortMap := make(map[int]request.RuleConfig)
 		dateIntList := make([]int64, 0)
+		endNumList := make([]int, 0)
 		for _, v := range req.RuleList {
-			confEndDate, err := time.Parse(utils.FormatDate, v.EndDate)
-			if err != nil {
-				br.Msg = "配置项中时间异常,请重新选择"
-				br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
-				br.IsSendEmail = false
-				return
+			if req.EndDateType == 0 {
+				confEndDate, err := time.Parse(utils.FormatDate, v.EndDate)
+				if err != nil {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
+					br.IsSendEmail = false
+					return
+				}
+				dateIntList = append(dateIntList, confEndDate.Unix())
+				ruleMap[v.EndDate] = v
+			} else {
+				if _, ok := ruleEndNumSortMap[v.EndNum]; ok {
+					br.Msg = "所选期数不能和其他时间段相同"
+					return
+				}
+				endNumList = append(endNumList, v.EndNum)
+				ruleEndNumSortMap[v.EndNum] = v
 			}
-			dateIntList = append(dateIntList, confEndDate.Unix())
-			ruleMap[v.EndDate] = v
-		}
-		sort.Slice(dateIntList, func(i, j int) bool {
-			return dateIntList[i] < dateIntList[j]
-		})
-		for _, dateInt := range dateIntList {
-			currDateTime := time.Unix(dateInt, 0)
-			item, ok := ruleMap[currDateTime.Format(utils.FormatDate)]
-			if !ok {
-				br.Msg = "配置项中时间异常,请重新选择"
-				br.ErrMsg = "配置项中时间异常,请重新选择"
-				br.IsSendEmail = false
-				return
+		}
+		if req.EndDateType == 0 {
+			sort.Slice(dateIntList, func(i, j int) bool {
+				return dateIntList[i] < dateIntList[j]
+			})
+			for _, dateInt := range dateIntList {
+				currDateTime := time.Unix(dateInt, 0)
+				item, ok := ruleMap[currDateTime.Format(utils.FormatDate)]
+				if !ok {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择"
+					br.IsSendEmail = false
+					return
+				}
+				ruleList = append(ruleList, item)
+			}
+		} else {
+			sort.Ints(endNumList)
+			for _, endNum := range endNumList {
+				item, ok := ruleEndNumSortMap[endNum]
+				if !ok {
+					br.Msg = "配置项中时间异常,请重新选择"
+					br.ErrMsg = "配置项中时间异常,请重新选择"
+					br.IsSendEmail = false
+					return
+				}
+				ruleList = append(ruleList, item)
 			}
-			ruleList = append(ruleList, item)
 		}
 	}
 
@@ -1556,13 +1667,17 @@ func (this *PredictEdbInfoController) ChartDataList() {
 
 	endDateStr := `` //数据的结束日期
 	for _, v := range ruleList {
-		endDateStr = v.EndDate //预测指标的结束日期
-		confEndDate, err := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
-		if err != nil {
-			br.Msg = "配置项中时间异常,请重新选择"
-			br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
-			return
+		var confEndDate time.Time
+		if v.EndDate != "" {
+			endDateStr = v.EndDate //预测指标的结束日期
+			confEndDate, err = time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+			if err != nil {
+				br.Msg = "配置项中时间异常,请重新选择"
+				br.ErrMsg = "配置项中时间异常,请重新选择,err:" + err.Error()
+				return
+			}
 		}
+
 		// 没有数据,自己瞎测试
 		//switch v.RuleType {
 		//case 3: //3:同比
@@ -1623,6 +1738,7 @@ func (this *PredictEdbInfoController) ChartDataList() {
 			FixedValue:       0,
 			Value:            v.Value,
 			EndDate:          confEndDate,
+			EndNum:           v.EndNum,
 			ModifyTime:       time.Now(),
 			CreateTime:       time.Now(),
 			DataList:         tmpDataList,
@@ -1696,7 +1812,7 @@ func (this *PredictEdbInfoController) ChartDataList() {
 		// 获取预测数据
 		var predictMinValue, predictMaxValue float64
 
-		predictDataList, predictMinValue, predictMaxValue, err, errMsg := data.GetChartPredictEdbInfoDataListByConfList(predictEdbConfAndDataList, startDate, sourceEdbInfoItem.LatestDate, endDateStr, sourceEdbInfoItem.Frequency, req.DataDateType, allDataList)
+		predictDataList, predictMinValue, predictMaxValue, err, errMsg := data.GetChartPredictEdbInfoDataListByConfList(predictEdbConfAndDataList, startDate, sourceEdbInfoItem.LatestDate, endDateStr, req.EndDateType, sourceEdbInfoItem.Frequency, req.DataDateType, allDataList)
 		if err != nil {
 			br.Msg = "获取预测指标数据失败"
 			if errMsg != `` {

+ 6 - 2
controllers/data_manage/range_analysis/chart_info.go

@@ -1818,7 +1818,7 @@ func (this *RangeChartChartInfoController) SearchByEs() {
 	sourceList := make([]int, 0)
 	sourceList = append(sourceList, utils.CHART_SOURCE_RANGE_ANALYSIS)
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1871,13 +1871,17 @@ func (this *RangeChartChartInfoController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 50 - 25
controllers/data_manage/smm_api.go

@@ -4,10 +4,10 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"strconv"
 	"strings"
 )
 
@@ -66,16 +66,16 @@ func (this *EdbInfoController) SmmApiList() {
 	if len(types) > 0 {
 		condition += " AND ( "
 		for _, v := range types {
-			typeArr := strings.Split(v,",")
+			typeArr := strings.Split(v, ",")
 			for i, v := range typeArr {
 				if i == 0 {
 					condition += " ( "
 				}
 				typeStr := "type_"
 				typeStr += fmt.Sprintf("%d", i+1)
-				condition += typeStr+" =? "
+				condition += typeStr + " =? "
 				pars = append(pars, v)
-				if i == len(typeArr) - 1 {
+				if i == len(typeArr)-1 {
 					condition += " ) "
 				} else {
 					condition += " AND "
@@ -88,13 +88,13 @@ func (this *EdbInfoController) SmmApiList() {
 	}
 
 	if frequency != "" {
-		frequencyArr := strings.Split(frequency,",")
+		frequencyArr := strings.Split(frequency, ",")
 		condition += ` AND frequency IN (` + utils.GetOrmInReplace(len(frequencyArr)) + `) `
 		pars = append(pars, frequencyArr)
 	}
 
 	if dataState != "" {
-		stateArr := strings.Split(dataState,",")
+		stateArr := strings.Split(dataState, ",")
 		if strings.Contains(dataState, "normal") {
 			stateArr = append(stateArr, "")
 			condition += ` AND data_state IN (` + utils.GetOrmInReplace(len(stateArr)) + `) `
@@ -107,25 +107,50 @@ func (this *EdbInfoController) SmmApiList() {
 
 	sortStr := ``
 
+	keyword = strings.TrimSpace(keyword)
 	if keyword != "" {
-		keyWordArr := strings.Split(keyword, " ")
-		if len(keyWordArr) > 0 {
-			condition += " AND ( "
-			keywordStr := strings.Replace(keyword, " ", "", -1)
-			condition += ` CONCAT(index_name,index_code) LIKE '%` + keywordStr + `%' OR `
-			sortStr += ` CASE WHEN CONCAT(index_name,index_code) LIKE '%` + keywordStr + `%' THEN 1 `
-			for i, v := range keyWordArr {
-				condition += ` CONCAT(index_name,index_code) LIKE '%` + v + `%' OR`
-				sortStr += ` WHEN CONCAT(index_name,index_code) LIKE '%` + v + `%' THEN  ` + strconv.Itoa(i+2) + ` `
+		//keyWordArr := strings.Split(keyword, " ")
+		//if len(keyWordArr) > 0 {
+		//	condition += " AND ( "
+		//	keywordStr := strings.Replace(keyword, " ", "", -1)
+		//	condition += ` CONCAT(index_name,index_code) LIKE '%` + keywordStr + `%' OR `
+		//	sortStr += ` CASE WHEN CONCAT(index_name,index_code) LIKE '%` + keywordStr + `%' THEN 1 `
+		//	for i, v := range keyWordArr {
+		//		condition += ` CONCAT(index_name,index_code) LIKE '%` + v + `%' OR`
+		//		sortStr += ` WHEN CONCAT(index_name,index_code) LIKE '%` + v + `%' THEN  ` + strconv.Itoa(i+2) + ` `
+		//	}
+		//	sortStr += ` END, `
+		//	condition = strings.TrimRight(condition, "OR")
+		//	condition += " ) "
+		//}
+
+		// ES搜
+		_, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, keyword, utils.DATA_SOURCE_YS, 0, []int{}, []int{}, []string{}, startSize, req.PageSize)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("ES-搜索SMM指标失败, %v", e)
+			return
+		}
+		if len(list) == 0 {
+			br.Data = data_manage.BaseFromSmmIndexListResp{
+				Paging: paging.GetPaging(req.CurrentIndex, req.PageSize, 0),
+				List:   make([]*data_manage.BaseFromSmmIndexItem, 0),
 			}
-			sortStr += ` END, `
-			condition = strings.TrimRight(condition, "OR")
-			condition += " ) "
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			return
 		}
+		var indexIds []int
+		for _, v := range list {
+			indexIds = append(indexIds, v.PrimaryId)
+		}
+		condition += fmt.Sprintf(" AND base_from_smm_index_id IN (%s)", utils.GetOrmInReplace(len(indexIds)))
+		pars = append(pars, indexIds)
 	}
 
 	if indexCodes != "" {
-		indexCodeArr := strings.Split(indexCodes,",")
+		indexCodeArr := strings.Split(indexCodes, ",")
 		indexCodeStr := ""
 		for _, v := range indexCodeArr {
 			indexCodeStr += "'" + v + "',"
@@ -141,7 +166,7 @@ func (this *EdbInfoController) SmmApiList() {
 	}
 
 	total, err := data_manage.GetSmmIndexDataListCount(condition, pars)
-	if err!= nil {
+	if err != nil {
 		br.Msg = "获取指标总数失败"
 		br.ErrMsg = "获取指标总数失败,Err:" + err.Error()
 		return
@@ -203,11 +228,11 @@ func (this *EdbInfoController) SmmApiTypeList() {
 
 	// 初始化
 	for _, v := range typeList {
-		if v.Type1 != ""{
-			if _, ok := typeMap[v.Type1];!ok {
+		if v.Type1 != "" {
+			if _, ok := typeMap[v.Type1]; !ok {
 				typeMap[v.Type1] = make(map[string][]string)
 			} else {
-				if _, ok := typeMap[v.Type1][v.Type2];!ok {
+				if _, ok := typeMap[v.Type1][v.Type2]; !ok {
 					typeMap[v.Type1][v.Type2] = make([]string, 0)
 				}
 			}
@@ -215,7 +240,7 @@ func (this *EdbInfoController) SmmApiTypeList() {
 	}
 
 	for _, v := range typeList {
-		if v.Type1 != ""{
+		if v.Type1 != "" {
 			typeMap[v.Type1][v.Type2] = append(typeMap[v.Type1][v.Type2], v.Type3)
 		}
 	}
@@ -227,7 +252,7 @@ func (this *EdbInfoController) SmmApiTypeList() {
 			var child data_manage.TypeListRespItem
 			child.Type = type2
 			for _, type3 := range type3List {
-				child.Child = append(child.Child, data_manage.TypeListRespItem{type3,nil})
+				child.Child = append(child.Child, data_manage.TypeListRespItem{type3, nil})
 			}
 			item.Child = append(item.Child, child)
 		}

+ 3 - 0
controllers/data_manage/smm_data.go

@@ -13,6 +13,7 @@ import (
 	"github.com/tealeg/xlsx"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -63,6 +64,7 @@ func (this *EdbInfoController) SmmClassify() {
 
 	rootChildMap := make(map[int][]*data_manage.BaseFromSmmClassifyItems)
 	for _, v := range classifyAll {
+		v.UniqueCode = strconv.Itoa(v.ClassifyId)
 		rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
 		/*if existItems, ok := baseFromSmmIndexMap[v.ClassifyId]; ok {
 			v.Children = existItems
@@ -93,6 +95,7 @@ func (this *EdbInfoController) SmmClassify() {
 		Level:              1,
 		Sort:               0,
 		Children:           nil,
+		UniqueCode:         "0",
 	}
 	/*initIndexList, err := data_manage.GetBaseFromSmmIndexByClassifyId(initClassify.ClassifyId)
 	if err != nil {

+ 6 - 1
controllers/data_manage/stl/stl.go

@@ -6,6 +6,7 @@ import (
 	"eta/eta_api/models"
 	"eta/eta_api/services/data/stl"
 	"eta/eta_api/utils"
+	"strings"
 
 	"eta/eta_api/models/data_manage/stl/request"
 	"eta/eta_api/models/data_manage/stl/response"
@@ -196,6 +197,10 @@ func (this *STLController) EdbInfoFilterByEs() {
 	pageSize, _ := this.GetInt("PageSize")
 	currentIndex, _ := this.GetInt("CurrentIndex")
 	keyWord := this.GetString("KeyWord")
+	if keyWord == "" {
+		keyWord = this.GetString("Keyword")
+	}
+	keyWord = strings.TrimSpace(keyWord)
 
 	if pageSize <= 0 {
 		pageSize = utils.PageSize20
@@ -203,7 +208,7 @@ func (this *STLController) EdbInfoFilterByEs() {
 	if currentIndex <= 0 {
 		currentIndex = 1
 	}
-	resp, msg, err := stl.SearchEdbInfoWithStl(this.SysUser.AdminId, keyWord, currentIndex, pageSize)
+	resp, msg, err := stl.SearchEdbInfoWithStl(this.SysUser.AdminId, keyWord, currentIndex, pageSize, this.Lang)
 	if err != nil {
 		if msg == "" {
 			msg = "获取异常"

+ 272 - 0
controllers/data_source/data_source.go

@@ -0,0 +1,272 @@
+package data_source
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	dataSourceModel "eta/eta_api/models/data_source"
+	"eta/eta_api/services/elastic"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strconv"
+)
+
+// SearchByEs
+// @Title ES搜索
+// @Description ES搜索
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   Source   query   int  true       "数据源"
+// @Success 200 {object} response.ExcelListResp
+// @router /common/search_by_es [get]
+func (c *DataSourceController) SearchByEs() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyword := c.GetString("KeyWord")
+	if keyword == `` {
+		keyword = c.GetString("Keyword")
+	}
+	source, _ := c.GetInt("Source")
+	if source <= 0 {
+		br.Msg = "来源有误"
+		br.ErrMsg = fmt.Sprintf("数据来源有误, Source: %d", source)
+		return
+	}
+	subSource, _ := c.GetInt("SubSource")
+
+	// 以下为兼容各旧接口的额外传参,不为空时修改Resp对应的Key
+	primaryIdKey := c.GetString("PrimaryIdKey")
+	indexNameKey := c.GetString("IndexNameKey")
+	classifyIdKey := c.GetString("ClassifyIdKey")
+
+	var total, startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize15
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	// es搜索
+	t, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, keyword, source, subSource, []int{}, []int{}, []string{}, startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("ES-搜索数据源列表失败, %v", e)
+		return
+	}
+	total = int(t)
+
+	// (短期方案)中国煤炭市场网/煤炭江湖额外补充基础字段
+	indexModifyTime := make(map[string]string)
+	indexBaseInfo := make(map[string]*dataSourceModel.BaseFromCoalmineIndexBase)
+	{
+		if source == utils.DATA_SOURCE_MTJH {
+			var updateCodes []string
+			for _, v := range list {
+				if v.ModifyTime == "" {
+					updateCodes = append(updateCodes, v.IndexCode)
+				}
+			}
+			if len(updateCodes) > 0 {
+				baseIndexes, e := dataSourceModel.GetMtjhBaseInfoFromDataTable(updateCodes)
+				if e != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = fmt.Sprintf("获取煤炭江湖指标基础信息失败, %v", e)
+					return
+				}
+				for _, v := range baseIndexes {
+					indexModifyTime[v.IndexCode] = utils.TimeTransferString(utils.FormatDateTime, v.ModifyTime)
+				}
+			}
+		}
+		if source == utils.DATA_SOURCE_COAL {
+			var updateCodes []string
+			for _, v := range list {
+				if v.Unit == "" || v.Frequency == "" || v.ModifyTime == "" {
+					updateCodes = append(updateCodes, v.IndexCode)
+				}
+			}
+			if len(updateCodes) > 0 {
+				baseIndexes, e := dataSourceModel.GetCoalmineBaseInfoFromDataTable(updateCodes)
+				if e != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = fmt.Sprintf("获取中国煤炭市场网指标基础信息失败, %v", e)
+					return
+				}
+				for _, v := range baseIndexes {
+					indexBaseInfo[v.IndexCode] = v
+				}
+			}
+		}
+	}
+
+	listMap := make([]map[string]interface{}, 0)
+	for _, v := range list {
+		// (短期方案)由于start_date、end_date和latest_value字段不全的历史遗留问题,这里查出来并补充进ES里面去
+		var updateEs bool
+		if v.StartDate == "" || v.EndDate == "" || v.LatestValue == "" || v.LatestValue == "0" {
+			minMax, e := dataSourceModel.GetBaseIndexDataMinMax(v.Source, v.SubSource, v.IndexCode)
+			if e != nil && e.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取指标开始结束时间失败, %v", e)
+				return
+			}
+			if minMax != nil {
+				v.StartDate = minMax.MinDate
+				v.EndDate = minMax.MaxDate
+				v.LatestValue = minMax.LatestValue
+				updateEs = true
+			}
+		}
+
+		// 煤炭江湖缺ModifyTime
+		if source == utils.DATA_SOURCE_MTJH && v.ModifyTime == "" && indexModifyTime[v.IndexCode] != "" {
+			v.ModifyTime = indexModifyTime[v.IndexCode]
+			updateEs = true
+		}
+
+		// 中国煤炭市场网缺Unit, Frequency, ModifyTime
+		if source == utils.DATA_SOURCE_COAL && (v.Unit == "" || v.Frequency == "" || v.ModifyTime == "") {
+			if indexBaseInfo[v.IndexCode] != nil {
+				v.Unit = indexBaseInfo[v.IndexCode].Unit
+				v.Frequency = indexBaseInfo[v.IndexCode].Frequency
+				v.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, indexBaseInfo[v.IndexCode].ModifyTime)
+				updateEs = true
+			}
+		}
+
+		// 写入ES更新队列
+		if updateEs {
+			if e := utils.Rc.LPush(utils.CACHE_DATA_SOURCE_ES_HANDLE, v); e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("写入ES更新队列失败, Source: %d, IndexCode: %s, err: %v", v.Source, v.IndexCode, e)
+				return
+			}
+		}
+
+		// 转换成map返回
+		listMap = append(listMap, v.ToMap(primaryIdKey, indexNameKey, classifyIdKey))
+	}
+
+	// 美国农业部(多一个ParentClassifyId字段)
+	if source == utils.DATA_SOURCE_USDA_FAS {
+		// 父级分类ID
+		classifyIds := make([]int, 0)
+		for _, v := range list {
+			classifyIds = append(classifyIds, v.ClassifyId)
+		}
+		classifyList, e := data_manage.GetBaseFromUsdaFasClassifyByIds(classifyIds)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取美国农业部分类失败, %v", e)
+			return
+		}
+		classifyMap := make(map[int]int)
+		for _, v := range classifyList {
+			classifyMap[v.ClassifyId] = v.ParentId
+		}
+		for _, v := range listMap {
+			id, ok := v["ClassifyId"].(int)
+			if !ok {
+				v["ParentClassifyId"] = 0
+				continue
+			}
+			v["ParentClassifyId"] = classifyMap[id]
+		}
+	}
+
+	// 煤炭江湖(多一个Area字段)
+	if source == utils.DATA_SOURCE_MTJH {
+		cond := ``
+		pars := make([]interface{}, 0)
+		indexes, e := data_manage.GetMtjhItemsByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取煤炭江湖指标失败, %v", e)
+			return
+		}
+		indexArea := make(map[string]string)
+		for _, v := range indexes {
+			indexArea[v.IndexCode] = v.Area
+		}
+		for _, v := range listMap {
+			code, ok := v["IndexCode"].(string)
+			if !ok {
+				v["Area"] = ""
+				continue
+			}
+			v["Area"] = indexArea[code]
+		}
+	}
+
+	// 分类的唯一编码(前端定位用)
+	if classifyIdKey == "" {
+		classifyIdKey = "ClassifyId"
+	}
+	idUniqueCodeArr := []int{
+		utils.DATA_SOURCE_SCI_HQ, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, utils.DATA_SOURCE_YS, utils.DATA_SOURCE_EIA_STEO,
+	}
+	if utils.InArrayByInt(idUniqueCodeArr, source) {
+		for _, v := range listMap {
+			classifyId, ok := v[classifyIdKey].(int)
+			if !ok {
+				v["ClassifyUniqueCode"] = ""
+				continue
+			}
+			v["ClassifyUniqueCode"] = strconv.Itoa(classifyId)
+		}
+	}
+	//if source == utils.DATA_SOURCE_MANUAL {
+	//	// 手工指标
+	//	var wxUserId int64
+	//	wxUserId = int64(sysUser.AdminId)
+	//	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
+	//		wxUserId = 0
+	//	}
+	//	classifies, err := models.GetEdbdataClassify(wxUserId)
+	//	if err != nil {
+	//		br.Msg = "获取失败"
+	//		br.ErrMsg = fmt.Sprintf("获取手工指标分类失败, %v", e)
+	//		return
+	//	}
+	//	unicodeMap := make(map[int]string)
+	//	for _, v := range classifies {
+	//		unicodeMap[v.ClassifyId] = v.UniqueCode
+	//	}
+	//	for _, v := range listMap {
+	//		classifyId, ok := v["ClassifyId"].(int)
+	//		if !ok {
+	//			continue
+	//		}
+	//		v["ClassifyUniqueCode"] = unicodeMap[classifyId]
+	//	}
+	//}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := dataSourceModel.SearchDataSourceResp{
+		Paging: page,
+		List:   listMap,
+	}
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 256 - 0
controllers/eta_forum/eta_forum.go

@@ -0,0 +1,256 @@
+package eta_forum
+
+import (
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/eta_forum"
+	"eta/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type EtaForumController struct {
+	controllers.BaseAuthController
+}
+
+// UserChartList
+// @Title 查询用户在eta社区中有权限查看的图表列表
+// @Description 查询用户在eta社区中有权限查看的图表列表
+// @Param	request	body data_manage.SetChartInfoImageReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /chart_list [get]
+func (this *EtaForumController) UserChartList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	keyword := this.GetString("Keyword")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	businessCode := utils.BusinessCode
+	userMobile := sysUser.Mobile
+	telAreaCode := sysUser.TelAreaCode
+	if businessCode == "" {
+		br.Msg = "商户号未配置"
+		return
+	}
+
+	if userMobile == "" {
+		br.Msg = "请先绑定手机号"
+		return
+	}
+	if telAreaCode == "" {
+		telAreaCode = utils.TelAreaCodeHome
+	}
+
+	resp, err, _ := eta_forum.GetUserChartList(businessCode, userMobile, telAreaCode, keyword, currentIndex, pageSize)
+	if err != nil {
+		/*br.Msg = errMsg
+		br.ErrMsg = err.Error()*/
+		page := paging.GetPaging(currentIndex, pageSize, 0)
+		resp = eta_forum.UserChartListRespItem{
+			ChartInfoList: make([]*data_manage.ChartInfo, 0),
+			Paging:        page,
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "保存成功"
+		br.Data = resp
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// UserCollectChartClassifyList
+// @Title 查询社区中用户收藏的分类列表
+// @Description 查询社区中用户收藏的分类列表
+// @Param	request	body data_manage.SetChartInfoImageReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /collect/chart_classify [get]
+func (this *EtaForumController) UserCollectChartClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	businessCode := utils.BusinessCode
+	userMobile := sysUser.Mobile
+	telAreaCode := sysUser.TelAreaCode
+	if businessCode == "" {
+		br.Msg = "商户号未配置"
+		return
+	}
+
+	if userMobile == "" {
+		br.Msg = "请先绑定手机号"
+		return
+	}
+	if telAreaCode == "" {
+		telAreaCode = utils.TelAreaCodeHome
+	}
+
+	resp, err, _ := eta_forum.GetUserCollectChartClassifyList(businessCode, userMobile, telAreaCode)
+	if err != nil {
+		resp = eta_forum.UserCollectChartClassifyListItem{
+			List:     make([]*eta_forum.ChartCollectClassifyItem, 0),
+			Language: "CN",
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "保存成功"
+		br.Data = resp
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// UserCollectChartList
+// @Title 查询社区中用户收藏的图表列表
+// @Description 查询社区中用户收藏的图表列表
+// @Param	request	body data_manage.SetChartInfoImageReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /collect/chart [get]
+func (this *EtaForumController) UserCollectChartList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	businessCode := utils.BusinessCode
+	userMobile := sysUser.Mobile
+	telAreaCode := sysUser.TelAreaCode
+	if businessCode == "" {
+		br.Msg = "商户号未配置"
+		return
+	}
+
+	if userMobile == "" {
+		br.Msg = "请先绑定手机号"
+		return
+	}
+	if telAreaCode == "" {
+		telAreaCode = utils.TelAreaCodeHome
+	}
+
+	collectClassifyIds := this.GetString("CollectClassifyIds")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("Keyword")
+
+	resp, err, _ := eta_forum.GetUserCollectChartList(businessCode, userMobile, telAreaCode, keyword, collectClassifyIds, currentIndex, pageSize)
+	if err != nil {
+		page := paging.GetPaging(currentIndex, pageSize, 0)
+		resp = eta_forum.UserCollectChartListRespItem{
+			List:   make([]*eta_forum.ChartCollectView, 0),
+			Paging: page,
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "保存成功"
+		br.Data = resp
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+}
+
+// CommonChartInfoDetailFromUniqueCode
+// @Title 根据编码获取图表详情
+// @Description 根据编码获取图表详情接口
+// @Param   UniqueCode   query   int  true       "图表唯一编码,如果是管理后台访问,传固定字符串:7c69b590249049942070ae9dcd5bf6dc"
+// @Param   IsCache   query   bool  true       "是否走缓存,默认false"
+// @Success 200 {object} data_manage.ChartInfoDetailFromUniqueCodeResp
+// @router /chart/from_unique_code [get]
+func (this *EtaForumController) CommonChartInfoDetailFromUniqueCode() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	uniqueCode := this.GetString("UniqueCode")
+	if uniqueCode == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,uniqueCode is empty"
+		return
+	}
+
+	//是否走缓存
+	isCache, _ := this.GetBool("IsCache")
+	resp := new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+	status := true
+	forumResp, err, _ := eta_forum.GeChartFromUniqueCode(uniqueCode, isCache)
+	if err != nil {
+		endInfoList := make([]*data_manage.ChartEdbInfoMapping, 0)
+		resp.EdbInfoList = endInfoList
+		resp.ChartInfo = nil
+		resp.Status = false
+
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	chartInfo := forumResp.ChartInfo
+	if chartInfo == nil {
+		endInfoList := make([]*data_manage.ChartEdbInfoMapping, 0)
+		resp.EdbInfoList = endInfoList
+		resp.ChartInfo = nil
+		resp.Status = false
+
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	resp.ChartInfo = chartInfo
+	resp.Status = status
+	resp.DataResp = forumResp.DataResp
+	resp.EdbInfoList = forumResp.EdbInfoList
+	resp.XDataList = forumResp.XDataList
+	resp.YDataList = forumResp.YDataList
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 1908 - 0
controllers/material/material.go

@@ -0,0 +1,1908 @@
+package material
+
+import (
+	"archive/zip"
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/material"
+	"eta/eta_api/services"
+	materialService "eta/eta_api/services/material"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/http"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"io/ioutil"
+	"os"
+	"path"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// MaterialController 逻辑导图
+type MaterialController struct {
+	controllers.BaseAuthController
+}
+
+// AddMaterialClassify
+// @Title 新增素材库分类
+// @Description 新增材库分类接口
+// @Param	request	body data_manage.AddChartClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /classify/add [post]
+func (this *MaterialController) AddMaterialClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req material.AddMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	level := 1
+	levelPath := ""
+	if req.ParentId > 0 {
+		//查找父级分类
+		parentClassify, e := material.GetMaterialClassifyById(req.ParentId)
+		if e != nil {
+			br.Msg = "获取父级分类失败"
+			br.ErrMsg = "获取父级分类失败,Err:" + e.Error()
+			return
+		}
+		level = parentClassify.Level + 1
+		levelPath = parentClassify.LevelPath
+	}
+	var count int
+	switch this.Lang {
+	case utils.LANG_EN:
+		count, err = material.GetMaterialClassifyNameEnCount(req.ClassifyName, req.ParentId)
+	default:
+		count, err = material.GetMaterialClassifyNameCount(req.ClassifyName, req.ParentId)
+	}
+
+	if err != nil {
+		br.Msg = "判断名称是否已存在失败"
+		br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "分类名称已存在,请重新输入"
+		br.IsSendEmail = false
+		return
+	}
+
+	//获取该层级下最大的排序数
+	classify := new(material.MaterialClassify)
+	maxSort, _ := material.GetMaterialClassifyMaxSort(req.ParentId)
+	classify.ParentId = req.ParentId
+	classify.ClassifyName = req.ClassifyName
+	classify.ClassifyNameEn = req.ClassifyName
+	classify.CreateTime = time.Now()
+	classify.ModifyTime = time.Now()
+	classify.SysUserId = this.SysUser.AdminId
+	classify.SysUserRealName = this.SysUser.RealName
+	classify.Level = level
+	classify.Sort = maxSort + 1
+	classifyId, e := material.AddMaterialClassify(classify)
+	if e != nil {
+		br.Msg = "保存分类失败"
+		br.ErrMsg = "保存分类失败,Err:" + e.Error()
+		return
+	}
+	if req.ParentId > 0 {
+		levelPath = fmt.Sprintf("%s%d,", levelPath, classifyId)
+	} else {
+		levelPath = fmt.Sprintf("%d,", classifyId)
+	}
+	classify.ClassifyId = int(classifyId)
+	classify.LevelPath = levelPath
+	e = classify.Update([]string{"LevelPath"})
+	if e != nil {
+		br.Msg = "保存分类失败"
+		br.ErrMsg = "保存分类失败,Err:" + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+}
+
+// EditMaterialClassify
+// @Title 修改素材库分类
+// @Description 修改素材库分类接口
+// @Param	request	body data_manage.EditChartClassifyReq true "type json string"
+// @Success 200 Ret=200 修改成功
+// @router /classify/edit [post]
+func (this *MaterialController) EditMaterialClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req material.EditMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 只允许修改分类名称
+	item, err := material.GetMaterialClassifyById(req.ClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在"
+			return
+		}
+		br.Msg = "保存失败"
+		br.Msg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	var count int
+	updateStr := make([]string, 0)
+	switch this.Lang {
+	case utils.LANG_EN:
+		count, err = material.GetMaterialClassifyNameEnNotSelfCount(req.ClassifyId, req.ClassifyName, item.ParentId)
+		item.ClassifyNameEn = req.ClassifyName
+		updateStr = append(updateStr, "ClassifyNameEn")
+	default:
+		count, err = material.GetMaterialClassifyNameNotSelfCount(req.ClassifyId, req.ClassifyName, item.ParentId)
+		item.ClassifyName = req.ClassifyName
+		updateStr = append(updateStr, "ClassifyName")
+	}
+
+	if err != nil {
+		br.Msg = "判断名称是否已存在失败"
+		br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "分类名称已存在,请重新输入"
+		br.IsSendEmail = false
+		return
+	}
+
+	item.ModifyTime = time.Now()
+	updateStr = append(updateStr, "ModifyTime")
+	err = item.Update(updateStr)
+	if err != nil {
+		br.Msg = "保存分类失败"
+		br.ErrMsg = "保存分类失败,Err:" + err.Error()
+		return
+	}
+	//todo 测试更新子集的levelPath
+	/*levelPath := ""
+	oldLevelPath := item.LevelPath
+	if item.ParentId > 0 {
+		//查找父级分类
+		parentClassify, e := material.GetMaterialClassifyById(item.ParentId)
+		if e != nil {
+			br.Msg = "获取父级分类失败"
+			br.ErrMsg = "获取父级分类失败,Err:" + e.Error()
+			return
+		}
+		levelPath = fmt.Sprintf("%s,%d", parentClassify.LevelPath, item.ClassifyId)
+		tmpList, e := material.GetMaterialClassifyByLevelPath(oldLevelPath)
+		if e != nil {
+			br.Msg = "保存分类失败"
+			br.ErrMsg = "保存分类失败,Err:" + e.Error()
+			return
+		}
+		// 把原先的父级levePath,替换成最新的父级序列
+		for _, tmp := range tmpList {
+			tmp.LevelPath = strings.Replace(tmp.LevelPath, oldLevelPath, levelPath, -1)
+			tmp.ModifyTime = time.Now()
+			e = tmp.Update([]string{"LevelPath", "ModifyTime"})
+			if e != nil {
+				br.Msg = "保存分类失败"
+				br.ErrMsg = "保存分类失败,Err:" + e.Error()
+				return
+			}
+		}
+	} else {
+		// 只有更改了父级才需要更新levelPath
+	}*/
+
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// DeleteMaterialClassifyCheck
+// @Title 删除素材库检测接口
+// @Description 删除素材库检测接口
+// @Param	request	body data_manage.ChartClassifyDeleteCheckResp true "type json string"
+// @Success 200 Ret=200 检测成功
+// @router /classify/del/check [post]
+func (this *MaterialController) DeleteMaterialClassifyCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req material.MaterialClassifyDeleteCheckReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+	var deleteStatus int
+	var tipsMsg string
+	//删除分类
+	// 查询当前的分类
+	classifyInfo, err := material.GetMaterialClassifyById(req.ClassifyId)
+	if err != nil {
+		br.Msg = "分类不存在"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	// 查找当前分类以及子分类, 截取分类ID之后的字符串
+	// 获取所有子分类
+	childList, e := material.GetMaterialClassifyByLevelPath(classifyInfo.LevelPath)
+	if e != nil {
+		err = fmt.Errorf("保存分类失败,Err:" + e.Error())
+		return
+	}
+	classifyIds := make([]int, 0)
+	if len(childList) > 0 {
+		for _, item := range childList {
+			classifyIds = append(classifyIds, item.ClassifyId)
+		}
+	}
+
+	if len(classifyIds) > 0 {
+		count, e := material.GetMaterialInfoCountByClassifyIds(classifyIds)
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有指标失败,Err:" + e.Error()
+			return
+		}
+
+		if count > 0 {
+			deleteStatus = 1
+			tipsMsg = "该分类下关联素材库不可删除"
+		} else {
+			if len(classifyIds) > 1 {
+				deleteStatus = 2
+				tipsMsg = "确认删除当前目录及包含的子目录吗"
+			}
+		}
+	}
+
+	if deleteStatus == 0 {
+		tipsMsg = "可删除,进行删除操作"
+	}
+	if this.Lang == utils.EnLangVersion {
+		if utils.ViperConfig.InConfig(tipsMsg) {
+			tipsMsg = utils.ViperConfig.GetString(tipsMsg)
+		}
+	}
+	resp := new(material.MaterialClassifyDeleteCheckResp)
+	resp.DeleteStatus = deleteStatus
+	resp.TipsMsg = tipsMsg
+	br.Ret = 200
+	br.Msg = "检测成功"
+	br.Success = true
+	br.Data = resp
+}
+
+// DeleteMaterialClassify
+// @Title 删除素材库分类/素材库
+// @Description 删除素材库分类/素材库接口
+// @Param	request	body data_manage.DeleteChartClassifyReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /classify/del [post]
+func (this *MaterialController) DeleteMaterialClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req material.DeleteMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 查询当前的分类
+	classifyInfo, err := material.GetMaterialClassifyById(req.ClassifyId)
+	if err != nil {
+		br.Msg = "分类不存在"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	// 查找当前分类以及子分类, 截取分类ID之后的字符串
+	// 获取所有子分类
+	childList, e := material.GetMaterialClassifyByLevelPath(classifyInfo.LevelPath)
+	if e != nil {
+		err = fmt.Errorf("保存分类失败,Err:" + e.Error())
+		return
+	}
+	classifyIds := make([]int, 0)
+	if len(childList) > 0 {
+		for _, item := range childList {
+			classifyIds = append(classifyIds, item.ClassifyId)
+		}
+	}
+
+	if len(classifyIds) > 0 {
+		count, e := material.GetMaterialInfoCountByClassifyIds(classifyIds)
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有指标失败,Err:" + e.Error()
+			return
+		}
+
+		if count > 0 {
+			br.Msg = "该分类下关联素材库不可删除"
+			br.IsSendEmail = false
+			return
+		}
+
+		err = material.DeleteMaterialClassify(classifyIds)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	//删除素材库
+	/*if req.MaterialId > 0 {
+		materialInfo, err := material.GetMaterialById(req.MaterialId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "素材库已删除,请刷新页面"
+				br.ErrMsg = "指标不存在,Err:" + err.Error()
+				return
+			} else {
+				br.Msg = "删除失败"
+				br.ErrMsg = "删除失败,获取指标信息失败,Err:" + err.Error()
+				return
+			}
+		}
+		if materialInfo == nil {
+			br.Msg = "素材库已删除,请刷新页面"
+			return
+		}
+		err = materialService.DeleteMaterial(req.MaterialId)
+		if err != nil {
+			br.Msg = err.Error()
+			return
+		}
+	}*/
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// ClassifyMove
+// @Title 素材库分类移动接口
+// @Description 素材库分类移动接口
+// @Success 200 {object} data_manage.MoveChartClassifyReq
+// @router /classify/move [post]
+func (this *MaterialController) ClassifyMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req material.MoveMaterialClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id小于等于0"
+		return
+	}
+	//判断分类是否存在
+	materialClassifyInfo, err := material.GetMaterialClassifyById(req.ClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在,请刷新页面"
+			br.ErrMsg = "分类不存在,Err:" + err.Error()
+			return
+		}
+		br.Msg = "移动失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	if materialClassifyInfo.ParentId != req.ParentClassifyId {
+		count, err := material.GetMaterialClassifyNameNotSelfCount(materialClassifyInfo.ClassifyId, materialClassifyInfo.ClassifyName, req.ParentClassifyId)
+		if err != nil {
+			br.Msg = "判断名称是否已存在失败"
+			br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+			return
+		}
+		if count > 0 {
+			br.Msg = "移动失败,分类名称已存在"
+			br.IsSendEmail = false
+			return
+		}
+	}
+	err, errMsg := materialService.MoveMaterialClassify(materialClassifyInfo, &req)
+	if err != nil {
+		br.Msg = errMsg
+		if errMsg == "" {
+			br.Msg = "移动失败"
+		}
+		br.ErrMsg = err.Error()
+	}
+
+	// todo权限校验
+	//移动的是分类
+	//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+	/*updateCol := make([]string, 0)
+	if MaterialClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
+		parentChartClassifyInfo, err := material.GetMaterialClassifyById(req.ParentClassifyId)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+			return
+		}
+		MaterialClassifyInfo.ParentId = parentChartClassifyInfo.ClassifyId
+		MaterialClassifyInfo.Level = parentChartClassifyInfo.Level + 1
+		MaterialClassifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+	} else if MaterialClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId == 0 {
+		//改为一级分类
+		MaterialClassifyInfo.ParentId = req.ParentClassifyId
+		MaterialClassifyInfo.Level = 1
+		MaterialClassifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	if req.PrevId > 0 {
+		if req.PrevType == 1 {
+			//上一个节点是分类
+			//上一个兄弟节点
+			prevClassify, err := material.GetMaterialClassifyById(req.PrevId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+				return
+			}
+
+			//如果是移动在两个兄弟节点之间
+			if req.NextId > 0 {
+				if req.NextType == 1 {
+					//上一个节点是分类 下一个节点是分类的情况
+					//下一个兄弟节点
+					nextClassify, err := material.GetMaterialClassifyById(req.NextId)
+					if err != nil {
+						br.Msg = "移动失败"
+						br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+						return
+					}
+					//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+					if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == MaterialClassifyInfo.Sort {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 2`
+						_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+						_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+					} else {
+						//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+						if nextClassify.Sort-prevClassify.Sort == 1 {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 1`
+							_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+						}
+					}
+				} else {
+					//上一个节点是分类 下一个节点是素材库的情况
+					//下一个兄弟节点
+					nextChartInfo, err := material.GetMaterialById(req.NextId)
+					if err != nil {
+						br.Msg = "移动失败"
+						br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+						return
+					}
+					//如果上一个兄弟(分类)与下一个兄弟(素材库)的排序权重是一致的,那么需要将下一个兄弟(素材库)(以及下个兄弟(素材库)的同样排序权重)的排序权重+2,自己变成上一个兄弟(分类)的排序权重+1
+					if prevClassify.Sort == nextChartInfo.Sort || prevClassify.Sort == MaterialClassifyInfo.Sort {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 2`
+						_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+						_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+					} else {
+						//如果下一个兄弟(素材库)的排序权重正好是上个兄弟节点(分类)的下一层,那么需要再加一层了
+						if nextChartInfo.Sort-prevClassify.Sort == 1 {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 1`
+							_ = material.UpdateMaterialClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.ClassifyId, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevClassify.ClassifyId, prevClassify.Sort, 0, updateSortStr)
+						}
+					}
+				}
+
+			}
+
+			MaterialClassifyInfo.Sort = prevClassify.Sort + 1
+			MaterialClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+
+		} else {
+			//上一个节点是素材库
+			prevMaterial, err := material.GetMaterialById(req.PrevId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+				return
+			}
+
+			//如果是移动在两个兄弟节点之间
+			if req.NextId > 0 {
+				if req.NextType == 1 {
+					//上一个节点是素材库 下一个节点是分类的情况
+					//下一个兄弟节点
+					nextClassify, err := material.GetMaterialClassifyById(req.NextId)
+					if err != nil {
+						br.Msg = "移动失败"
+						br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+						return
+					}
+					//如果上一个兄弟(素材库)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(素材库)的排序权重+1
+					if prevMaterial.Sort == nextClassify.Sort || prevMaterial.Sort == MaterialClassifyInfo.Sort {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 2`
+						_ = material.UpdateMaterialClassifySortByParentId(prevMaterial.ClassifyId, 0, prevMaterial.Sort, updateSortStr)
+						_ = material.UpdateMaterialSortByClassifyId(prevMaterial.ClassifyId, prevMaterial.Sort, prevMaterial.MaterialId, updateSortStr)
+					} else {
+						//如果下一个兄弟(分类)的排序权重正好是上个兄弟(素材库)节点的下一层,那么需要再加一层了
+						if nextClassify.Sort-prevMaterial.Sort == 1 {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 1`
+							_ = material.UpdateMaterialClassifySortByParentId(prevMaterial.ClassifyId, 0, prevMaterial.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevMaterial.ClassifyId, prevMaterial.Sort, prevMaterial.MaterialId, updateSortStr)
+						}
+					}
+				} else {
+					//上一个节点是素材库 下一个节点是素材库的情况
+					//下一个兄弟节点
+					nextChartInfo, err := material.GetMaterialById(req.NextId)
+					if err != nil {
+						br.Msg = "移动失败"
+						br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+						return
+					}
+					//如果上一个兄弟(素材库)与下一个兄弟(分类)的排序权重是一致的,那么需要将下一个兄弟(分类)(以及下个兄弟(分类)的同样排序权重)的排序权重+2,自己变成上一个兄弟(素材库)的排序权重+1
+					if prevMaterial.Sort == nextChartInfo.Sort || prevMaterial.Sort == MaterialClassifyInfo.Sort {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 2`
+						_ = material.UpdateMaterialClassifySortByParentId(prevMaterial.ClassifyId, 0, prevMaterial.Sort, updateSortStr)
+						_ = material.UpdateMaterialSortByClassifyId(prevMaterial.ClassifyId, prevMaterial.Sort, prevMaterial.MaterialId, updateSortStr)
+					} else {
+						//如果下一个兄弟(分类)的排序权重正好是上个兄弟(素材库)节点的下一层,那么需要再加一层了
+						if nextChartInfo.Sort-prevMaterial.Sort == 1 {
+							//变更兄弟节点的排序
+							updateSortStr := `sort + 1`
+							_ = material.UpdateMaterialClassifySortByParentId(prevMaterial.ClassifyId, 0, prevMaterial.Sort, updateSortStr)
+							_ = material.UpdateMaterialSortByClassifyId(prevMaterial.ClassifyId, prevMaterial.Sort, prevMaterial.MaterialId, updateSortStr)
+						}
+					}
+				}
+
+			}
+			MaterialClassifyInfo.Sort = prevMaterial.Sort + 1
+			MaterialClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+
+		}
+
+	} else {
+		firstClassify, err := material.GetFirstMaterialClassifyByParentId(MaterialClassifyInfo.ParentId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+			return
+		}
+
+		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+		if firstClassify != nil && firstClassify.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = material.UpdateMaterialClassifySortByParentId(firstClassify.ParentId, firstClassify.ClassifyId-1, 0, updateSortStr)
+		}
+
+		MaterialClassifyInfo.Sort = 0 //那就是排在第一位
+		MaterialClassifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	}
+
+	//更新
+	if len(updateCol) > 0 {
+		err = MaterialClassifyInfo.Update(updateCol)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "修改失败,Err:" + err.Error()
+			return
+		}
+		// todo 记录整个层级的分类ID
+	}*/
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}
+
+// List
+// @Title 素材列表接口
+// @Description 素材列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   int  true       "分类id"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartListResp
+// @router /list [get]
+func (this *MaterialController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	classifyId, _ := this.GetInt("ClassifyId")
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("Keyword")
+	keyword = strings.Trim(keyword, " ")
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	classifyIds := make([]string, 0)
+	childClassifyMap := make(map[int]*material.MaterialClassify)
+	if classifyId > 0 {
+		classifyIds = append(classifyIds, strconv.Itoa(classifyId))
+		// 查询当前的分类
+		classifyInfo, err := material.GetMaterialClassifyById(classifyId)
+		if err != nil {
+			br.Msg = "分类不存在"
+			br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+			return
+		}
+		// 获取所有子分类
+		childList, e := material.GetMaterialClassifyByLevelPath(classifyInfo.LevelPath)
+		if e != nil {
+			err = fmt.Errorf("保存分类失败,Err:" + e.Error())
+			return
+		}
+		// 把原先的父级levePath,替换成最新的父级序列
+		classifyIdMap := make(map[string]struct{})
+		for _, tmp := range childList {
+			childClassifyMap[tmp.ClassifyId] = tmp
+			//获取字符串前缀的位置
+			after, _ := strings.CutPrefix(tmp.LevelPath, classifyInfo.LevelPath)
+			fmt.Println("after", after)
+			// 拼接字符串
+			if after != "" {
+				ids := strings.Split(after, ",")
+				for _, v := range ids {
+					if _, ok := classifyIdMap[v]; !ok {
+						classifyIds = append(classifyIds, v)
+						classifyIdMap[v] = struct{}{}
+					}
+				}
+			}
+		}
+	}
+
+	if len(classifyIds) > 0 {
+		condition += " AND classify_id IN(" + utils.GetOrmInReplace(len(classifyIds)) + ") "
+		pars = append(pars, classifyIds)
+	}
+
+	if keyword != "" {
+		// 将关键词按照空格拆分,并处理查询
+		keywordList := strings.Split(keyword, " ")
+		switch this.Lang {
+		case utils.LANG_EN:
+			if len(keywordList) == 1 {
+				condition += ` AND  ( material_name_en LIKE '%` + keyword + `%' )`
+			} else {
+				condition += ` AND  (`
+				for _, key := range keywordList {
+					condition += ` material_name_en LIKE '%` + key + `%' AND`
+				}
+				condition = strings.TrimSuffix(condition, "AND")
+				condition += ` )`
+			}
+		default:
+			if len(keywordList) == 1 {
+				condition += ` AND  ( material_name LIKE '%` + keyword + `%' )`
+			} else {
+				condition += ` AND  (`
+				for _, key := range keywordList {
+					condition += ` material_name LIKE '%` + key + `%' AND`
+				}
+				condition = strings.TrimSuffix(condition, "AND")
+				condition += ` )`
+			}
+		}
+	}
+
+	//只看我的
+	isShowMe, _ := this.GetBool("IsShowMe")
+	if isShowMe {
+		condition += ` AND sys_user_id = ? `
+		pars = append(pars, sysUser.AdminId)
+	}
+
+	//获取图表信息
+	list, err := material.GetMaterialListPageByCondition(condition, pars, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Success = true
+		br.Msg = "获取素材库信息失败"
+		br.ErrMsg = "获取素材库信息失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(material.MaterialListResp)
+	if list == nil || len(list) <= 0 || (err != nil && err.Error() == utils.ErrNoRow()) {
+		items := make([]*material.MaterialListItems, 0)
+		resp.Paging = page
+		resp.List = items
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	dataCount, err := material.GetMaterialListCountByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标数据总数失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, dataCount)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// SaveAsMaterial
+// @Title 将图表等封面上传至素材库
+// @Description 将图表等封面上传至素材库
+// @Param	request	body material.AddAndEditSandbox true "type json string"
+// @Success 200 {object} material.Material
+// @router /saveAs [post]
+func (this *MaterialController) SaveAsMaterial() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.SaveAsMaterialReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.MaterialName = strings.Trim(req.MaterialName, " ")
+	if req.MaterialName == "" {
+		br.Msg = "请填写图片名称"
+		return
+	}
+	var exist *material.Material
+	switch this.Lang {
+	case utils.LANG_EN:
+		exist, err = material.GetMaterialByNameEn(req.MaterialName)
+	default:
+		// 判断名称是否重复
+		exist, err = material.GetMaterialByName(req.MaterialName)
+	}
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	if err == nil && exist.MaterialId > 0 {
+		br.Msg = "图片名称已存在"
+		return
+	}
+	err, errMsg := materialService.AddToMaterial(req, sysUser.AdminId, sysUser.RealName)
+	if err != nil {
+		br.Msg = "保存失败!"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	msg := "保存成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// MyChartSaveAsMaterial
+// @Title 将我的图表等封面上传至素材库
+// @Description 将图表等封面上传至素材库
+// @Param	request	body material.AddAndEditSandbox true "type json string"
+// @Success 200 {object} material.Material
+// @router /my_chart/saveAs [post]
+func (this *MaterialController) MyChartSaveAsMaterial() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.MyChartSaveAsMaterialReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	materialNames := make([]string, 0)
+	namesMap := make(map[string]struct{})
+	for _, v := range req.MaterialList {
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+		v.MaterialName = strings.Trim(v.MaterialName, " ")
+		if v.MaterialName == "" {
+			br.Msg = "请填写图片名称"
+			return
+		}
+
+		if v.ChartInfoId <= 0 {
+			br.Msg = "请选择图表"
+			return
+		}
+		if v.MyChartId <= 0 {
+			br.Msg = "请选择我的图表"
+			return
+		}
+		if _, ok := namesMap[v.MaterialName]; ok {
+			br.Msg = "图片名称不能重复"
+			return
+		}
+		namesMap[v.MaterialName] = struct{}{}
+		materialNames = append(materialNames, v.MaterialName)
+	}
+	if len(materialNames) == 0 {
+		br.Msg = "请填写图片名称"
+		return
+	}
+	existList := make([]*material.Material, 0)
+	existNameMap := make(map[string]struct{})
+	switch this.Lang {
+	case utils.LANG_EN:
+		existList, err = material.GetMaterialByNameEns(materialNames)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		if len(existList) > 0 {
+			for _, v := range existList {
+				existNameMap[v.MaterialNameEn] = struct{}{}
+			}
+		}
+	default:
+		// 判断文件名是否已存在
+		existList, err = material.GetMaterialByNames(materialNames)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		if len(existList) > 0 {
+			for _, v := range existList {
+				existNameMap[v.MaterialName] = struct{}{}
+			}
+		}
+	}
+	if len(existList) > 0 {
+		br.Msg = "图片名称已存在"
+		respData := materialService.GetMyChartExistMaterialNameListMsg(existNameMap, req.MaterialList)
+		br.Data = respData
+		return
+	}
+	if len(req.MaterialList) > 30 {
+		br.Msg = "最多支持选择30个图表"
+		return
+	}
+
+	err, errMsg := materialService.MyChartAddToMaterial(req, sysUser.AdminId, sysUser.RealName)
+	if err != nil {
+		br.Msg = "保存失败!"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	msg := "保存成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// Delete
+// @Title 删除素材库
+// @Description 删除素材库接口
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /del [post]
+func (this *MaterialController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.DeleteMaterial
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.MaterialId <= 0 {
+		br.Msg = "缺少素材库编号"
+		return
+	}
+
+	//删除素材库
+	err = material.DeleteByMaterialIds([]int{req.MaterialId})
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "操作失败,Err:" + err.Error()
+		return
+	}
+
+	msg := "删除成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// BatchDelete
+// @Title 批量删除素材库
+// @Description 删除素材库接口
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /batch/del [post]
+func (this *MaterialController) BatchDelete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.BatchDeleteMaterialReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	deleteMaterialIds := make([]int, 0)
+	if req.IsSelectAll {
+		classifyId := req.ClassifyId
+		keyword := req.Keyword
+		isShowMe := req.IsShowMe
+		//获取图表信息
+		list, e, msg := materialService.GetBatchSelectedMaterialList(classifyId, keyword, isShowMe, sysUser, this.Lang)
+		if e != nil {
+			br.Msg = "获取素材库信息失败"
+			if msg != "" {
+				br.Msg = msg
+			}
+			br.ErrMsg = "获取素材库信息失败,Err:" + e.Error()
+			return
+		}
+		notSelectIds := make(map[int]struct{})
+		if len(req.MaterialIds) >= 0 {
+			for _, v := range req.MaterialIds {
+				notSelectIds[v] = struct{}{}
+			}
+		}
+		for _, v := range list {
+			if _, ok := notSelectIds[v.MaterialId]; !ok {
+				deleteMaterialIds = append(deleteMaterialIds, v.MaterialId)
+			}
+		}
+	} else {
+		deleteMaterialIds = req.MaterialIds
+	}
+
+	if len(deleteMaterialIds) <= 0 {
+		br.Msg = "请选择删除的素材"
+		return
+	}
+
+	//删除素材库
+	err = material.DeleteByMaterialIds(deleteMaterialIds)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+
+	msg := "删除成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// GetMaterialVersionDetail
+// @Title 获取素材库版本数据详情(已保存的)
+// @Description 获取素材库版本数据详情接口(已保存的)
+// @Param   SandboxVersionCode   query   string  true       "素材库版本code"
+// @Success 200 {object} material.MaterialVersion
+// @router /detail [get]
+func (this *MaterialController) GetMaterialDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	sandboxId, _ := this.GetInt("SandboxId")
+	if sandboxId == 0 {
+		br.Msg = "缺少素材库Id"
+		return
+	}
+
+	//获取素材库数据详情(已保存的)
+	materialInfo, err := material.GetMaterialById(sandboxId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	msg := "获取成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+	br.Data = materialInfo
+}
+
+// MaterialClassifyList
+// @Title 获取所有素材库分类接口-不包含素材库
+// @Description 获取所有素材库分类接口-不包含素材库
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /classify/list [get]
+func (this *MaterialController) MaterialClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	resp := new(material.MaterialClassifyListResp)
+
+	rootList, err := material.GetMaterialClassifyByParentId(0)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := material.GetMaterialClassifyAll()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	nodeAll := make([]*material.MaterialClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		materialService.MaterialClassifyItemsMakeTree(this.SysUser, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// BatchAdd
+// @Title 批量新增素材
+// @Description 新增/编辑保存素材库接口
+// @Param	request	body material.AddAndEditSandbox true "type json string"
+// @Success 200 {object} material.Material
+// @router /batch/add [post]
+func (this *MaterialController) BatchAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.BatchAddMaterialReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	classifyId := req.ClassifyId
+	if classifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	materialNames := make([]string, 0)
+	namesMap := make(map[string]struct{})
+	for _, v := range req.MaterialList {
+		if v.MaterialName == "" {
+			br.Msg = "请填写图片名称"
+			return
+		}
+		if v.ImgUrl == "" {
+			br.Msg = "请上传图片"
+			return
+		}
+		if _, ok := namesMap[v.MaterialName]; ok {
+			br.Msg = "图片名称不能重复"
+			return
+		}
+		namesMap[v.MaterialName] = struct{}{}
+		materialNames = append(materialNames, v.MaterialName)
+	}
+	if len(materialNames) == 0 {
+		br.Msg = "请填写图片名称"
+		return
+	}
+	existList := make([]*material.Material, 0)
+	switch this.Lang {
+	case utils.LANG_EN:
+		existList, err = material.GetMaterialByNameEns(materialNames)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		if len(existList) > 0 {
+			msg := "图片名称:"
+			for _, v := range existList {
+				msg += v.MaterialNameEn + " "
+			}
+			br.Msg = fmt.Sprintf("%s 已存在", msg)
+			return
+		}
+	default:
+		// 判断文件名是否已存在
+		existList, err = material.GetMaterialByNames(materialNames)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		if len(existList) > 0 {
+			msg := "图片名称:"
+			for _, v := range existList {
+				msg += v.MaterialName + " "
+			}
+			br.Msg = fmt.Sprintf("%s 已存在", msg)
+			return
+		}
+	}
+	err = materialService.BatchAddMaterial(req.MaterialList, classifyId, sysUser.AdminId, sysUser.RealName)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+	msg := "添加成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// Upload
+// @Title 上传素材
+// @Description 上传素材接口
+// @Param	request	body material.AddAndEditSandbox true "type json string"
+// @Success 200 {object} material.Material
+// @router /upload [post]
+func (this *MaterialController) Upload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	f, h, err := this.GetFile("file")
+	if err != nil {
+		br.Msg = "获取资源信息失败"
+		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+		return
+	}
+	defer f.Close() //关闭上传文件
+
+	// 不依赖于文件扩展名检查文件格式
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	//pass := filetype.IsImage(fileData)
+	//if !pass {
+	//	br.Msg = "文件格式有误"
+	//	br.ErrMsg = "文件格式有误"
+	//	return
+	//}
+	ext := path.Ext(h.Filename)
+	if !utils.IsValidType(fileData, []utils.SourceType{
+		utils.Image,
+	}, []string{
+		"jpg",
+		"png",
+	}, ext) {
+		br.Msg = "文件格式不支持"
+		br.ErrMsg = "文件格式不支持"
+		return
+	}
+	randStr := utils.GetRandStringNoSpecialChar(28)
+	fileName := randStr + ext
+
+	// 上传到阿里云
+	ossDir := utils.RESOURCE_DIR + "material_dir/"
+	savePath := ossDir + time.Now().Format("200601/20060102/") + fileName
+	// 上传文件
+	resourceUrl, err := services.CommonUploadToOssAndFileName(h, fileName, savePath)
+	if err != nil {
+		br.Msg = "文件上传失败"
+		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(models.UploadImgResp)
+	resp.ResourceUrl = resourceUrl
+
+	br.Msg = "上传成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+}
+
+// BatchChangeClassify
+// @Title 批量更换分类
+// @Description 批量更换分类
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /batch/changeClassify [post]
+func (this *MaterialController) BatchChangeClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.BatchChangeClassifyMaterialReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.NewClassifyId <= 0 {
+		br.Msg = "请选择新的分类"
+		return
+	}
+	updateMaterialIds := make([]int, 0)
+	if req.IsSelectAll {
+		classifyId := req.ClassifyId
+		keyword := req.Keyword
+		isShowMe := req.IsShowMe
+		//获取图表信息
+		list, e, msg := materialService.GetBatchSelectedMaterialList(classifyId, keyword, isShowMe, sysUser, this.Lang)
+		if e != nil {
+			br.Msg = "获取素材库信息失败"
+			if msg != "" {
+				br.Msg = msg
+			}
+			br.ErrMsg = "获取素材库信息失败,Err:" + e.Error()
+			return
+		}
+		notSelectIds := make(map[int]struct{})
+		if len(req.MaterialIds) >= 0 {
+			for _, v := range req.MaterialIds {
+				notSelectIds[v] = struct{}{}
+			}
+		}
+		for _, v := range list {
+			if _, ok := notSelectIds[v.MaterialId]; !ok {
+				updateMaterialIds = append(updateMaterialIds, v.MaterialId)
+			}
+		}
+	} else {
+		updateMaterialIds = req.MaterialIds
+	}
+
+	if len(updateMaterialIds) <= 0 {
+		br.Msg = "请选择变更的素材"
+		return
+	}
+
+	// 判断新分类是否存在
+	_, err = material.GetMaterialClassifyById(req.NewClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在"
+			return
+		}
+		br.Msg = "获取分类信息失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+	}
+	//更换分类素材库
+	err = material.UpdateClassifyByMaterialIds(updateMaterialIds, req.NewClassifyId, time.Now())
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+
+	msg := "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// ChangeClassify
+// @Title 更换分类
+// @Description 批量更换分类
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /changeClassify [post]
+func (this *MaterialController) ChangeClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.ChangeClassifyMaterialReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.MaterialId <= 0 {
+		br.Msg = "缺少素材库编号"
+		return
+	}
+	if req.NewClassifyId <= 0 {
+		br.Msg = "请选择新的分类"
+		return
+	}
+
+	// 判断素材是否存在
+	info, err := material.GetMaterialById(req.MaterialId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "素材不存在"
+			return
+		}
+		br.Msg = "获取素材库信息失败"
+		br.ErrMsg = "获取素材库信息失败,Err:" + err.Error()
+		return
+	}
+
+	if req.NewClassifyId == info.ClassifyId {
+		br.Msg = "请选择不同的分类"
+		return
+	}
+
+	// 判断新分类是否存在
+	_, err = material.GetMaterialClassifyById(req.NewClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在"
+			return
+		}
+		br.Msg = "获取分类信息失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+	}
+	info.ClassifyId = req.NewClassifyId
+	info.ModifyTime = time.Now()
+
+	//更换分类素材库
+	err = info.Update([]string{"ClassifyId", "ModifyTime"})
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+
+	msg := "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// Rename
+// @Title 素材重命名
+// @Description 素材重命名
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /rename [post]
+func (this *MaterialController) Rename() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req material.RenameMaterialReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.MaterialId <= 0 {
+		br.Msg = "缺少素材库编号"
+		return
+	}
+	req.MaterialName = strings.Trim(req.MaterialName, " ")
+	if req.MaterialName == "" {
+		br.Msg = "请填写图片名称"
+		return
+	}
+	// 判断素材是否存在
+	info, err := material.GetMaterialById(req.MaterialId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "素材不存在"
+			return
+		}
+		br.Msg = "获取素材库信息失败"
+		br.ErrMsg = "获取素材库信息失败,Err:" + err.Error()
+		return
+	}
+	var exist *material.Material
+	updateStr := make([]string, 0)
+	switch this.Lang {
+	case utils.LANG_EN:
+		// 判断名称是否重复
+		if info.MaterialNameEn == req.MaterialName {
+			br.Msg = "名称未修改"
+			return
+		}
+		exist, err = material.GetMaterialByNameEn(req.MaterialName)
+		info.MaterialNameEn = req.MaterialName
+		updateStr = append(updateStr, "MaterialNameEn")
+	default:
+		// 判断名称是否重复
+		if info.MaterialName == req.MaterialName {
+			br.Msg = "名称未修改"
+			return
+		}
+		exist, err = material.GetMaterialByName(req.MaterialName)
+		info.MaterialName = req.MaterialName
+		updateStr = append(updateStr, "MaterialName")
+	}
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	if err == nil && exist.MaterialId > 0 {
+		br.Msg = "图片名称已存在"
+		return
+	}
+	info.ModifyTime = time.Now()
+	updateStr = append(updateStr, "ModifyTime")
+	//更换分类素材库
+	err = info.Update(updateStr)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+
+	msg := "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// BatchDownload
+// @Title 批量下载素材
+// @Description 批量下载素材
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /batch/download [get]
+func (this *MaterialController) BatchDownload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	//var req material.BatchDeleteMaterialReq
+	materialIdStr := this.GetString("MaterialIds")
+	isSelectAll, _ := this.GetBool("IsSelectAll")
+	classifyId, _ := this.GetInt("ClassifyId")
+	keyword := this.GetString("Keyword")
+	isShowMe, _ := this.GetBool("IsShowMe")
+
+	downMaterialList := make([]*material.MaterialListItems, 0)
+	materialIds := strings.Split(materialIdStr, ",")
+	var err error
+	if isSelectAll {
+		//获取图表信息
+		list, e, msg := materialService.GetBatchSelectedMaterialList(classifyId, keyword, isShowMe, sysUser, this.Lang)
+		if e != nil {
+			br.Msg = "获取素材库信息失败"
+			if msg != "" {
+				br.Msg = msg
+			}
+			br.ErrMsg = "获取素材库信息失败,Err:" + e.Error()
+			return
+		}
+		notSelectIds := make(map[int]struct{})
+		if len(materialIds) >= 0 {
+			//转成数组
+			for _, v := range materialIds {
+				id, _ := strconv.Atoi(v)
+				notSelectIds[id] = struct{}{}
+			}
+		}
+		for _, v := range list {
+			if _, ok := notSelectIds[v.MaterialId]; !ok {
+				downMaterialList = append(downMaterialList, v)
+			}
+		}
+	} else {
+		if len(materialIds) > 0 {
+			// 批量查询指标数据
+			materialIdsInt := make([]int, 0)
+			for _, v := range materialIds {
+				id, _ := strconv.Atoi(v)
+				materialIdsInt = append(materialIdsInt, id)
+			}
+			downMaterialList, err = material.GetMaterialByIds(materialIdsInt)
+			if err != nil {
+				br.Msg = "获取素材库信息失败"
+				br.ErrMsg = "获取素材库信息失败,Err:" + err.Error()
+				return
+			}
+		}
+	}
+
+	if len(downMaterialList) <= 0 {
+		br.Msg = "请选择要下载的素材"
+		return
+	}
+	// 创建zip
+	zipName := time.Now().Format(utils.FormatDateTimeUnSpace) + utils.GetRandString(5) + ".zip"
+	savePath := zipName
+	zipFile, err := os.Create(zipName)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "生成压缩文件失败, Err: " + err.Error()
+		return
+	}
+	zipWriter := zip.NewWriter(zipFile)
+	// 生成zip过程中报错关闭
+	defer func() {
+		if err != nil {
+			_ = zipWriter.Close()
+			_ = zipFile.Close()
+		}
+		_ = os.Remove(savePath)
+	}()
+
+	// 获取资源, 写入zip
+	zipFileName := ""
+	for i := range downMaterialList {
+		if downMaterialList[i].MaterialName == "" || downMaterialList[i].ImgUrl == "" {
+			continue
+		}
+		fmt.Printf("开始压缩第%d个文件\n", i+1)
+		dotIndex := strings.LastIndex(downMaterialList[i].ImgUrl, ".")
+
+		// 如果找不到点,或者点是文件名的第一个字符(不合法情况),则返回空字符串
+		if dotIndex == -1 || dotIndex == 0 {
+			continue
+		}
+
+		fileExt := downMaterialList[i].ImgUrl[dotIndex+1:]
+		ioWriter, err := zipWriter.Create(fmt.Sprintf("%s.%s", downMaterialList[i].MaterialName, fileExt))
+		if err != nil {
+			if os.IsPermission(err) {
+				br.Msg = "操作失败"
+				br.ErrMsg = "打包权限不足, Err: " + err.Error()
+				return
+			}
+			br.Msg = "操作失败"
+			br.ErrMsg = "压缩出错, Err: " + err.Error()
+			return
+		}
+
+		var content []byte
+		content, err = http.Get(downMaterialList[i].ImgUrl)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "资源获取失败, Err: " + err.Error()
+			return
+		}
+		_, err = ioWriter.Write(content)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "压缩文件写入失败, Err: " + err.Error()
+			return
+		}
+		if zipFileName == "" {
+			zipFileName = downMaterialList[i].MaterialName
+		}
+		fmt.Printf("第%d个文件写入成功\n", i+1)
+	}
+	// 生成zip后关闭,否则下载文件会损坏
+	_ = zipWriter.Close()
+	_ = zipFile.Close()
+
+	this.Ctx.Output.Download(savePath, fmt.Sprintf("%s.zip", zipFileName))
+
+	msg := "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.Msg = msg
+}
+
+// Download
+// @Title 下载素材
+// @Description 下载素材
+// @Param	request	body material.DeleteSandbox true "type json string"
+// @Success 200 标记成功
+// @router /download [get]
+func (this *MaterialController) Download() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	materialId, _ := this.GetInt("MaterialId")
+	if materialId <= 0 {
+		br.Msg = "缺少素材库编号"
+		return
+	}
+	// 判断素材是否存在
+	info, err := material.GetMaterialById(materialId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "素材不存在"
+			return
+		}
+		br.Msg = "获取素材库信息失败"
+		br.ErrMsg = "获取素材库信息失败,Err:" + err.Error()
+		return
+	}
+	if info.ImgUrl == "" {
+		br.Msg = "素材地址为空"
+		return
+	}
+
+	fileName := info.MaterialName
+	// 查找文件名中最后一个点的位置
+	dotIndex := strings.LastIndex(info.ImgUrl, ".")
+
+	// 如果找不到点,或者点是文件名的第一个字符(不合法情况),则返回空字符串
+	if dotIndex == -1 || dotIndex == 0 {
+		br.Msg = "素材地址错误"
+		return
+	}
+
+	fileName += "." + info.ImgUrl[dotIndex+1:] // 添加扩展名到文件名
+	// 获取路径中的文件名
+	urlFileName := path.Base(info.ImgUrl)
+	uploadDir := utils.STATIC_DIR + "hongze/" + time.Now().Format("20060102")
+	if e := os.MkdirAll(uploadDir, utils.DIR_MOD); e != nil {
+		br.Msg = "存储目录创建失败"
+		br.ErrMsg = "存储目录创建失败, Err:" + e.Error()
+		return
+	}
+	var content []byte
+	content, err = http.Get(info.ImgUrl)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "资源获取失败, Err: " + err.Error()
+		return
+	}
+	filePath := uploadDir + "/" + urlFileName
+	ioWriter, err := os.Create(filePath)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "文件创建失败, Err: " + err.Error()
+		return
+	}
+	n, err := ioWriter.Write(content)
+	fmt.Println("n", n)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "压缩文件写入失败, Err: " + err.Error()
+		return
+	}
+	this.Ctx.Output.Download(filePath, fileName)
+
+	defer func() {
+		_ = os.Remove(filePath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+}

+ 133 - 110
controllers/report.go

@@ -232,112 +232,122 @@ func (this *ReportController) Delete() {
 //	br.Msg = "保存成功"
 //	br.Data = resp
 //}
-
-// Upload
-// @Title 图片上传
-// @Description 图片上传接口
-// @Param   file   query   file  true       "文件"
-// @Success 200 新增成功
-// @router /upload [post]
-func (this *ReportController) Upload() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
-	f, h, err := this.GetFile("file")
-	if err != nil {
-		br.Msg = "获取资源信息失败"
-		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
-		return
-	}
-
-	fileData, e := io.ReadAll(f)
-	if e != nil {
-		br.Msg = "上传失败"
-		br.ErrMsg = "读取文件失败, Err: " + e.Error()
-		return
-	}
-	pass := filetype.IsImage(fileData)
-	if !pass {
-		br.Msg = "文件格式有误"
-		br.ErrMsg = "文件格式有误"
-		return
-	}
-
-	ext := path.Ext(h.Filename)
-	dateDir := time.Now().Format("20060102")
-	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
-	if err != nil {
-		br.Msg = "存储目录创建失败"
-		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
-		return
-	}
-	randStr := utils.GetRandStringNoSpecialChar(28)
-	fileName := randStr + ext
-	fpath := uploadDir + "/" + fileName
-	defer f.Close() //关闭上传文件
-	err = this.SaveToFile("file", fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
-	}
-
-	resourceUrl := ``
-	//上传到阿里云 和 minio
-	//if utils.ObjectStorageClient == "minio" {
-	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//} else {
-	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//}
-	ossClient := services.NewOssClient()
-	if ossClient == nil {
-		br.Msg = "上传失败"
-		br.ErrMsg = "初始化OSS服务失败"
-		return
-	}
-	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
-	}
-
-	defer func() {
-		os.Remove(fpath)
-	}()
-
-	item := new(models.Resource)
-	item.ResourceUrl = resourceUrl
-	item.ResourceType = 1
-	item.CreateTime = time.Now()
-	newId, err := models.AddResource(item)
-	if err != nil {
-		br.Msg = "资源上传失败"
-		br.ErrMsg = "资源上传失败,Err:" + err.Error()
-		return
-	}
-	resp := new(models.ResourceResp)
-	resp.Id = newId
-	resp.ResourceUrl = resourceUrl
-	br.Msg = "上传成功"
-	br.Ret = 200
-	br.Success = true
-	br.Data = resp
-	return
-}
+//
+//// Upload
+//// @Title 图片上传
+//// @Description 图片上传接口
+//// @Param   file   query   file  true       "文件"
+//// @Success 200 新增成功
+//// @router /upload [post]
+//func (this *ReportController) Upload() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//	f, h, err := this.GetFile("file")
+//	if err != nil {
+//		br.Msg = "获取资源信息失败"
+//		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	fileData, e := io.ReadAll(f)
+//	if e != nil {
+//		br.Msg = "上传失败"
+//		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+//		return
+//	}
+//	//pass := filetype.IsImage(fileData)
+//	//if !pass {
+//	//	br.Msg = "文件格式有误"
+//	//	br.ErrMsg = "文件格式有误"
+//	//	return
+//	//}
+//
+//	ext := path.Ext(h.Filename)
+//	if !utils.IsValidType(fileData, []utils.SourceType{
+//		utils.Image,
+//	}, []string{
+//		"jpg",
+//		"png",
+//	}, ext) {
+//		br.Msg = "文件格式不支持"
+//		br.ErrMsg = "文件格式不支持"
+//		return
+//	}
+//	dateDir := time.Now().Format("20060102")
+//	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
+//	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+//	if err != nil {
+//		br.Msg = "存储目录创建失败"
+//		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+//		return
+//	}
+//	randStr := utils.GetRandStringNoSpecialChar(28)
+//	fileName := randStr + ext
+//	fpath := uploadDir + "/" + fileName
+//	defer f.Close() //关闭上传文件
+//	err = this.SaveToFile("file", fpath)
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	resourceUrl := ``
+//	//上传到阿里云 和 minio
+//	//if utils.ObjectStorageClient == "minio" {
+//	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+//	//	if err != nil {
+//	//		br.Msg = "文件上传失败"
+//	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//	//		return
+//	//	}
+//	//} else {
+//	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+//	//	if err != nil {
+//	//		br.Msg = "文件上传失败"
+//	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//	//		return
+//	//	}
+//	//}
+//	ossClient := services.NewOssClient()
+//	if ossClient == nil {
+//		br.Msg = "上传失败"
+//		br.ErrMsg = "初始化OSS服务失败"
+//		return
+//	}
+//	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	defer func() {
+//		os.Remove(fpath)
+//	}()
+//
+//	item := new(models.Resource)
+//	item.ResourceUrl = resourceUrl
+//	item.ResourceType = 1
+//	item.CreateTime = time.Now()
+//	newId, err := models.AddResource(item)
+//	if err != nil {
+//		br.Msg = "资源上传失败"
+//		br.ErrMsg = "资源上传失败,Err:" + err.Error()
+//		return
+//	}
+//	resp := new(models.ResourceResp)
+//	resp.Id = newId
+//	resp.ResourceUrl = resourceUrl
+//	br.Msg = "上传成功"
+//	br.Ret = 200
+//	br.Success = true
+//	br.Data = resp
+//	return
+//}
 
 // ClassifyIdDetail
 // @Title 根据分类获取最近一次报告详情接口
@@ -517,8 +527,23 @@ func (this *ReportUploadCommonController) UploadImg() {
 		err = fmt.Errorf("读取文件失败, Err: %s", e.Error())
 		return
 	}
-	pass := filetype.IsImage(fileData)
-	if !pass {
+	//pass := filetype.IsImage(fileData)
+	//if !pass {
+	//	kind, _ := filetype.Match(fileData)
+	//	if kind.Extension != "pdf" {
+	//		err = fmt.Errorf("文件格式有误")
+	//		return
+	//	}
+	//	fmt.Printf("File type: %s. MIME: %s\n", kind.Extension, kind.MIME.Value)
+	//}
+
+	ext := path.Ext(h.Filename)
+	if !utils.IsValidType(fileData, []utils.SourceType{
+		utils.Image,
+	}, []string{
+		"jpg",
+		"png",
+	}, ext) {
 		kind, _ := filetype.Match(fileData)
 		if kind.Extension != "pdf" {
 			err = fmt.Errorf("文件格式有误")
@@ -526,8 +551,6 @@ func (this *ReportUploadCommonController) UploadImg() {
 		}
 		fmt.Printf("File type: %s. MIME: %s\n", kind.Extension, kind.MIME.Value)
 	}
-
-	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)

+ 101 - 119
controllers/report_chapter.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/report"
+	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
@@ -332,38 +333,12 @@ func (this *ReportController) EditDayWeekChapter() {
 	}
 
 	// 操作权限校验
-	{
-		// 如果不是创建人,那么就要去查看是否授权
-		if reportInfo.AdminId != sysUser.AdminId {
-			// 授权用户权限校验
-			chapterGrantObj := report.ReportChapterGrant{}
-			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(reportChapterInfo.ReportChapterId, sysUser.AdminId)
-			if tmpErr != nil {
-				if tmpErr.Error() == utils.ErrNoRow() {
-					br.Msg = "没有权限"
-					br.ErrMsg = "没有权限"
-					br.IsSendEmail = false
-					return
-				}
-				br.Msg = "获取章节id授权用户失败"
-				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
-				return
-			}
-		}
-
-		// 标记更新中
-		{
-			markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
-			if err != nil {
-				br.Msg = err.Error()
-				return
-			}
-			if markStatus.Status == 1 {
-				br.Msg = markStatus.Msg
-				br.IsSendEmail = false
-				return
-			}
-		}
+	hasAuth, msg, errMsg, isSendEmail := checkOpPermission(sysUser, reportInfo, reportChapterInfo, true, this.Lang)
+	if !hasAuth {
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		br.IsSendEmail = isSendEmail
+		return
 	}
 
 	if reportInfo.State == 2 {
@@ -535,38 +510,12 @@ func (this *ReportController) DelChapter() {
 	}
 
 	// 操作权限校验
-	{
-		// 如果不是创建人,那么就要去查看是否授权
-		if reportInfo.AdminId != sysUser.AdminId {
-			// 授权用户权限校验
-			chapterGrantObj := report.ReportChapterGrant{}
-			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(reportChapterInfo.ReportChapterId, sysUser.AdminId)
-			if tmpErr != nil {
-				if tmpErr.Error() == utils.ErrNoRow() {
-					br.Msg = "没有权限"
-					br.ErrMsg = "没有权限"
-					br.IsSendEmail = false
-					return
-				}
-				br.Msg = "获取章节id授权用户失败"
-				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
-				return
-			}
-		}
-
-		// 标记更新中
-		{
-			markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
-			if err != nil {
-				br.Msg = err.Error()
-				return
-			}
-			if markStatus.Status == 1 {
-				br.Msg = markStatus.Msg
-				br.IsSendEmail = false
-				return
-			}
-		}
+	hasAuth, msg, errMsg, isSendEmail := checkOpPermission(sysUser, reportInfo, reportChapterInfo, true, this.Lang)
+	if !hasAuth {
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		br.IsSendEmail = isSendEmail
+		return
 	}
 
 	if reportInfo.State == 2 {
@@ -577,7 +526,7 @@ func (this *ReportController) DelChapter() {
 	}
 
 	// 删除章节
-	err, errMsg := services.DelChapter(reportInfo, reportChapterInfo, sysUser)
+	err, errMsg = services.DelChapter(reportInfo, reportChapterInfo, sysUser)
 	if err != nil {
 		br.Msg = "删除失败"
 		if errMsg != "" {
@@ -634,7 +583,7 @@ func (this *ReportController) GetReportChapterList() {
 	}
 
 	// 权限校验
-	isAuth, err := services.CheckReportAuthByReportChapterInfo(sysUser.AdminId, reportInfo.AdminId, reportId)
+	isAuth, err := services.CheckReportAuthByReportId(sysUser, reportInfo.AdminId, reportId)
 	if err != nil {
 		br.Msg = "获取报告权限失败"
 		br.ErrMsg = "获取报告权限失败,Err:" + err.Error()
@@ -762,7 +711,7 @@ func (this *ReportController) GetReportChapterList() {
 			}
 
 			// 报告章节的操作权限
-			tmpChapterItem.IsAuth = services.CheckChapterAuthByAdminIdList(sysUser.AdminId, reportInfo.AdminId, tmpChapterIdGrandList)
+			tmpChapterItem.IsAuth = services.CheckChapterAuthByAdminIdList(sysUser, reportInfo.AdminId, tmpChapterIdGrandList)
 
 			resp = append(resp, tmpChapterItem)
 		}
@@ -816,7 +765,7 @@ func (this *ReportController) GetDayWeekChapter() {
 	}
 
 	// 权限校验
-	isAuth, err := services.CheckReportAuthByReportChapterInfo(sysUser.AdminId, reportInfo.AdminId, reportInfo.Id)
+	isAuth, err := services.CheckReportAuthByReportId(sysUser, reportInfo.AdminId, reportInfo.Id)
 	if err != nil {
 		br.Msg = "获取报告权限失败"
 		br.ErrMsg = "获取报告权限失败,Err:" + err.Error()
@@ -993,24 +942,12 @@ func (this *ReportController) EditChapterTrendTag() {
 	}
 
 	// 操作权限校验
-	{
-		// 如果不是创建人,那么就要去查看是否授权
-		if reportInfo.AdminId != sysUser.AdminId {
-			// 授权用户权限校验
-			chapterGrantObj := report.ReportChapterGrant{}
-			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(chapterInfo.ReportChapterId, sysUser.AdminId)
-			if tmpErr != nil {
-				if tmpErr.Error() == utils.ErrNoRow() {
-					br.Msg = "没有权限"
-					br.ErrMsg = "没有权限"
-					br.IsSendEmail = false
-					return
-				}
-				br.Msg = "获取章节id授权用户失败"
-				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
-				return
-			}
-		}
+	hasAuth, msg, errMsg, isSendEmail := checkOpPermission(sysUser, reportInfo, chapterInfo, false, this.Lang)
+	if !hasAuth {
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		br.IsSendEmail = isSendEmail
+		return
 	}
 
 	// 更新章节标签
@@ -1136,7 +1073,7 @@ func (this *ReportController) VoiceUpload() {
 	}
 
 	// 权限校验
-	isAuth, err := services.CheckChapterAuthByReportChapterInfo(this.SysUser.AdminId, reportInfo.AdminId, reportChapterInfo)
+	isAuth, err := services.CheckChapterAuthByReportChapterInfo(this.SysUser, reportInfo.AdminId, reportChapterInfo)
 	if err != nil {
 		br.Msg = "获取报告权限失败"
 		br.ErrMsg = "获取报告权限失败,Err:" + err.Error()
@@ -1564,38 +1501,12 @@ func (this *ReportController) EditChapterTitle() {
 	}
 
 	// 操作权限校验
-	{
-		// 如果不是创建人,那么就要去查看是否授权
-		if reportInfo.AdminId != sysUser.AdminId {
-			// 授权用户权限校验
-			chapterGrantObj := report.ReportChapterGrant{}
-			_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(reportChapterInfo.ReportChapterId, sysUser.AdminId)
-			if tmpErr != nil {
-				if tmpErr.Error() == utils.ErrNoRow() {
-					br.Msg = "没有权限"
-					br.ErrMsg = "没有权限"
-					br.IsSendEmail = false
-					return
-				}
-				br.Msg = "获取章节id授权用户失败"
-				br.ErrMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
-				return
-			}
-		}
-
-		// 标记更新中
-		{
-			markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
-			if err != nil {
-				br.Msg = err.Error()
-				return
-			}
-			if markStatus.Status == 1 {
-				br.Msg = markStatus.Msg
-				br.IsSendEmail = false
-				return
-			}
-		}
+	hasAuth, msg, errMsg, isSendEmail := checkOpPermission(sysUser, reportInfo, reportChapterInfo, true, this.Lang)
+	if !hasAuth {
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		br.IsSendEmail = isSendEmail
+		return
 	}
 
 	if reportInfo.State == 2 {
@@ -1733,3 +1644,74 @@ func (this *ReportController) CancelPublishReportChapter() {
 	br.Success = true
 	br.Msg = "撤销成功"
 }
+
+// checkOpPermission
+// @Description: 操作权限校验
+// @author: Roc
+// @datetime 2024-11-12 09:58:34
+// @param sysUser *system.Admin
+// @param reportInfo *models.Report
+// @param reportChapterInfo *models.ReportChapter
+// @param isMarkStatus bool
+// @param lang string
+// @return hasAuth bool
+// @return msg string
+// @return errMsg string
+// @return isSendEmail bool
+func checkOpPermission(sysUser *system.Admin, reportInfo *models.Report, reportChapterInfo *models.ReportChapter, isMarkStatus bool, lang string) (hasAuth bool, msg, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+
+	// 权限校验
+	isAuth, err := services.CheckChapterAuthByReportChapterInfo(sysUser, reportInfo.AdminId, reportChapterInfo)
+	if err != nil {
+		msg = "获取报告权限失败"
+		errMsg = "获取报告权限失败,Err:" + err.Error()
+		return
+	}
+	if !isAuth {
+		msg = "没有权限"
+		errMsg = "没有权限"
+		isSendEmail = false
+		return
+	}
+
+	// 如果不是创建人,那么就要去查看是否授权
+	//if reportInfo.AdminId != sysUser.AdminId && !utils.IsAdminRole(sysUser.RoleTypeCode) {
+	//	// 授权用户权限校验
+	//	chapterGrantObj := report.ReportChapterGrant{}
+	//	_, tmpErr := chapterGrantObj.GetGrantByIdAndAdmin(reportChapterInfo.ReportChapterId, sysUser.AdminId)
+	//	if tmpErr != nil {
+	//		if tmpErr.Error() == utils.ErrNoRow() {
+	//			msg = "没有权限"
+	//			errMsg = "没有权限"
+	//			isSendEmail = false
+	//			return
+	//		}
+	//		msg = "获取章节id授权用户失败"
+	//		errMsg = "获取章节id授权用户失败, Err: " + tmpErr.Error()
+	//		return
+	//	}
+	//}
+
+	// 标记更新中
+	if isMarkStatus {
+		markStatus, err := services.UpdateReportEditMark(reportChapterInfo.ReportId, reportChapterInfo.ReportChapterId, sysUser.AdminId, 1, sysUser.RealName, lang)
+		if err != nil {
+			msg = err.Error()
+			errMsg = err.Error()
+			return
+		}
+		if markStatus.Status == 1 {
+			msg = markStatus.Msg
+			errMsg = markStatus.Msg
+			isSendEmail = false
+			return
+		}
+	}
+
+	// 有权限
+	hasAuth = true
+
+	return
+}
+

+ 35 - 15
controllers/report_v2.go

@@ -134,11 +134,17 @@ func (this *ReportController) ListReport() {
 		pars = append(pars, 1)
 		condition += `  AND a.state in (2,6) `
 	case 3:
-		condition += ` AND a.admin_id = ? `
-		pars = append(pars, this.SysUser.AdminId)
+		// 如果不是超管,那么就看自己有权限的
+		if !utils.IsAdminRole(this.SysUser.RoleTypeCode) {
+			condition += ` AND a.admin_id = ? `
+			pars = append(pars, this.SysUser.AdminId)
+		}
 	case 2:
-		condition += ` AND (a.admin_id = ? or b.admin_id = ?) `
-		pars = append(pars, this.SysUser.AdminId, this.SysUser.AdminId)
+		// 如果不是超管,那么就看自己有权限的
+		if !utils.IsAdminRole(this.SysUser.RoleTypeCode) {
+			condition += ` AND (a.admin_id = ? or b.admin_id = ?) `
+			pars = append(pars, this.SysUser.AdminId, this.SysUser.AdminId)
+		}
 	}
 
 	// 共享报告需要连表查询,所以需要单独写
@@ -278,6 +284,12 @@ func (this *ReportController) ListReport() {
 					continue
 				}
 
+				// 如果是超管,那么有权限
+				if utils.IsAdminRole(this.SysUser.RoleTypeCode) {
+					list[i].HasAuth = true
+					continue
+				}
+
 				// 查找授权
 				var hasAuth bool
 				grantUserMap, ok := grantMap[item.Id]
@@ -481,7 +493,12 @@ func (this *ReportController) Add() {
 	item.IsPublicPublish = req.IsPublicPublish
 	item.ReportCreateTime = time.Now()
 
-	err, errMsg := services.AddReportAndChapter(item, req.InheritReportId, req.GrantAdminIdList)
+	reportDate := time.Now()
+	t, _ := time.ParseInLocation(utils.FormatDate, req.CreateTime, time.Local)
+	if !t.IsZero() {
+		reportDate = t
+	}
+	err, errMsg := services.AddReportAndChapter(item, req.InheritReportId, req.GrantAdminIdList, reportDate)
 	if err != nil {
 		br.Msg = "保存失败"
 		if errMsg != "" {
@@ -907,18 +924,21 @@ func (this *ReportController) AuthorizedListReport() {
 	var err error
 	var total int
 
-	orCondition := `AND ( (a.is_public_publish = ? AND a.state in (2,6)) or a.admin_id = ? `
-	pars = append(pars, 1, this.SysUser.AdminId)
+	// 如果不是超管,那么只能看到有权限的报告
+	if !utils.IsAdminRole(this.SysUser.RoleTypeCode) {
+		orCondition := `AND ( (a.is_public_publish = ? AND a.state in (2,6)) or a.admin_id = ? `
+		pars = append(pars, 1, this.SysUser.AdminId)
 
-	// 当前用户有权限的报告id列表
-	num := len(grantReportIdList)
-	if num > 0 {
-		orCondition += ` OR a.id in (` + utils.GetOrmInReplace(num) + `)`
-		pars = append(pars, grantReportIdList)
-	}
-	orCondition += ` ) `
+		// 当前用户有权限的报告id列表
+		num := len(grantReportIdList)
+		if num > 0 {
+			orCondition += ` OR a.id in (` + utils.GetOrmInReplace(num) + `)`
+			pars = append(pars, grantReportIdList)
+		}
+		orCondition += ` ) `
 
-	condition += orCondition
+		condition += orCondition
+	}
 
 	total, err = models.GetReportListCountByAuthorized(condition, pars)
 	if err != nil {

+ 286 - 0
controllers/residual_analysis/residual_analysis_controller.go

@@ -0,0 +1,286 @@
+package residual_analysis
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/residual_analysis_model"
+	"eta/eta_api/services/residual_analysis_service"
+)
+
+// ResidualAnalysisController 残差分析
+type ResidualAnalysisController struct {
+	controllers.BaseAuthController
+}
+
+// ResidualAnalysisPreview
+// @Title 残差分析预览
+// @Description 残差分析预览
+// @Success 200 {object} residual_analysis_model.ResidualAnalysisResp
+// @router /residual/analysis/preview [post]
+func (this *ResidualAnalysisController) ResidualAnalysisPreview() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req residual_analysis_model.ResidualAnalysisReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.EdbInfoIdA == req.EdbInfoIdB {
+		br.Msg = "自变量指标和因变量指标不能相同"
+		br.ErrMsg = "自变量指标和因变量指标不能相同"
+		return
+	}
+
+	resp, err := residual_analysis_service.ResidualAnalysisPreview(req)
+	if err != nil {
+		br.Ret = 403
+		br.Msg = "获取失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ContrastPreview
+// @Title 对比指标预览
+// @Description 对比指标预览
+// @Success 200 {object} residual_analysis_model.ResidualAnalysisChartEdbInfoMapping
+// @router /contrast/preview [get]
+func (this *ResidualAnalysisController) ContrastPreview() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	indexCode := this.GetString("IndexCode")
+	if indexCode == "" {
+		br.Ret = 403
+		br.Msg = "IndexCode不能为空"
+		br.ErrMsg = "IndexCode不能为空"
+		return
+	}
+	startTime := this.GetString("StartTime")
+	endTime := this.GetString("EndTime")
+
+	resp, err := residual_analysis_service.ContrastPreview(indexCode, startTime, endTime)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// SaveResidualAnalysis
+// @Title 保存残差分析指标
+// @Description 保存残差分析指标
+// @Success 200
+// @router /save/residual/analysis [post]
+func (this *ResidualAnalysisController) SaveResidualAnalysis() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req residual_analysis_model.ResidualAnalysisIndexSaveReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId == 0 {
+		br.Msg = "分类id不能为空"
+		br.ErrMsg = "分类id不能为空"
+		return
+	}
+
+	if req.Source == 0 {
+		br.Msg = "来源不能为空"
+		br.ErrMsg = "来源不能为空"
+		return
+	}
+
+	err = residual_analysis_service.SaveResidualAnalysis(req, sysUser)
+	if err != nil {
+		br.Ret = 403
+		br.Msg = err.Error()
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "添加成功"
+}
+
+// SaveResidualAnalysisConfig
+// @Title 保存残差指标配置
+// @Description 保存残差指标配置
+// @Success 200
+// @router /save/residual/analysis/config [post]
+func (this *ResidualAnalysisController) SaveResidualAnalysisConfig() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req residual_analysis_model.ResidualAnalysisReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	configId, err := residual_analysis_service.SaveResidualAnalysisConfig(req, sysUser)
+	if err != nil {
+		br.Ret = 403
+		br.Msg = "保存失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "添加成功"
+	br.Data = configId
+}
+
+// ResidualAnalysisDetail
+// @Title 残差分析指标详情
+// @Description 残差分析指标详情
+// @Success 200 {object} []*data_manage.EdbInfoList
+// @router /residual/analysis/detail [get]
+func (this *ResidualAnalysisController) ResidualAnalysisDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	EdbInfoId, err := this.GetInt("EdbInfoId")
+	if err != nil {
+		br.Msg = "EdbInfoId参数异常!"
+		br.ErrMsg = "EdbInfoId参数解析失败,Err:" + err.Error()
+	}
+	if EdbInfoId <= 0 {
+		br.Msg = "EdbInfoId参数异常!"
+		br.ErrMsg = "EdbInfoId参数异常!"
+	}
+
+	resp, err := residual_analysis_service.ResidualAnalysisDetail(EdbInfoId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = err.Error()
+		br.Ret = 403
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// CheckResidualAnalysisExist
+// @Title 校验残差指标是否存在
+// @Description 校验残差指标是否存在
+// @Success 200 {object}
+// @router /check/residual/analysis/exist [get]
+func (this *ResidualAnalysisController) CheckResidualAnalysisExist() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	configId, err := this.GetInt("ConfigId")
+	if err != nil {
+		br.Msg = "EdbInfoId参数异常!"
+		br.ErrMsg = "EdbInfoId参数解析失败,Err:" + err.Error()
+	}
+	if configId <= 0 {
+		br.Msg = "ConfigId参数异常!"
+		br.ErrMsg = "ConfigId参数异常!"
+	}
+
+	resp, err := residual_analysis_service.CheckResidualAnalysisExist(configId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = err.Error()
+		br.Ret = 403
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 280 - 227
controllers/resource.go

@@ -30,113 +30,124 @@ type ResourceAuthController struct {
 	BaseAuthController
 }
 
-// @Title 图片上传
-// @Description 图片上传接口
-// @Param   file   query   file  true       "文件"
-// @Success 200 {object} models.ResourceResp
-// @router /image/upload [post]
-func (this *ResourceController) Upload() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
-	f, h, err := this.GetFile("file")
-	if err != nil {
-		br.Msg = "获取资源信息失败"
-		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
-		return
-	}
-
-	fileData, e := ioutil.ReadAll(f)
-	if e != nil {
-		br.Msg = "上传失败"
-		br.ErrMsg = "读取文件失败, Err: " + e.Error()
-		return
-	}
-	pass := filetype.IsImage(fileData)
-	if !pass {
-		br.Msg = "文件格式有误"
-		br.ErrMsg = "文件格式有误"
-		return
-	}
-
-	uploadFileName := h.Filename //上传的文件名
-	ext := path.Ext(h.Filename)
-	dateDir := time.Now().Format("20060102")
-	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
-	if err != nil {
-		br.Msg = "存储目录创建失败"
-		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
-		return
-	}
-	randStr := utils.GetRandStringNoSpecialChar(28)
-	fileName := randStr + ext
-	fpath := uploadDir + "/" + fileName
-	defer f.Close() //关闭上传文件
-	err = this.SaveToFile("file", fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
-	}
-	resourceUrl := ``
-	//上传到阿里云 和 minio
-	//if utils.ObjectStorageClient == "minio" {
-	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//} else {
-	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//}
-	ossClient := services.NewOssClient()
-	if ossClient == nil {
-		br.Msg = "上传失败"
-		br.ErrMsg = "初始化OSS服务失败"
-		return
-	}
-	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
-	}
-
-	defer func() {
-		os.Remove(fpath)
-	}()
-
-	item := new(models.Resource)
-	item.ResourceUrl = resourceUrl
-	item.ResourceType = 1
-	item.CreateTime = time.Now()
-	newId, err := models.AddResource(item)
-	if err != nil {
-		br.Msg = "资源上传失败"
-		br.ErrMsg = "资源上传失败,Err:" + err.Error()
-		return
-	}
-	resp := models.ResourceResp{
-		Id:           newId,
-		ResourceUrl:  resourceUrl,
-		ResourceName: uploadFileName,
-	}
-
-	br.Msg = "上传成功"
-	br.Ret = 200
-	br.Success = true
-	br.Data = resp
-	return
-}
+//
+//// @Title 图片上传
+//// @Description 图片上传接口
+//// @Param   file   query   file  true       "文件"
+//// @Success 200 {object} models.ResourceResp
+//// @router /image/upload [post]
+//func (this *ResourceController) Upload() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//	f, h, err := this.GetFile("file")
+//	if err != nil {
+//		br.Msg = "获取资源信息失败"
+//		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	fileData, e := ioutil.ReadAll(f)
+//	if e != nil {
+//		br.Msg = "上传失败"
+//		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+//		return
+//	}
+//	//pass := filetype.IsImage(fileData)
+//	//if !pass {
+//	//	br.Msg = "文件格式有误"
+//	//	br.ErrMsg = "文件格式有误"
+//	//	return
+//	//}
+//
+//	uploadFileName := h.Filename //上传的文件名
+//	ext := path.Ext(h.Filename)
+//	if !utils.IsValidType(fileData, []utils.SourceType{
+//		utils.Image,
+//	}, []string{
+//		"jpg",
+//		"png",
+//	}, ext) {
+//		br.Msg = "文件格式不支持"
+//		br.ErrMsg = "文件格式不支持"
+//		return
+//	}
+//	dateDir := time.Now().Format("20060102")
+//	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
+//	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+//	if err != nil {
+//		br.Msg = "存储目录创建失败"
+//		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+//		return
+//	}
+//	randStr := utils.GetRandStringNoSpecialChar(28)
+//	fileName := randStr + ext
+//	fpath := uploadDir + "/" + fileName
+//	defer f.Close() //关闭上传文件
+//	err = this.SaveToFile("file", fpath)
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//	resourceUrl := ``
+//	//上传到阿里云 和 minio
+//	//if utils.ObjectStorageClient == "minio" {
+//	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+//	//	if err != nil {
+//	//		br.Msg = "文件上传失败"
+//	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//	//		return
+//	//	}
+//	//} else {
+//	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+//	//	if err != nil {
+//	//		br.Msg = "文件上传失败"
+//	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//	//		return
+//	//	}
+//	//}
+//	ossClient := services.NewOssClient()
+//	if ossClient == nil {
+//		br.Msg = "上传失败"
+//		br.ErrMsg = "初始化OSS服务失败"
+//		return
+//	}
+//	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	defer func() {
+//		os.Remove(fpath)
+//	}()
+//
+//	item := new(models.Resource)
+//	item.ResourceUrl = resourceUrl
+//	item.ResourceType = 1
+//	item.CreateTime = time.Now()
+//	newId, err := models.AddResource(item)
+//	if err != nil {
+//		br.Msg = "资源上传失败"
+//		br.ErrMsg = "资源上传失败,Err:" + err.Error()
+//		return
+//	}
+//	resp := models.ResourceResp{
+//		Id:           newId,
+//		ResourceUrl:  resourceUrl,
+//		ResourceName: uploadFileName,
+//	}
+//
+//	br.Msg = "上传成功"
+//	br.Ret = 200
+//	br.Success = true
+//	br.Data = resp
+//	return
+//}
 
 // @Title 视频上传
 // @Description 视频上传接口
@@ -662,14 +673,24 @@ func (this *ResourceController) UploadImageBase64() {
 			br.ErrMsg = "读取文件失败, Err: " + e.Error()
 			return
 		}
-		pass := filetype.IsImage(fileData)
-		if !pass {
-			br.Msg = "文件格式有误"
-			br.ErrMsg = "文件格式有误"
-			return
-		}
+		//pass := filetype.IsImage(fileData)
+		//if !pass {
+		//	br.Msg = "文件格式有误"
+		//	br.ErrMsg = "文件格式有误"
+		//	return
+		//}
 
 		ext = path.Ext(h.Filename)
+		if !utils.IsValidType(fileData, []utils.SourceType{
+			utils.Image,
+		}, []string{
+			"jpg",
+			"png",
+		}, ext) {
+			br.Msg = "文件格式不支持"
+			br.ErrMsg = "文件格式不支持"
+			return
+		}
 		dateDir := time.Now().Format("20060102")
 		uploadDir = utils.STATIC_DIR + "hongze/" + dateDir
 		err = os.MkdirAll(uploadDir, utils.DIR_MOD)
@@ -825,123 +846,135 @@ func IsFileExist(filename string) bool {
 	return true
 }
 
-// @Title 图片上传
-// @Description 图片上传接口
-// @Param   file   query   file  true       "文件"
-// @Success 200 新增成功
-// @router /image/uploadV2 [post]
-func (this *ResourceController) UploadV2() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
-	businessType := this.Ctx.Request.Form.Get("business_type")
-	//this.Ctx.Request
-	fmt.Println("businessType:", businessType)
-	fmt.Println(this.Ctx.Request.Form)
-	fmt.Println("===========")
-	br.Data = businessType
-
-	f, h, err := this.GetFile("file")
-	if err != nil {
-		br.Msg = "获取资源信息失败"
-		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
-		return
-	}
-
-	fileData, e := ioutil.ReadAll(f)
-	if e != nil {
-		br.Msg = "上传失败"
-		br.ErrMsg = "读取文件失败, Err: " + e.Error()
-		return
-	}
-	pass := filetype.IsImage(fileData)
-	if !pass {
-		br.Msg = "文件格式有误"
-		br.ErrMsg = "文件格式有误"
-		return
-	}
-
-	ext := path.Ext(h.Filename)
-	dateDir := time.Now().Format("20060102")
-	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
-	if err != nil {
-		br.Msg = "存储目录创建失败"
-		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
-		return
-	}
-	randStr := utils.GetRandStringNoSpecialChar(28)
-	fileName := randStr + ext
-	fpath := uploadDir + "/" + fileName
-	defer f.Close() //关闭上传文件
-	err = this.SaveToFile("file", fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
-	}
-	resourceUrl := ``
-	//上传到阿里云 和 minio
-	//if utils.ObjectStorageClient == "minio" {
-	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//} else {
-	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//}
-	ossClient := services.NewOssClient()
-	if ossClient == nil {
-		br.Msg = "上传失败"
-		br.ErrMsg = "初始化OSS服务失败"
-		return
-	}
-	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
-	}
-
-	defer func() {
-		os.Remove(fpath)
-	}()
-
-	item := new(models.Resource)
-	item.ResourceUrl = resourceUrl
-	item.ResourceType = 1
-	item.CreateTime = time.Now()
-	newId, err := models.AddResource(item)
-	if err != nil {
-		br.Msg = "资源上传失败"
-		br.ErrMsg = "资源上传失败,Err:" + err.Error()
-		return
-	}
-	resp := new(models.ResourceResp)
-	resp.Id = newId
-	resp.ResourceUrl = resourceUrl
-
-	br.Msg = "上传成功"
-	br.Ret = 200
-	br.Success = true
-	//br.Data = resp
-	return
-}
+//
+//// @Title 图片上传
+//// @Description 图片上传接口
+//// @Param   file   query   file  true       "文件"
+//// @Success 200 新增成功
+//// @router /image/uploadV2 [post]
+//func (this *ResourceController) UploadV2() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		this.Data["json"] = br
+//		this.ServeJSON()
+//	}()
+//	businessType := this.Ctx.Request.Form.Get("business_type")
+//	//this.Ctx.Request
+//	fmt.Println("businessType:", businessType)
+//	fmt.Println(this.Ctx.Request.Form)
+//	fmt.Println("===========")
+//	br.Data = businessType
+//
+//	f, h, err := this.GetFile("file")
+//	if err != nil {
+//		br.Msg = "获取资源信息失败"
+//		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	fileData, e := ioutil.ReadAll(f)
+//	if e != nil {
+//		br.Msg = "上传失败"
+//		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+//		return
+//	}
+//	//pass := filetype.IsImage(fileData)
+//	//
+//	//if !pass {
+//	//	br.Msg = "文件格式有误"
+//	//	br.ErrMsg = "文件格式有误"
+//	//	return
+//	//}
+//
+//	ext := path.Ext(h.Filename)
+//	if !utils.IsValidType(fileData, []utils.SourceType{
+//		utils.Image,
+//	}, []string{
+//		"jpg",
+//		"png",
+//	}, ext) {
+//		br.Msg = "文件格式不支持"
+//		br.ErrMsg = "文件格式不支持"
+//		return
+//	}
+//	dateDir := time.Now().Format("20060102")
+//	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
+//	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
+//	if err != nil {
+//		br.Msg = "存储目录创建失败"
+//		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
+//		return
+//	}
+//	randStr := utils.GetRandStringNoSpecialChar(28)
+//	fileName := randStr + ext
+//	fpath := uploadDir + "/" + fileName
+//	defer f.Close() //关闭上传文件
+//	err = this.SaveToFile("file", fpath)
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//	resourceUrl := ``
+//	//上传到阿里云 和 minio
+//	//if utils.ObjectStorageClient == "minio" {
+//	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+//	//	if err != nil {
+//	//		br.Msg = "文件上传失败"
+//	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//	//		return
+//	//	}
+//	//} else {
+//	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+//	//	if err != nil {
+//	//		br.Msg = "文件上传失败"
+//	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//	//		return
+//	//	}
+//	//}
+//	ossClient := services.NewOssClient()
+//	if ossClient == nil {
+//		br.Msg = "上传失败"
+//		br.ErrMsg = "初始化OSS服务失败"
+//		return
+//	}
+//	resourceUrl, err = ossClient.UploadFile(fileName, fpath, "")
+//	if err != nil {
+//		br.Msg = "文件上传失败"
+//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	defer func() {
+//		os.Remove(fpath)
+//	}()
+//
+//	item := new(models.Resource)
+//	item.ResourceUrl = resourceUrl
+//	item.ResourceType = 1
+//	item.CreateTime = time.Now()
+//	newId, err := models.AddResource(item)
+//	if err != nil {
+//		br.Msg = "资源上传失败"
+//		br.ErrMsg = "资源上传失败,Err:" + err.Error()
+//		return
+//	}
+//	resp := new(models.ResourceResp)
+//	resp.Id = newId
+//	resp.ResourceUrl = resourceUrl
+//
+//	br.Msg = "上传成功"
+//	br.Ret = 200
+//	br.Success = true
+//	//br.Data = resp
+//	return
+//}
 
 // @Title 获取STSToken
 // @Description 获取STSToken
 // @Success 200 获取成功
 // @router /oss/get_sts_token [get]
-func (this *ResourceController) OssSTSToken() {
+func (this *ResourceAuthController) OssSTSToken() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
 		this.Data["json"] = br
@@ -1118,8 +1151,29 @@ func (this *ResourceController) FileUpload() {
 		return
 	}
 
-	uploadFileName := h.Filename //上传的文件名
 	ext := path.Ext(h.Filename)
+	fileData, e := io.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	if !utils.IsValidType(fileData, []utils.SourceType{
+		utils.Image,
+		utils.Document,
+		utils.Archive,
+	}, []string{
+		"jpg",
+		"png",
+		"docx",
+		"xlsx",
+		"pdf",
+	}, ext) {
+		br.Msg = "文件格式不支持"
+		br.ErrMsg = "文件格式不支持"
+		return
+	}
+	uploadFileName := h.Filename //上传的文件名
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
@@ -1171,7 +1225,6 @@ func (this *ResourceController) FileUpload() {
 		ResourceUrl:  resourceUrl,
 		ResourceName: uploadFileName,
 	}
-
 	br.Msg = "上传成功"
 	br.Ret = 200
 	br.Success = true

+ 35 - 20
controllers/sandbox/sandbox.go

@@ -2394,16 +2394,21 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		return
 	}
 	edbList := make([]*sandbox.SandboxLinkCheckItem, 0)
-	for _, v := range edbInfoList {
-		tmp := &sandbox.SandboxLinkCheckItem{
-			Id:         v.EdbInfoId,
-			Name:       v.EdbName,
-			UniqueCode: v.UniqueCode,
-			ClassifyId: v.ClassifyId,
+	for _, id := range req.EdbInfoIdList {
+		for _, v := range edbInfoList {
+			if v.EdbInfoId == id {
+				tmp := &sandbox.SandboxLinkCheckItem{
+					Id:         v.EdbInfoId,
+					Name:       v.EdbName,
+					UniqueCode: v.UniqueCode,
+					ClassifyId: v.ClassifyId,
+				}
+				edbList = append(edbList, tmp)
+			}
 		}
-		edbList = append(edbList, tmp)
 	}
 
+
 	chartList, err := data_manage.GetChartInfoByIdList(req.ChartInfoIdList)
 	if err != nil {
 		br.Msg = `获取失败`
@@ -2411,16 +2416,21 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		return
 	}
 	chartListTmp := make([]*sandbox.SandboxLinkCheckItem, 0)
-	for _, v := range chartList {
-		tmp := &sandbox.SandboxLinkCheckItem{
-			Id:         v.ChartInfoId,
-			Name:       v.ChartName,
-			UniqueCode: v.UniqueCode,
-			ClassifyId: v.ChartClassifyId,
+	for _, id := range req.ChartInfoIdList {
+		for _, v := range chartList {
+			if v.ChartInfoId == id {
+				tmp := &sandbox.SandboxLinkCheckItem{
+					Id:         v.ChartInfoId,
+					Name:       v.ChartName,
+					UniqueCode: v.UniqueCode,
+					ClassifyId: v.ChartClassifyId,
+				}
+				chartListTmp = append(chartListTmp, tmp)
+			}
 		}
-		chartListTmp = append(chartListTmp, tmp)
 	}
 
+
 	reportList, err := models.GetSimpleReportByIds(req.ReportIdList)
 	if err != nil {
 		br.Msg = `获取失败`
@@ -2428,14 +2438,19 @@ func (this *SandboxController) LinkEdbInfoCheck() {
 		return
 	}
 	reportListTmp := make([]*sandbox.SandboxLinkCheckItem, 0)
-	for _, v := range reportList {
-		tmp := &sandbox.SandboxLinkCheckItem{
-			Id:         v.Id,
-			Name:       v.Title,
-			UniqueCode: v.ReportCode,
+	for _, id := range req.ReportIdList {
+		for _, v := range reportList {
+			if v.Id == id {
+				tmp := &sandbox.SandboxLinkCheckItem{
+					Id:         v.Id,
+					Name:       v.Title,
+					UniqueCode: v.ReportCode,
+				}
+				reportListTmp = append(reportListTmp, tmp)
+			}
 		}
-		reportListTmp = append(reportListTmp, tmp)
 	}
+
 	resp.EdbInfoIdList = edbList
 	resp.ChartInfoIdList = chartListTmp
 	resp.ReportIdList = reportListTmp

+ 4 - 1
controllers/sys_department.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
+	"eta/eta_api/services/eta_forum"
 	"eta/eta_api/utils"
 	"fmt"
 	"strings"
@@ -64,6 +65,7 @@ func (this *SysDepartmentController) Add() {
 		syncData.DepartmentId = int(departmentId)
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
 	}
+	go eta_forum.DepartmentSave(int(departmentId))
 
 	br.Ret = 200
 	br.Success = true
@@ -123,6 +125,7 @@ func (this *SysDepartmentController) Edit() {
 		syncData.DepartmentId = req.DepartmentId
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
 	}
+	go eta_forum.DepartmentSave(req.DepartmentId)
 
 	br.Ret = 200
 	br.Success = true
@@ -166,7 +169,7 @@ func (this *SysDepartmentController) Delete() {
 		syncData.DepartmentId = req.DepartmentId
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
 	}
-
+	go eta_forum.DepartmentDelete(req.DepartmentId)
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "删除成功"

+ 9 - 2
controllers/sys_group.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
+	"eta/eta_api/services/eta_forum"
 	"eta/eta_api/utils"
 	"strings"
 	"time"
@@ -42,6 +43,7 @@ func (this *SysGroupController) Add() {
 		return
 	}
 	groupNameArr := strings.Split(req.GroupName, ",")
+	groupIds := make([]int, 0)
 	for _, v := range groupNameArr {
 		count, err := system.GetSysGroupCount(req.DepartmentId, v)
 		if err != nil {
@@ -68,8 +70,11 @@ func (this *SysGroupController) Add() {
 				syncData.GroupId = int(groupId)
 				_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
 			}
+			groupIds = append(groupIds, int(groupId))
 		}
 	}
+
+	go eta_forum.GroupSave(groupIds)
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "新增成功"
@@ -128,7 +133,9 @@ func (this *SysGroupController) Edit() {
 		syncData.GroupId = req.GroupId
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
 	}
-
+	groupIds := make([]int, 0)
+	groupIds = append(groupIds, req.GroupId)
+	go eta_forum.GroupSave(groupIds)
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "修改成功"
@@ -177,7 +184,7 @@ func (this *SysGroupController) Delete() {
 		syncData.GroupId = req.GroupId
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
 	}
-
+	go eta_forum.GroupDelete(req.GroupId)
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "删除成功"

+ 5 - 2
controllers/sys_role.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
+	"eta/eta_api/services/eta_forum"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -70,6 +71,8 @@ func (this *SysRoleController) Add() {
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
 	}
 
+	go eta_forum.RoleSave(int(roleId))
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "新增成功"
@@ -138,7 +141,7 @@ func (this *SysRoleController) Edit() {
 		syncData.RoleId = item.RoleId
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
 	}
-
+	go eta_forum.RoleSave(item.RoleId)
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true
@@ -194,7 +197,7 @@ func (this *SysRoleController) Delete() {
 		syncData.RoleId = role.RoleId
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
 	}
-
+	go eta_forum.RoleDelete(role.RoleId)
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true

+ 93 - 3
controllers/trade_analysis/trade_analysis.go

@@ -227,9 +227,6 @@ func (this *TradeAnalysisController) GetTradeClassifyList() {
 		// 郑商所
 		if v.Exchange == tradeAnalysisModel.TradeExchangeZhengzhou {
 			name := trade_analysis.GetZhengzhouClassifyName(v.ClassifyName)
-			if name == "" {
-				continue
-			}
 			if classifyExist[name] {
 				continue
 			}
@@ -424,3 +421,96 @@ func (this *TradeAnalysisController) GetTradeFuturesCompanyList() {
 	br.Ret = 200
 	br.Success = true
 }
+
+// GetTradeExchangeClassifyTree
+// @Title 获取交易所-品种树
+// @Description 获取交易所-品种树
+// @Param   IsTotal  query  bool  false  "是否显示全部交易所"
+// @Success 200 {object} data_manage.BaseFromTradeExchangeItem
+// @router /exchange_classify/tree [get]
+func (this *TradeAnalysisController) GetTradeExchangeClassifyTree() {
+	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
+	}
+	isTotal, _ := this.GetBool("IsTotal", false)
+
+	// 交易所
+	var cond string
+	var pars []interface{}
+	exchangeOb := new(tradeAnalysisModel.BaseFromTradeExchange)
+	if !isTotal {
+		cond += fmt.Sprintf(` AND %s = ?`, exchangeOb.Cols().AnalysisState)
+		pars = append(pars, 1)
+	}
+	exchanges, e := exchangeOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取交易所列表失败, %v", e)
+		return
+	}
+
+	// 品种
+	classifyOb := new(tradeAnalysisModel.BaseFromTradeClassify)
+	fields := []string{classifyOb.Cols().ClassifyName, classifyOb.Cols().Exchange}
+	classifies, e := classifyOb.GetClassifyItemsByCondition(``, make([]interface{}, 0), fields, "id ASC")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取品种列表失败, %v", e)
+		return
+	}
+	exchangeClassify := make(map[string][]*tradeAnalysisModel.BaseFromTradeClassify, 0)
+	for _, v := range classifies {
+		if exchangeClassify[v.Exchange] == nil {
+			exchangeClassify[v.Exchange] = make([]*tradeAnalysisModel.BaseFromTradeClassify, 0)
+		}
+		exchangeClassify[v.Exchange] = append(exchangeClassify[v.Exchange], v)
+	}
+
+	// 构建树
+	resp := make([]*tradeAnalysisModel.TradeExchangeClassifyNode, 0)
+	classifyExist := make(map[string]bool)
+	for _, v := range exchanges {
+		t := new(tradeAnalysisModel.TradeExchangeClassifyNode)
+		t.UniqueFlag = v.Exchange
+		t.NodeType = 1
+		t.NodeName = v.ExchangeName
+		t.NodeValue = v.Exchange
+		t.Children = make([]*tradeAnalysisModel.TradeExchangeClassifyNode, 0)
+		for _, c := range exchangeClassify[v.Exchange] {
+			// 郑商所
+			classifyName := c.ClassifyName
+			if v.Exchange == tradeAnalysisModel.TradeExchangeZhengzhou {
+				classifyName = trade_analysis.GetZhengzhouClassifyName(c.ClassifyName)
+				if classifyExist[classifyName] {
+					continue
+				}
+				classifyExist[classifyName] = true
+			}
+			tc := new(tradeAnalysisModel.TradeExchangeClassifyNode)
+			tc.UniqueFlag = fmt.Sprintf("%s-%s", v.Exchange, classifyName)
+			tc.ParentFlag = v.Exchange
+			tc.NodeType = 2
+			tc.NodeName = classifyName
+			tc.NodeValue = classifyName
+			t.Children = append(t.Children, tc)
+		}
+		resp = append(resp, t)
+	}
+
+	br.Data = resp
+	br.Msg = "获取成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 719 - 0
controllers/trade_analysis/trade_analysis_correlation.go

@@ -0,0 +1,719 @@
+package trade_analysis
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/excel"
+	tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
+	tradeAnalysisRequest "eta/eta_api/models/data_manage/trade_analysis/request"
+	tradeAnalysisResponse "eta/eta_api/models/data_manage/trade_analysis/response"
+	"eta/eta_api/services"
+	tradeAnalysisService "eta/eta_api/services/data/trade_analysis"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// TradeAnalysisCorrelationController 相关性分析
+type TradeAnalysisCorrelationController struct {
+	controllers.BaseAuthController
+}
+
+// Preview
+// @Title 表格预览
+// @Description 表格预览
+// @Param	request	body request.CorrelationTableResp true "type json string"
+// @Success 200 {object} response.TableResp
+// @router /correlation/preview [post]
+func (this *TradeAnalysisCorrelationController) Preview() {
+	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 tradeAnalysisRequest.CorrelationTablePreviewReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	// 校验表格配置
+	pass, tips := tradeAnalysisService.CheckAnalysisCorrelationExtraConfig(req.TableConfig)
+	if !pass {
+		br.Msg = tips
+		return
+	}
+
+	// 获取表格数据
+	tableData, e := tradeAnalysisService.GetCorrelationTableRowsDataByConfig(req.TableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格数据失败, %v", e)
+		return
+	}
+
+	// 标的指标名称
+	if req.TableConfig.BaseEdbInfoId > 0 {
+		baseEdb, e := data_manage.GetEdbInfoById(req.TableConfig.BaseEdbInfoId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取标的指标失败, %v", e)
+			return
+		}
+		if baseEdb != nil {
+			req.TableConfig.BaseEdbName = baseEdb.EdbName
+		}
+	}
+
+	resp := new(tradeAnalysisResponse.CorrelationTableResp)
+	resp.TableName = strings.TrimSpace(req.TableName)
+	resp.ClassifyId = req.ClassifyId
+	resp.TableConfig = req.TableConfig
+	resp.TableData = tableData
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Save
+// @Title 保存表格
+// @Description 保存表格
+// @Param	request	body request.CorrelationTableSaveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /correlation/save [post]
+func (this *TradeAnalysisCorrelationController) Save() {
+	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 tradeAnalysisRequest.CorrelationTableSaveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	// 校验表格配置
+	pass, tips := tradeAnalysisService.CheckAnalysisCorrelationExtraConfig(req.TableConfig)
+	if !pass {
+		br.Msg = tips
+		return
+	}
+	req.TableName = strings.TrimSpace(req.TableName)
+	if req.TableName == "" {
+		br.Msg = "请输入表格名称"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	configByte, e := json.Marshal(req.TableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格配置JSON格式化失败, %v", e)
+		return
+	}
+	extraConfig := string(configByte)
+
+	// 表格数据
+	tableData, e := tradeAnalysisService.GetCorrelationTableRowsDataByConfig(req.TableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格数据失败, %v", e)
+		return
+	}
+	contentByte, e := json.Marshal(tableData)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格数据JSON格式化失败, %v", e)
+		return
+	}
+	excelContent := string(contentByte)
+
+	// 新增表格
+	var excelId int
+	if req.ExcelInfoId <= 0 {
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + utils.GetRandString(10)
+		newExcel := &excel.ExcelInfo{
+			ExcelName:          req.TableName,
+			Source:             utils.TRADE_ANALYSIS_CORRELATION_TABLE,
+			ExcelType:          1,
+			UniqueCode:         utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
+			ExcelClassifyId:    req.ClassifyId,
+			SysUserId:          sysUser.AdminId,
+			SysUserRealName:    sysUser.RealName,
+			Content:            excelContent,
+			ExtraConfig:        extraConfig,
+			UpdateUserId:       sysUser.AdminId,
+			UpdateUserRealName: sysUser.RealName,
+			ModifyTime:         time.Now(),
+			CreateTime:         time.Now(),
+		}
+		if e := newExcel.Create(); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("新增表格失败, %v", e)
+			return
+		}
+		excelId = newExcel.ExcelInfoId
+	}
+
+	// 更新表格
+	if req.ExcelInfoId > 0 {
+		excelItem, e := excel.GetExcelInfoById(req.ExcelInfoId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "表格不存在, 请刷新页面"
+				return
+			}
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+			return
+		}
+		excelItem.ExcelName = req.TableName
+		excelItem.ExcelClassifyId = req.ClassifyId
+		excelItem.Content = excelContent
+		excelItem.ExtraConfig = extraConfig
+		excelItem.UpdateUserId = sysUser.AdminId
+		excelItem.UpdateUserRealName = sysUser.RealName
+		excelItem.ModifyTime = time.Now()
+		updateCols := []string{"ExcelName", "ExcelClassifyId", "ExtraConfig", "Content", "UpdateUserId", "UpdateUserRealName", "ModifyTime"}
+		if e = excelItem.Update(updateCols); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("更新表格信息失败, %v", e)
+			return
+		}
+		excelId = excelItem.ExcelInfoId
+	}
+	if excelId <= 0 {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格ID异常: %d", excelId)
+		return
+	}
+
+	br.Data = excelId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 表格详情
+// @Description 表格详情
+// @Param   ExcelInfoId  query  int  true  "表格ID"
+// @Success 200 {object} response.TableResp
+// @router /correlation/detail [get]
+func (this *TradeAnalysisCorrelationController) 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
+	}
+	excelId, _ := this.GetInt("ExcelInfoId", 0)
+	if excelId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID不可为空, ID: %d", excelId)
+		return
+	}
+
+	// 获取表格信息
+	excelItem, e := excel.GetExcelInfoById(excelId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+	if excelItem.Source != utils.TRADE_ANALYSIS_CORRELATION_TABLE {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("表格类型异常, Source: %d", excelItem.Source)
+		return
+	}
+	var tableConfig tradeAnalysisModel.CorrelationTableExtraConfig
+	if e = json.Unmarshal([]byte(excelItem.ExtraConfig), &tableConfig); e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+		return
+	}
+
+	// 根据配置获取表格行数据
+	tableRows := make([]*tradeAnalysisModel.CorrelationTableData, 0)
+	if excelItem.Content != "" {
+		if e = json.Unmarshal([]byte(excelItem.Content), &tableRows); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+			return
+		}
+	} else {
+		// 根据配置获取表格行数据
+		tableRows, e = tradeAnalysisService.GetCorrelationTableRowsDataByConfig(tableConfig)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取表格行数据失败, %v", e)
+			return
+		}
+	}
+
+	// 标的指标名称
+	if tableConfig.BaseEdbInfoId > 0 {
+		baseEdb, e := data_manage.GetEdbInfoById(tableConfig.BaseEdbInfoId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取标的指标失败, %v", e)
+			return
+		}
+		if baseEdb != nil {
+			tableConfig.BaseEdbName = baseEdb.EdbName
+		}
+	}
+
+	// 响应
+	resp := new(tradeAnalysisResponse.CorrelationTableResp)
+	resp.ExcelInfoId = excelItem.ExcelInfoId
+	resp.TableName = excelItem.ExcelName
+	resp.ClassifyId = excelItem.ExcelClassifyId
+	resp.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, excelItem.ModifyTime)
+	resp.TableConfig = tableConfig
+	resp.TableData = tableRows
+	resp.Button = services.GetTradeAnalysisTableOpButton(excelItem.SysUserId, sysUser.AdminId, sysUser.RoleTypeCode, false)
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// SaveAs
+// @Title 表格另存为
+// @Description 表格另存为
+// @Param	request	body request.CorrelationTableSaveAsReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /correlation/save_as [post]
+func (this *TradeAnalysisCorrelationController) SaveAs() {
+	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 tradeAnalysisRequest.CorrelationTableSaveAsReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	req.TableName = strings.TrimSpace(req.TableName)
+	if req.TableName == "" {
+		br.Msg = "请输入表格名称"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	// 原表格
+	excelItem, e := excel.GetExcelInfoById(req.ExcelInfoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "原表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+
+	// 新表格
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + utils.GetRandString(10)
+	newExcel := &excel.ExcelInfo{
+		ExcelName:          req.TableName,
+		Source:             utils.TRADE_ANALYSIS_CORRELATION_TABLE,
+		ExcelType:          1,
+		UniqueCode:         utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
+		ExcelClassifyId:    req.ClassifyId,
+		SysUserId:          sysUser.AdminId,
+		SysUserRealName:    sysUser.RealName,
+		Content:            excelItem.Content,
+		ExtraConfig:        excelItem.ExtraConfig,
+		UpdateUserId:       sysUser.AdminId,
+		UpdateUserRealName: sysUser.RealName,
+		ModifyTime:         time.Now(),
+		CreateTime:         time.Now(),
+	}
+	if e := newExcel.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("新增表格失败, %v", e)
+		return
+	}
+
+	br.Data = newExcel.ExcelInfoId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Refresh
+// @Title 刷新表格
+// @Description 刷新表格
+// @Param	request	body request.CorrelationTableRefreshReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /correlation/refresh [post]
+func (this *TradeAnalysisCorrelationController) 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 req tradeAnalysisRequest.CorrelationTableRefreshReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID有误: %d", req.ExcelInfoId)
+		return
+	}
+
+	cacheKey := fmt.Sprintf("%s_%d", utils.CACHE_EXCEL_REFRESH, req.ExcelInfoId)
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "系统处理中,请勿频繁操作"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 2*time.Minute)
+	defer func() {
+		_ = utils.Rc.Delete(cacheKey)
+	}()
+
+	// 获取表格信息
+	item, e := excel.GetExcelInfoById(req.ExcelInfoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+
+	// 获取表格内容
+	var tableConfig tradeAnalysisModel.CorrelationTableExtraConfig
+	if item.ExtraConfig == "" {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格配置为空, ExcelId: %d", item.ExcelInfoId)
+		return
+	}
+	if e = json.Unmarshal([]byte(item.ExtraConfig), &tableConfig); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格配置解析失败, ExcelId: %d, Err: %v", item.ExcelInfoId, e)
+		return
+	}
+	tableData, e := tradeAnalysisService.GetCorrelationTableRowsDataByConfig(tableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格数据失败, %v", e)
+		return
+	}
+	content, e := json.Marshal(tableData)
+	if e != nil {
+		br.Msg = "表格数据JSON格式化失败"
+		br.ErrMsg = fmt.Sprintf("表格数据JSON格式化失败, %v", e)
+		return
+	}
+	item.Content = string(content)
+
+	// 更新内容
+	updateCols := []string{"Content", "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 = "操作成功"
+}
+
+// Remove
+// @Title 删除表格
+// @Description 删除表格
+// @Param	request	body request.CorrelationTableRemoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /correlation/remove [post]
+func (this *TradeAnalysisCorrelationController) 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 req tradeAnalysisRequest.CorrelationTableRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID有误: %d", req.ExcelInfoId)
+		return
+	}
+
+	// 获取表格信息
+	excelItem, e := excel.GetExcelInfoById(req.ExcelInfoId)
+	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
+	}
+
+	// 软删
+	excelItem.IsDelete = 1
+	excelItem.UpdateUserId = sysUser.AdminId
+	excelItem.UpdateUserRealName = sysUser.RealName
+	excelItem.ModifyTime = time.Now()
+	updateCols := []string{"IsDelete", "UpdateUserId", "UpdateUserRealName", "ModifyTime"}
+	if e = excelItem.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("删除表格信息失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Download
+// @Title 下载表格
+// @Description 下载表格
+// @Param   ExcelInfoId  query  int  true  "表格ID"
+// @Success 200  下载成功
+// @router /correlation/download [get]
+func (this *TradeAnalysisCorrelationController) Download() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+			utils.FileLog.Info(fmt.Sprintf("多空分析表格-下载失败, ErrMsg: %s", br.ErrMsg))
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	excelId, _ := this.GetInt("ExcelInfoId")
+	if excelId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID有误: %d", excelId)
+		return
+	}
+
+	// 获取表格信息
+	excelItem, e := excel.GetExcelInfoById(excelId)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+
+	// 根据配置获取表格行数据
+	tableRows := make([]*tradeAnalysisModel.CorrelationTableData, 0)
+	if excelItem.Content != "" {
+		if e = json.Unmarshal([]byte(excelItem.Content), &tableRows); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+			return
+		}
+	} else {
+		// 根据配置获取表格行数据
+		var tableConfig tradeAnalysisModel.CorrelationTableExtraConfig
+		if e = json.Unmarshal([]byte(excelItem.ExtraConfig), &tableConfig); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+			return
+		}
+		tableRows, e = tradeAnalysisService.GetCorrelationTableRowsDataByConfig(tableConfig)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取表格行数据失败, %v", e)
+			return
+		}
+	}
+
+	// 生成excel
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downFile := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	sheetNew := new(xlsx.Sheet)
+	sheetNew, e = xlsxFile.AddSheet("Sheet1")
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("生成Sheet失败, %v", e)
+		return
+	}
+
+	// 第一行-表头
+	headerRow := sheetNew.AddRow()
+	headerRow.AddCell().SetString("与标的指标的相关性矩阵")
+	for _, v := range tableRows {
+		cell := headerRow.AddCell()
+		if len(v.RowsData) == 0 {
+			continue
+		}
+		// TODO:向右合并-1到-10天数, 合并不知道为啥不生效...先占位处理吧
+		mergeNum := len(v.RowsData[0].DayData) - 1
+		cell.HMerge = mergeNum
+		for i := 0; i < mergeNum; i++ {
+			headerRow.AddCell()
+		}
+		cell.Value = fmt.Sprintf("滚动相关性(%d交易日)", v.CalculateValue)
+	}
+
+	// 第二行
+	secondRow := sheetNew.AddRow()
+	secondRow.AddCell().SetString("合约持仓")
+	for _, v := range tableRows {
+		if len(v.RowsData) == 0 {
+			continue
+		}
+		rowData := v.RowsData[0]
+		for _, d := range rowData.DayData {
+			showDay := strconv.Itoa(d.Day)
+			if d.Day == 0 {
+				showDay = "最新"
+			}
+			secondRow.AddCell().SetString(showDay)
+		}
+	}
+
+	// 数据行, 把多个表格的行数据合并到一起
+	var sortIndex []int // 仅用于排序
+	indexRowsData := make(map[int]*tradeAnalysisModel.CorrelationTableRowData, 0)
+	for _, v := range tableRows {
+		for rk, rd := range v.RowsData {
+			if indexRowsData[rk] == nil {
+				indexRowsData[rk] = rd
+				sortIndex = append(sortIndex, rk)
+				continue
+			}
+			indexRowsData[rk].DayData = append(indexRowsData[rk].DayData, rd.DayData...)
+		}
+	}
+	for k := range sortIndex {
+		rd := indexRowsData[k]
+		if rd == nil {
+			continue
+		}
+		row := sheetNew.AddRow()
+		row.AddCell().SetString(rd.RowName)
+		for _, dd := range rd.DayData {
+			row.AddCell().SetString(fmt.Sprintf("%.2f", dd.DataVal))
+		}
+	}
+
+	// 保存文件
+	if e = xlsxFile.Save(downFile); e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("保存文件内容失败, %v", e)
+		return
+	}
+	defer func() {
+		_ = os.Remove(downFile)
+	}()
+	fileName := fmt.Sprintf(`%s%s.xlsx`, excelItem.ExcelName, time.Now().Format("06.01.02"))
+	this.Ctx.Output.Download(downFile, fileName)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "下载成功"
+}

+ 842 - 0
controllers/trade_analysis/trade_analysis_table.go

@@ -0,0 +1,842 @@
+package trade_analysis
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage/excel"
+	tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
+	tradeAnalysisRequest "eta/eta_api/models/data_manage/trade_analysis/request"
+	tradeAnalysisResponse "eta/eta_api/models/data_manage/trade_analysis/response"
+	"eta/eta_api/services"
+	tradeAnalysisService "eta/eta_api/services/data/trade_analysis"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// TradeAnalysisTableController 多空分析表格
+type TradeAnalysisTableController struct {
+	controllers.BaseAuthController
+}
+
+// Preview
+// @Title 表格预览
+// @Description 表格预览
+// @Param	request	body request.TablePreviewReq true "type json string"
+// @Success 200 {object} response.TableResp
+// @router /table/preview [post]
+func (this *TradeAnalysisTableController) Preview() {
+	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 tradeAnalysisRequest.TablePreviewReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	// 校验配置
+	pass, tips := tradeAnalysisService.CheckAnalysisTableExtraConfig(req.TableConfig)
+	if !pass {
+		br.Msg = tips
+		return
+	}
+
+	// 根据配置获取表格行数据
+	tableRows, e := tradeAnalysisService.GetTableRowsDataByConfig(req.TableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格行数据失败, %v", e)
+		return
+	}
+
+	// 响应
+	resp := new(tradeAnalysisResponse.TableResp)
+	resp.TableData = tableRows
+	resp.TableName = strings.TrimSpace(req.TableName)
+	resp.ClassifyId = req.ClassifyId
+	resp.TableConfig = req.TableConfig
+	resp.TableCols = make([]*tradeAnalysisModel.TradeAnalysisTableColumnItem, 0)
+	if len(req.TableCols) > 0 {
+		resp.TableCols = req.TableCols
+	}
+	if len(req.TableCols) == 0 {
+		// 获取默认的表头信息
+		colOb := new(tradeAnalysisModel.TradeAnalysisTableColumn)
+		cond := fmt.Sprintf(` AND %s = 0`, colOb.Cols().ExcelInfoId)
+		defaultCols, e := colOb.GetItemsByCondition(cond, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC", colOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取默认表头设置失败, %v", e)
+			return
+		}
+		for _, v := range defaultCols {
+			resp.TableCols = append(resp.TableCols, v.Format2Item())
+		}
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Save
+// @Title 保存表格
+// @Description 保存表格
+// @Param	request	body request.TableSaveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /table/save [post]
+func (this *TradeAnalysisTableController) Save() {
+	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 tradeAnalysisRequest.TableSaveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	// 校验配置
+	pass, tips := tradeAnalysisService.CheckAnalysisTableExtraConfig(req.TableConfig)
+	if !pass {
+		br.Msg = tips
+		return
+	}
+	req.TableName = strings.TrimSpace(req.TableName)
+	if req.TableName == "" {
+		br.Msg = "请输入表格名称"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	configByte, e := json.Marshal(req.TableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格配置JSON格式化失败, %v", e)
+		return
+	}
+	extraConfig := string(configByte)
+
+	// 表格数据
+	tableRows, e := tradeAnalysisService.GetTableRowsDataByConfig(req.TableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格行数据失败, %v", e)
+		return
+	}
+	contentByte, e := json.Marshal(tableRows)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格数据JSON格式化失败, %v", e)
+		return
+	}
+	excelContent := string(contentByte)
+
+	// 新增表格
+	var excelId int
+	if req.ExcelInfoId <= 0 {
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + utils.GetRandString(10)
+		newExcel := &excel.ExcelInfo{
+			ExcelName:          req.TableName,
+			Source:             utils.TRADE_ANALYSIS_TABLE,
+			ExcelType:          1,
+			UniqueCode:         utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
+			ExcelClassifyId:    req.ClassifyId,
+			SysUserId:          sysUser.AdminId,
+			SysUserRealName:    sysUser.RealName,
+			Content:            excelContent,
+			ExtraConfig:        extraConfig,
+			UpdateUserId:       sysUser.AdminId,
+			UpdateUserRealName: sysUser.RealName,
+			ModifyTime:         time.Now(),
+			CreateTime:         time.Now(),
+		}
+		if e := newExcel.Create(); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("新增表格失败, %v", e)
+			return
+		}
+		excelId = newExcel.ExcelInfoId
+	}
+
+	// 更新表格
+	if req.ExcelInfoId > 0 {
+		excelItem, e := excel.GetExcelInfoById(req.ExcelInfoId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "表格不存在, 请刷新页面"
+				return
+			}
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+			return
+		}
+		excelItem.ExcelName = req.TableName
+		excelItem.ExcelClassifyId = req.ClassifyId
+		excelItem.Content = excelContent
+		excelItem.ExtraConfig = extraConfig
+		excelItem.UpdateUserId = sysUser.AdminId
+		excelItem.UpdateUserRealName = sysUser.RealName
+		excelItem.ModifyTime = time.Now()
+		updateCols := []string{"ExcelName", "ExcelClassifyId", "ExtraConfig", "Content", "UpdateUserId", "UpdateUserRealName", "ModifyTime"}
+		if e = excelItem.Update(updateCols); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("更新表格信息失败, %v", e)
+			return
+		}
+		excelId = excelItem.ExcelInfoId
+	}
+	if excelId <= 0 {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格ID异常: %d", excelId)
+		return
+	}
+
+	// 表头信息
+	columnOb := new(tradeAnalysisModel.TradeAnalysisTableColumn)
+	configColumns := make([]*tradeAnalysisModel.TradeAnalysisTableColumn, 0)
+	if len(req.TableCols) > 0 {
+		for _, v := range req.TableCols {
+			t := new(tradeAnalysisModel.TradeAnalysisTableColumn)
+			t.ExcelInfoId = excelId
+			t.ColumnKey = v.ColumnKey
+			t.ColumnName = v.ColumnName
+			t.ColumnNameEn = v.ColumnNameEn
+			t.Sort = v.Sort
+			t.IsMust = v.IsMust
+			t.IsShow = v.IsShow
+			t.IsSort = v.IsSort
+			t.CreateTime = time.Now()
+			t.ModifyTime = time.Now()
+			configColumns = append(configColumns, t)
+		}
+	} else {
+		// 获取默认的表头信息
+		cond := fmt.Sprintf(` AND %s = 0`, columnOb.Cols().ExcelInfoId)
+		defaultCols, e := columnOb.GetItemsByCondition(cond, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC", columnOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取默认表头设置失败, %v", e)
+			return
+		}
+		for _, v := range defaultCols {
+			v.Id = 0
+			v.ExcelInfoId = excelId
+			v.CreateTime = time.Now()
+			v.ModifyTime = time.Now()
+			configColumns = append(configColumns, v)
+		}
+	}
+
+	// 删除并新增表头
+	{
+		cond := fmt.Sprintf(`%s = ?`, columnOb.Cols().ExcelInfoId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, excelId)
+		if e = columnOb.RemoveByCondition(cond, pars); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("删除原表头配置失败, %v", e)
+			return
+		}
+		if e = columnOb.CreateMulti(configColumns); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("批量新增表头配置失败, %v", e)
+			return
+		}
+	}
+
+	br.Data = excelId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 表格详情
+// @Description 表格详情
+// @Param   ExcelInfoId  query  int  true  "表格ID"
+// @Success 200 {object} response.TableResp
+// @router /table/detail [get]
+func (this *TradeAnalysisTableController) 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
+	}
+	excelId, _ := this.GetInt("ExcelInfoId", 0)
+	if excelId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID不可为空, ID: %d", excelId)
+		return
+	}
+
+	// 获取表格信息
+	excelItem, e := excel.GetExcelInfoById(excelId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+	if excelItem.Source != utils.TRADE_ANALYSIS_TABLE {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("表格类型异常, Source: %d", excelItem.Source)
+		return
+	}
+	var tableConfig tradeAnalysisModel.TableExtraConfig
+	if e = json.Unmarshal([]byte(excelItem.ExtraConfig), &tableConfig); e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+		return
+	}
+
+	// 表格数据
+	tableRows := make([]*tradeAnalysisModel.TableRowData, 0)
+	if excelItem.Content != "" {
+		if e = json.Unmarshal([]byte(excelItem.Content), &tableRows); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+			return
+		}
+	} else {
+		// 根据配置获取表格行数据
+		tableRows, e = tradeAnalysisService.GetTableRowsDataByConfig(tableConfig)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取表格行数据失败, %v", e)
+			return
+		}
+	}
+
+	// 响应
+	resp := new(tradeAnalysisResponse.TableResp)
+	resp.ExcelInfoId = excelItem.ExcelInfoId
+	resp.TableData = tableRows
+	resp.TableName = strings.TrimSpace(excelItem.ExcelName)
+	resp.ClassifyId = excelItem.ExcelClassifyId
+	resp.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, excelItem.ModifyTime)
+	resp.TableConfig = tableConfig
+	resp.TableCols = make([]*tradeAnalysisModel.TradeAnalysisTableColumnItem, 0)
+	// 获取表头信息
+	{
+		colOb := new(tradeAnalysisModel.TradeAnalysisTableColumn)
+		cond := fmt.Sprintf(" AND %s = ?", colOb.Cols().ExcelInfoId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, excelId)
+		configCols, e := colOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", colOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取表格表头设置失败, %v", e)
+			return
+		}
+		// 表头信息为空
+		if len(configCols) == 0 {
+			defaultCols, e := colOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC", colOb.Cols().Sort))
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("获取默认表头设置失败, %v", e)
+				return
+			}
+			configCols = defaultCols
+		}
+		for _, v := range configCols {
+			resp.TableCols = append(resp.TableCols, v.Format2Item())
+		}
+	}
+	resp.Button = services.GetTradeAnalysisTableOpButton(excelItem.SysUserId, sysUser.AdminId, sysUser.RoleTypeCode, false)
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// SaveAs
+// @Title 表格另存为
+// @Description 表格另存为
+// @Param	request	body request.TableSaveAsReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /table/save_as [post]
+func (this *TradeAnalysisTableController) SaveAs() {
+	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 tradeAnalysisRequest.TableSaveAsReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	req.TableName = strings.TrimSpace(req.TableName)
+	if req.TableName == "" {
+		br.Msg = "请输入表格名称"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	// 原表格
+	excelItem, e := excel.GetExcelInfoById(req.ExcelInfoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "原表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+
+	// 新表格
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) + "_" + utils.GetRandString(10)
+	newExcel := &excel.ExcelInfo{
+		ExcelName:          req.TableName,
+		Source:             utils.TRADE_ANALYSIS_TABLE,
+		ExcelType:          1,
+		UniqueCode:         utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + timestamp),
+		ExcelClassifyId:    req.ClassifyId,
+		SysUserId:          sysUser.AdminId,
+		SysUserRealName:    sysUser.RealName,
+		Content:            excelItem.Content,
+		ExtraConfig:        excelItem.ExtraConfig,
+		UpdateUserId:       sysUser.AdminId,
+		UpdateUserRealName: sysUser.RealName,
+		ModifyTime:         time.Now(),
+		CreateTime:         time.Now(),
+	}
+	if e := newExcel.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("新增表格失败, %v", e)
+		return
+	}
+
+	// 表头信息
+	columnOb := new(tradeAnalysisModel.TradeAnalysisTableColumn)
+	configColumns := make([]*tradeAnalysisModel.TradeAnalysisTableColumn, 0)
+	{
+		cond := fmt.Sprintf(` AND %s = ?`, columnOb.Cols().ExcelInfoId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ExcelInfoId)
+		originColumns, e := columnOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", columnOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取默认表头设置失败, %v", e)
+			return
+		}
+		for _, v := range originColumns {
+			v.Id = 0
+			v.ExcelInfoId = newExcel.ExcelInfoId
+			v.CreateTime = time.Now()
+			v.ModifyTime = time.Now()
+			configColumns = append(configColumns, v)
+		}
+	}
+	// 没找到就获取默认的表头信息
+	if len(configColumns) == 0 {
+		cond := fmt.Sprintf(` AND %s = 0`, columnOb.Cols().ExcelInfoId)
+		defaultCols, e := columnOb.GetItemsByCondition(cond, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC", columnOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取默认表头设置失败, %v", e)
+			return
+		}
+		for _, v := range defaultCols {
+			v.Id = 0
+			v.ExcelInfoId = newExcel.ExcelInfoId
+			v.CreateTime = time.Now()
+			v.ModifyTime = time.Now()
+			configColumns = append(configColumns, v)
+		}
+	}
+	// 新增表头
+	if e = columnOb.CreateMulti(configColumns); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("批量新增表头配置失败, %v", e)
+		return
+	}
+
+	br.Data = newExcel.ExcelInfoId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Refresh
+// @Title 刷新表格
+// @Description 刷新表格
+// @Param	request	body request.TableRefreshReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /table/refresh [post]
+func (this *TradeAnalysisTableController) 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 req tradeAnalysisRequest.TableRefreshReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID有误: %d", req.ExcelInfoId)
+		return
+	}
+
+	cacheKey := fmt.Sprintf("%s_%d", utils.CACHE_EXCEL_REFRESH, req.ExcelInfoId)
+	if utils.Rc.IsExist(cacheKey) {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "系统处理中,请勿频繁操作"
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 2*time.Minute)
+	defer func() {
+		_ = utils.Rc.Delete(cacheKey)
+	}()
+
+	// 获取表格信息
+	item, e := excel.GetExcelInfoById(req.ExcelInfoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+
+	// 获取表格内容
+	var tableConfig tradeAnalysisModel.TableExtraConfig
+	if item.ExtraConfig == "" {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格配置为空, ExcelId: %d", item.ExcelInfoId)
+		return
+	}
+	if e = json.Unmarshal([]byte(item.ExtraConfig), &tableConfig); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("表格配置解析失败, ExcelId: %d, Err: %v", item.ExcelInfoId, e)
+		return
+	}
+	tableData, e := tradeAnalysisService.GetTableRowsDataByConfig(tableConfig)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格行数据失败, %v", e)
+		return
+	}
+	content, e := json.Marshal(tableData)
+	if e != nil {
+		br.Msg = "表格数据JSON格式化失败"
+		br.ErrMsg = fmt.Sprintf("表格数据JSON格式化失败, %v", e)
+		return
+	}
+	item.Content = string(content)
+
+	// 更新内容
+	updateCols := []string{"Content", "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 = "操作成功"
+}
+
+// Remove
+// @Title 删除表格
+// @Description 删除表格
+// @Param	request	body request.TableRemoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /table/remove [post]
+func (this *TradeAnalysisTableController) 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 req tradeAnalysisRequest.TableRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常: %v", e)
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID有误: %d", req.ExcelInfoId)
+		return
+	}
+
+	// 获取表格信息
+	excelItem, e := excel.GetExcelInfoById(req.ExcelInfoId)
+	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
+	}
+
+	// 软删
+	excelItem.IsDelete = 1
+	excelItem.UpdateUserId = sysUser.AdminId
+	excelItem.UpdateUserRealName = sysUser.RealName
+	excelItem.ModifyTime = time.Now()
+	updateCols := []string{"IsDelete", "UpdateUserId", "UpdateUserRealName", "ModifyTime"}
+	if e = excelItem.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("删除表格信息失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Download
+// @Title 下载表格
+// @Description 下载表格
+// @Param   ExcelInfoId  query  int  true  "表格ID"
+// @Success 200  下载成功
+// @router /table/download [get]
+func (this *TradeAnalysisTableController) Download() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+			utils.FileLog.Info(fmt.Sprintf("多空分析表格-下载失败, ErrMsg: %s", br.ErrMsg))
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	excelId, _ := this.GetInt("ExcelInfoId")
+	if excelId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("表格ID有误: %d", excelId)
+		return
+	}
+
+	// 获取表格信息
+	excelItem, e := excel.GetExcelInfoById(excelId)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("获取表格信息失败, %v", e)
+		return
+	}
+
+	// 获取表头信息
+	configCols := make([]*tradeAnalysisModel.TradeAnalysisTableColumn, 0)
+	{
+		colOb := new(tradeAnalysisModel.TradeAnalysisTableColumn)
+		cond := fmt.Sprintf(" AND %s = ?", colOb.Cols().ExcelInfoId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, excelId)
+		items, e := colOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", colOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "下载失败"
+			br.ErrMsg = fmt.Sprintf("获取表格表头设置失败, %v", e)
+			return
+		}
+		configCols = items
+		// 表头信息为空
+		if len(configCols) == 0 {
+			defaultCols, e := colOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC", colOb.Cols().Sort))
+			if e != nil {
+				br.Msg = "下载失败"
+				br.ErrMsg = fmt.Sprintf("获取默认表头设置失败, %v", e)
+				return
+			}
+			configCols = defaultCols
+		}
+	}
+	if len(configCols) == 0 {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("表头信息异常")
+		return
+	}
+
+	// 表格数据
+	tableRows := make([]*tradeAnalysisModel.TableRowData, 0)
+	var tableConfig tradeAnalysisModel.TableExtraConfig
+	if excelItem.Content != "" {
+		if e = json.Unmarshal([]byte(excelItem.Content), &tableRows); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+			return
+		}
+	} else {
+		// 根据配置获取表格行数据
+		if e = json.Unmarshal([]byte(excelItem.ExtraConfig), &tableConfig); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("解析表格配置失败, err: %v, config: %s", e, excelItem.ExtraConfig)
+			return
+		}
+		tableRows, e = tradeAnalysisService.GetTableRowsDataByConfig(tableConfig)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取表格行数据失败, %v", e)
+			return
+		}
+	}
+
+	// 生成excel
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downFile := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	sheetNew := new(xlsx.Sheet)
+	sheetNew, e = xlsxFile.AddSheet("Sheet1")
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("生成Sheet失败, %v", e)
+		return
+	}
+
+	// 第一行-表头
+	headerRow := sheetNew.AddRow()
+	var headerKeys []string
+	for _, v := range configCols {
+		if v.IsShow == 0 {
+			continue
+		}
+		headerRow.AddCell().SetValue(v.ColumnName)
+		headerKeys = append(headerKeys, v.ColumnKey)
+	}
+
+	// 数据行
+	for _, v := range tableRows {
+		row := sheetNew.AddRow()
+		rv := reflect.ValueOf(v)
+
+		// v为指针先解引用
+		if rv.Kind() == reflect.Ptr {
+			rv = rv.Elem()
+		}
+
+		// 获取对应key的值
+		for _, h := range headerKeys {
+			fieldVal := rv.FieldByName(h)
+			if fieldVal.IsValid() {
+				cell := row.AddCell()
+				cell.SetString(fmt.Sprint(fieldVal.Interface()))
+				continue
+			}
+			row.AddCell().SetString("")
+		}
+	}
+
+	// 保存文件
+	if e = xlsxFile.Save(downFile); e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("保存文件内容失败, %v", e)
+		return
+	}
+	fileName := fmt.Sprintf(`%s%s.xlsx`, excelItem.ExcelName, time.Now().Format("06.01.02"))
+	this.Ctx.Output.Download(downFile, fileName)
+	defer func() {
+		_ = os.Remove(downFile)
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "下载成功"
+}

+ 8 - 4
controllers/trade_analysis/warehouse.go

@@ -220,7 +220,7 @@ func (this *WarehouseController) Preview() {
 	}
 
 	// 获取指标数据, 该图表未用实际指标, 为了统一数据格式用ChartEdbInfoMapping
-	companyTradeData, e := tradeAnalysisService.GetOriginTradeData(extraConfig.Exchange, extraConfig.ClassifyName, extraConfig.Contracts, extraConfig.Companies, extraConfig.PredictRatio)
+	companyTradeData, e := tradeAnalysisService.GetWarehouseTradeData(extraConfig.Exchange, extraConfig.ClassifyName, extraConfig.Contracts, extraConfig.Companies, extraConfig.PredictRatio)
 	if e != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = fmt.Sprintf("获取期货公司持仓加总数据失败, %v", e)
@@ -687,7 +687,7 @@ func (this *WarehouseController) Detail() {
 	}
 
 	// 获取图表数据
-	companyTradeData, e := tradeAnalysisService.GetOriginTradeData(extraConfig.Exchange, extraConfig.ClassifyName, extraConfig.Contracts, extraConfig.Companies, extraConfig.PredictRatio)
+	companyTradeData, e := tradeAnalysisService.GetWarehouseTradeData(extraConfig.Exchange, extraConfig.ClassifyName, extraConfig.Contracts, extraConfig.Companies, extraConfig.PredictRatio)
 	if e != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = fmt.Sprintf("获取期货公司持仓加总数据失败, %v", e)
@@ -1285,7 +1285,7 @@ func (this *WarehouseController) SearchByEs() {
 	sourceList := make([]int, 0)
 	sourceList = append(sourceList, utils.CHART_SOURCE_TRADE_ANALYSIS_PROCESS)
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -1338,13 +1338,17 @@ func (this *WarehouseController) SearchByEs() {
 
 		for _, v := range searchList {
 			tmp := new(data_manage.ChartInfoMore)
-			tmp.ChartInfo = *v
+			tmp.ChartInfo = v.ChartInfo
 			// 图表数据权限
 			tmp.HaveOperaAuth = true
 			//判断是否需要展示英文标识
 			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
 				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
 			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
 			finalList = append(finalList, tmp)
 		}
 	}

+ 366 - 0
models/ai_predict_model/ai_predict_model_framework.go

@@ -0,0 +1,366 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// AiPredictModelFramework 图库框架表
+type AiPredictModelFramework struct {
+	AiPredictModelFrameworkId int       `orm:"column(ai_predict_model_framework_id);pk"`
+	FrameworkCode             string    `description:"框架唯一编码"`
+	FrameworkName             string    `description:"框架名称"`
+	FrameworkImg              string    `description:"框架图片"`
+	FrameworkContent          string    `description:"框架内容"`
+	IsPublic                  int       `description:"是否公开:0-私有;1-公开"`
+	PublicTime                time.Time `description:"公开时间"`
+	Sort                      int       `description:"排序"`
+	AdminId                   int       `description:"创建人ID"`
+	AdminName                 string    `description:"创建人姓名"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"更新时间"`
+}
+
+func (m *AiPredictModelFramework) TableName() string {
+	return "ai_predict_model_framework"
+}
+
+func (m *AiPredictModelFramework) PrimaryId() string {
+	return AiPredictModelFrameworkColumns.AiPredictModelFrameworkId
+}
+
+var AiPredictModelFrameworkColumns = struct {
+	AiPredictModelFrameworkId string
+	FrameworkCode             string
+	FrameworkName             string
+	FrameworkImg              string
+	FrameworkContent          string
+	IsPublic                  string
+	PublicTime                string
+	Sort                      string
+	AdminId                   string
+	AdminName                 string
+	CreateTime                string
+	ModifyTime                string
+}{
+	AiPredictModelFrameworkId: "ai_predict_model_framework_id",
+	FrameworkCode:             "framework_code",
+	FrameworkName:             "framework_name",
+	FrameworkImg:              "framework_img",
+	FrameworkContent:          "framework_content",
+	IsPublic:                  "is_public",
+	PublicTime:                "public_time",
+	Sort:                      "sort",
+	AdminId:                   "admin_id",
+	AdminName:                 "admin_name",
+	CreateTime:                "create_time",
+	ModifyTime:                "modify_time",
+}
+
+func (m *AiPredictModelFramework) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.AiPredictModelFrameworkId = int(id)
+	return
+}
+
+func (m *AiPredictModelFramework) CreateMulti(items []*AiPredictModelFramework) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AiPredictModelFramework) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AiPredictModelFramework) Del() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.AiPredictModelFrameworkId).Exec()
+	return
+}
+
+func (m *AiPredictModelFramework) GetItemById(id int) (item *AiPredictModelFramework, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *AiPredictModelFramework) GetItemByCondition(condition string, pars []interface{}) (item *AiPredictModelFramework, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *AiPredictModelFramework) 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 *AiPredictModelFramework) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AiPredictModelFramework, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	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 *AiPredictModelFramework) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AiPredictModelFramework, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	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
+}
+
+func (m *AiPredictModelFramework) CreateFrameworkAndNodes(item *AiPredictModelFramework, nodes []*AiPredictModelFrameworkNode) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	id, e := tx.Insert(item)
+	if e != nil {
+		err = fmt.Errorf("insert framework err: %s", e.Error())
+		return
+	}
+	newId := int(id)
+	item.AiPredictModelFrameworkId = newId
+
+	if len(nodes) > 0 {
+		for _, n := range nodes {
+			n.AiPredictModelFrameworkId = newId
+		}
+		_, e = tx.InsertMulti(len(nodes), nodes)
+		if e != nil {
+			err = fmt.Errorf("insert multi nodes err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+func (m *AiPredictModelFramework) EditFrameworkAndNodes(item *AiPredictModelFramework, updateCols []string, nodes []*AiPredictModelFrameworkNode) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	_, e = tx.Update(item, updateCols...)
+	if e != nil {
+		err = fmt.Errorf("framework update err: %s", e.Error())
+		return
+	}
+
+	sql := `DELETE FROM ai_predict_model_framework_node WHERE ai_predict_model_framework_id = ?`
+	_, e = tx.Raw(sql, item.AiPredictModelFrameworkId).Exec()
+	if e != nil {
+		err = fmt.Errorf("clear nodes err: %s", e.Error())
+		return
+	}
+	if len(nodes) > 0 {
+		_, e = tx.InsertMulti(len(nodes), nodes)
+		if e != nil {
+			err = fmt.Errorf("insert multi nodes err: %s", e.Error())
+			return
+		}
+	}
+	return
+}
+
+func (m *AiPredictModelFramework) RemoveFrameworkAndNodes(frameworkId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %s", e.Error())
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, e = tx.Raw(sql, frameworkId).Exec()
+	if e != nil {
+		err = fmt.Errorf("delete framework err: %s", e.Error())
+		return
+	}
+
+	sql = `DELETE FROM ai_predict_model_framework_node WHERE ai_predict_model_framework_id = ?`
+	_, e = tx.Raw(sql, frameworkId).Exec()
+	if e != nil {
+		err = fmt.Errorf("clear nodes err: %s", e.Error())
+		return
+	}
+	return
+}
+
+// AiPredictModelFrameworkAddReq 图库框架新增请求体
+type AiPredictModelFrameworkAddReq struct {
+	FrameworkName    string                           `description:"框架名称"`
+	FrameworkImg     string                           `description:"框架图片"`
+	FrameworkContent string                           `description:"框架内容"`
+	Nodes            []AiPredictModelFrameworkNodeReq `description:"框架节点"`
+}
+
+// AiPredictModelFrameworkNodeReq 图库框架节点请求体
+type AiPredictModelFrameworkNodeReq struct {
+	AiPredictModelIndexId int    `description:"模型标的ID"`
+	NodeId                string `description:"节点ID"`
+	NodeName              string `description:"节点名称"`
+}
+
+// AiPredictModelFrameworkEditReq 图库框架编辑请求体
+type AiPredictModelFrameworkEditReq struct {
+	AiPredictModelFrameworkId int `description:"图库框架ID"`
+	AiPredictModelFrameworkAddReq
+}
+
+// AiPredictModelFrameworkRemoveReq 图库框架编辑请求体
+type AiPredictModelFrameworkRemoveReq struct {
+	AiPredictModelFrameworkId int `description:"图库框架ID"`
+}
+
+// AiPredictModelFrameworkRenameReq 图库框架重命名请求体
+type AiPredictModelFrameworkRenameReq struct {
+	AiPredictModelFrameworkId int    `description:"图库框架ID"`
+	FrameworkName             string `description:"框架名称"`
+}
+
+// AiPredictModelFrameworkEditPublicReq 图库框架编辑公开请求体
+type AiPredictModelFrameworkEditPublicReq struct {
+	AiPredictModelFrameworkId int `description:"图库框架ID"`
+	IsPublic                  int `description:"0-隐藏公开; 1-公开"`
+}
+
+// AiPredictModelFrameworkMoveReq 图库框架移动排序请求体
+type AiPredictModelFrameworkMoveReq struct {
+	AiPredictModelFrameworkId     int `description:"图库框架ID"`
+	PrevAiPredictModelFrameworkId int `description:"上一个框架ID"`
+	NextAiPredictModelFrameworkId int `description:"下一个框架ID"`
+}
+
+// UpdateAiPredictModelFrameworkSort 更新我的图库框架排序
+func UpdateAiPredictModelFrameworkSort(adminId, frameworkId, current int, updates string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`UPDATE ai_predict_model_framework SET sort = %s WHERE  sort > ?`, updates)
+	if frameworkId > 0 {
+		sql += ` OR (ai_predict_model_framework_id > ` + fmt.Sprint(frameworkId) + ` AND sort = ` + fmt.Sprint(current) + `)`
+	}
+	_, err = o.Raw(sql, current).Exec()
+	return
+}
+
+// GetFirstAiPredictModelFramework 获取我的图库框架排首位的数据
+func GetFirstAiPredictModelFramework() (item *AiPredictModelFramework, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM ai_predict_model_framework ORDER BY sort ASC,ai_predict_model_framework_id ASC LIMIT 1`
+	err = o.Raw(sql).QueryRow(&item)
+	return
+}
+
+// AiPredictModelFrameworkItem 图库框架表信息
+type AiPredictModelFrameworkItem struct {
+	AiPredictModelFrameworkId int                                `description:"框架ID"`
+	FrameworkCode             string                             `description:"框架唯一编码"`
+	FrameworkName             string                             `description:"框架名称"`
+	FrameworkImg              string                             `description:"框架图片"`
+	FrameworkContent          string                             `description:"框架内容"`
+	IsPublic                  int                                `description:"是否公开:0-私有;1-公开"`
+	PublicTime                string                             `description:"公开时间"`
+	Sort                      int                                `description:"排序"`
+	AdminId                   int                                `description:"创建人ID"`
+	AdminName                 string                             `description:"创建人姓名"`
+	CreateTime                string                             `description:"创建时间"`
+	ModifyTime                string                             `description:"更新时间"`
+	Nodes                     []*AiPredictModelFrameworkNodeItem `description:"框架节点"`
+	Button                    AiPredictModelFrameworkItemButton  `description:"操作按钮"`
+}
+
+// AiPredictModelFrameworkItemButton 操作按钮
+type AiPredictModelFrameworkItemButton struct {
+	OpButton     bool `description:"是否可编辑"`
+	DeleteButton bool `description:"是否可删除"`
+	MoveButton   bool `description:"是否可移动"`
+}
+
+// FormatAiPredictModelFramework2Item 格式化框架信息
+func FormatAiPredictModelFramework2Item(origin *AiPredictModelFramework, nodes []*AiPredictModelFrameworkNodeItem) (item *AiPredictModelFrameworkItem) {
+	if origin == nil {
+		return
+	}
+	item = new(AiPredictModelFrameworkItem)
+	item.AiPredictModelFrameworkId = origin.AiPredictModelFrameworkId
+	item.FrameworkCode = origin.FrameworkCode
+	item.FrameworkName = origin.FrameworkName
+	item.FrameworkImg = origin.FrameworkImg
+	item.FrameworkContent = origin.FrameworkContent
+	item.IsPublic = origin.IsPublic
+	item.PublicTime = utils.TimeTransferString(utils.FormatDateTime, origin.PublicTime)
+	item.Sort = origin.Sort
+	item.AdminId = origin.AdminId
+	item.AdminName = origin.AdminName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	item.Nodes = nodes
+	return
+}
+
+// AiPredictModelFrameworkPublicMenuItem 公开框架目录
+type AiPredictModelFrameworkPublicMenuItem struct {
+	AdminId    int                            `description:"创建人ID"`
+	MenuName   string                         `description:"目录名称"`
+	Frameworks []*AiPredictModelFrameworkItem `description:"框架列表"`
+}

+ 144 - 0
models/ai_predict_model/ai_predict_model_framework_node.go

@@ -0,0 +1,144 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// AiPredictModelFrameworkNode 图库框架节点表
+type AiPredictModelFrameworkNode struct {
+	AiPredictModelFrameworkNodeId int       `orm:"column(ai_predict_model_framework_node_id);pk"`
+	AiPredictModelFrameworkId     int       `description:"框架ID"`
+	FrameworkName                 string    `description:"框架名称"`
+	NodeId                        string    `description:"节点ID"`
+	NodeName                      string    `description:"节点名称"`
+	AiPredictModelIndexId         int       `description:"我的图表分类ID"`
+	CreateTime                    time.Time `description:"创建时间"`
+}
+
+func (m *AiPredictModelFrameworkNode) TableName() string {
+	return "ai_predict_model_framework_node"
+}
+
+func (m *AiPredictModelFrameworkNode) PrimaryId() string {
+	return "ai_predict_model_framework_node_id"
+}
+
+func (m *AiPredictModelFrameworkNode) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.AiPredictModelFrameworkNodeId = int(id)
+	return
+}
+
+func (m *AiPredictModelFrameworkNode) CreateMulti(items []*AiPredictModelFrameworkNode) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AiPredictModelFrameworkNode) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AiPredictModelFrameworkNode) Del() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.AiPredictModelFrameworkNodeId).Exec()
+	return
+}
+
+func (m *AiPredictModelFrameworkNode) GetItemById(id int) (item *AiPredictModelFrameworkNode, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *AiPredictModelFrameworkNode) GetItemByCondition(condition string, pars []interface{}) (item *AiPredictModelFrameworkNode, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *AiPredictModelFrameworkNode) 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 *AiPredictModelFrameworkNode) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AiPredictModelFrameworkNode, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	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 *AiPredictModelFrameworkNode) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AiPredictModelFrameworkNode, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	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
+}
+
+// AiPredictModelFrameworkNodeItem 图库框架节点信息
+type AiPredictModelFrameworkNodeItem struct {
+	AiPredictModelFrameworkNodeId int
+	AiPredictModelFrameworkId     int    `description:"框架ID"`
+	FrameworkName                 string `description:"框架名称"`
+	NodeId                        string `description:"节点ID"`
+	NodeName                      string `description:"节点名称"`
+	AiPredictModelIndexId         int    `description:"模型标的ID"`
+	IndexName                     string `description:"模型标的名称"`
+	AiPredictModelNum             int    `description:"分类下的图表数"`
+	CreateTime                    string `description:"创建时间"`
+}
+
+// FormatAiPredictModelFrameworkNode2Item 格式化框架节点信息
+func FormatAiPredictModelFrameworkNode2Item(origin *AiPredictModelFrameworkNode, aiPredictModelNum int, aiPredictModelIndexMap map[int]*AiPredictModelIndex) (item *AiPredictModelFrameworkNodeItem) {
+	if origin == nil {
+		return
+	}
+	item = new(AiPredictModelFrameworkNodeItem)
+	item.AiPredictModelFrameworkNodeId = origin.AiPredictModelFrameworkNodeId
+	item.AiPredictModelFrameworkId = origin.AiPredictModelFrameworkId
+	item.FrameworkName = origin.FrameworkName
+	item.NodeId = origin.NodeId
+	item.NodeName = origin.NodeName
+	item.AiPredictModelIndexId = origin.AiPredictModelIndexId
+	if aiPredictModelIndex, ok := aiPredictModelIndexMap[origin.AiPredictModelIndexId]; ok {
+		item.IndexName = aiPredictModelIndex.IndexName
+	}
+	item.AiPredictModelNum = aiPredictModelNum
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	return
+}

+ 2 - 0
models/ai_predict_model/ai_predict_model_index.go

@@ -203,6 +203,7 @@ type AiPredictModelIndexItem struct {
 	SysUserRealName   string  `description:"创建人姓名"`
 	CreateTime        string  `description:"创建时间"`
 	ModifyTime        string  `description:"修改时间"`
+	SearchText        string  `description:"搜索结果(含高亮)"`
 }
 
 func (m *AiPredictModelIndex) Format2Item() (item *AiPredictModelIndexItem) {
@@ -342,6 +343,7 @@ func (m *AiPredictModelIndex) ImportIndexAndData(createIndexes, updateIndexes []
 				err = fmt.Errorf("insert index err: %v", e)
 				return
 			}
+			v.Index.AiPredictModelIndexId = int(indexId)
 			for _, d := range v.Data {
 				d.AiPredictModelIndexId = int(indexId)
 				d.IndexCode = v.Index.IndexCode

+ 46 - 38
models/business_conf.go

@@ -12,49 +12,50 @@ import (
 var (
 	BusinessConfMap map[string]string
 )
-const (
-	BusinessConfUseXf                     = "UseXf"
-	BusinessConfXfAppid                   = "XfAppid"
-	BusinessConfXfApiKey                  = "XfApiKey"
-	BusinessConfXfApiSecret               = "XfApiSecret"
-	BusinessConfXfVcn                     = "XfVcn"
-	BusinessConfEnPptCoverImgs            = "EnPptCoverImgs"
-	BusinessConfIsReportApprove           = "IsReportApprove"
-	BusinessConfReportApproveType         = "ReportApproveType"
-	BusinessConfCompanyName               = "CompanyName"
-	BusinessConfCompanyWatermark          = "CompanyWatermark"
-	BusinessConfWatermarkChart            = "WatermarkChart"
-	BusinessConfLoginSmsTpId              = "LoginSmsTpId"
-	BusinessConfLoginSmsGjTpId            = "LoginSmsGjTpId"
-	BusinessConfSmsJhgnAppKey             = "SmsJhgnAppKey"
-	BusinessConfSmsJhgjAppKey             = "SmsJhgjAppKey"
-	BusinessConfLdapHost                  = "LdapHost"
-	BusinessConfLdapBase                  = "LdapBase"
-	BusinessConfLdapPort                  = "LdapPort"
-	BusinessConfEmailClient               = "EmailClient"
-	BusinessConfEmailServerHost           = "EmailServerHost"
-	BusinessConfEmailServerPort           = "EmailServerPort"
-	BusinessConfEmailSender               = "EmailSender"
-	BusinessConfEmailSenderUserName       = "EmailSenderUserName"
-	BusinessConfEmailSenderPassword       = "EmailSenderPassword"
-	BusinessConfSmsClient                 = "SmsClient"
-	BusinessConfNanHuaSmsAppKey           = "NanHuaSmsAppKey"
-	BusinessConfNanHuaSmsAppSecret        = "NanHuaSmsAppSecret"
-	BusinessConfNanHuaSmsApiHost          = "NanHuaSmsApiHost"
-	BusinessConfLoginSmsTplContent        = "LoginSmsTplContent"
-	BusinessConfLoginEmailTemplateSubject = "LoginEmailTemplateSubject"
-	BusinessConfLoginEmailTemplateContent = "LoginEmailTemplateContent"
-	BusinessConfLdapBindUserSuffix        = "LdapBindUserSuffix"
-	BusinessConfLdapUserFilter            = "LdapUserFilter"
 
+const (
+	BusinessConfUseXf                        = "UseXf"
+	BusinessConfXfAppid                      = "XfAppid"
+	BusinessConfXfApiKey                     = "XfApiKey"
+	BusinessConfXfApiSecret                  = "XfApiSecret"
+	BusinessConfXfVcn                        = "XfVcn"
+	BusinessConfEnPptCoverImgs               = "EnPptCoverImgs"
+	BusinessConfIsReportApprove              = "IsReportApprove"
+	BusinessConfReportApproveType            = "ReportApproveType"
+	BusinessConfCompanyName                  = "CompanyName"
+	BusinessConfCompanyWatermark             = "CompanyWatermark"
+	BusinessConfWatermarkChart               = "WatermarkChart"
+	BusinessConfLoginSmsTpId                 = "LoginSmsTpId"
+	BusinessConfLoginSmsGjTpId               = "LoginSmsGjTpId"
+	BusinessConfSmsJhgnAppKey                = "SmsJhgnAppKey"
+	BusinessConfSmsJhgjAppKey                = "SmsJhgjAppKey"
+	BusinessConfLdapHost                     = "LdapHost"
+	BusinessConfLdapBase                     = "LdapBase"
+	BusinessConfLdapPort                     = "LdapPort"
+	BusinessConfEmailClient                  = "EmailClient"
+	BusinessConfEmailServerHost              = "EmailServerHost"
+	BusinessConfEmailServerPort              = "EmailServerPort"
+	BusinessConfEmailSender                  = "EmailSender"
+	BusinessConfEmailSenderUserName          = "EmailSenderUserName"
+	BusinessConfEmailSenderPassword          = "EmailSenderPassword"
+	BusinessConfSmsClient                    = "SmsClient"
+	BusinessConfNanHuaSmsAppKey              = "NanHuaSmsAppKey"
+	BusinessConfNanHuaSmsAppSecret           = "NanHuaSmsAppSecret"
+	BusinessConfNanHuaSmsApiHost             = "NanHuaSmsApiHost"
+	BusinessConfLoginSmsTplContent           = "LoginSmsTplContent"
+	BusinessConfLoginEmailTemplateSubject    = "LoginEmailTemplateSubject"
+	BusinessConfLoginEmailTemplateContent    = "LoginEmailTemplateContent"
+	BusinessConfLdapBindUserSuffix           = "LdapBindUserSuffix"
+	BusinessConfLdapUserFilter               = "LdapUserFilter"
 	BusinessConfTencentApiSecretId           = "TencentApiSecretId"           // 腾讯云API-密钥对
 	BusinessConfTencentApiSecretKey          = "TencentApiSecretKey"          // 腾讯云API-密钥对
 	BusinessConfTencentApiRecTaskCallbackUrl = "TencentApiRecTaskCallbackUrl" // 腾讯云API-语音识别回调地址
 	BusinessConfSmsJhgjVariable              = "SmsJhgjVariable"              // 聚合国际短信变量
-
-	BusinessConfEdbStopRefreshRule = "EdbStopRefreshRule" // 是否停止指标刷新规则
-	BusinessConfReport2ImgUrl      = "Report2ImgUrl"      // 报告转长图地址(用于兼容内外网环境的)
-	BusinessConfReportViewUrl      = "ReportViewUrl"      // 报告详情地址
+	BusinessConfEdbStopRefreshRule           = "EdbStopRefreshRule"           // 是否停止指标刷新规则
+	BusinessConfReport2ImgUrl                = "Report2ImgUrl"                // 报告转长图地址(用于兼容内外网环境的)
+	BusinessConfReportViewUrl                = "ReportViewUrl"                // 报告详情地址
+	BusinessConfEsIndexNameExcel             = "EsIndexNameExcel"             // ES索引名称-表格
+	BusinessConfEsIndexNameDataSource        = "EsIndexNameDataSource"        // ES索引名称-数据源
 )
 
 const (
@@ -265,4 +266,11 @@ func InitBusinessConf() {
 	if e != nil {
 		return
 	}
+	// ES索引名称
+	if BusinessConfMap[BusinessConfEsIndexNameExcel] != "" {
+		utils.EsExcelIndexName = BusinessConfMap[BusinessConfEsIndexNameExcel]
+	}
+	if BusinessConfMap[BusinessConfEsIndexNameDataSource] != "" {
+		utils.EsDataSourceIndexName = BusinessConfMap[BusinessConfEsIndexNameDataSource]
+	}
 }

+ 80 - 0
models/common/common_classify.go

@@ -0,0 +1,80 @@
+package common
+
+import "time"
+
+// CommonClassify 通用分类
+type CommonClassify struct {
+	ClassifyId   int       `description:"分类ID"`
+	ClassifyName string    `description:"分类名称"`
+	ParentId     int       `description:"父级ID"`
+	RootId       int       `description:"顶级ID"`
+	Level        int       `description:"层级"`
+	LevelPath    string    `description:"层级路径"`
+	Sort         int       `description:"排序"`
+	CreateTime   time.Time `description:"创建时间"`
+	ModifyTime   time.Time `description:"修改时间"`
+}
+
+// CommonClassifyCols 通用分类基本字段
+type CommonClassifyCols struct {
+	ClassifyId   string `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     string `description:"父级id"`
+	RootId       string `description:"顶级id"`
+	Level        string `description:"层级"`
+	LevelPath    string `description:"层级路径"`
+	Sort         string `description:"排序字段,越小越靠前,默认值:10"`
+	CreateTime   string `description:"创建时间"`
+	ModifyTime   string `description:"修改时间"`
+}
+
+// CommonClassifyObj 通用分类对象
+type CommonClassifyObj struct {
+	ObjectId   int       `description:"对象ID"`
+	ClassifyId int       `description:"分类ID"`
+	Sort       int       `description:"排序"`
+	CreateTime time.Time `description:"创建时间"`
+	ModifyTime time.Time `description:"修改时间"`
+}
+
+// CommonClassifyObjCols 通用分类对象基本字段
+type CommonClassifyObjCols struct {
+	ObjectId   string `description:"对象ID"`
+	ClassifyId string `description:"分类ID"`
+	Sort       string `description:"排序"`
+	CreateTime string `description:"创建时间"`
+	ModifyTime string `description:"修改时间"`
+}
+
+// CommonClassifyMoveReq 移动分类
+type CommonClassifyMoveReq struct {
+	ClassifyId       int `description:"分类ID"`
+	ParentClassifyId int `description:"父级分类ID"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类ID"`
+	NextClassifyId   int `description:"下一个兄弟节点分类ID"`
+	ObjectId         int `description:"对象ID(指标/图表..), 如果对象ID>0则移动对象, 否则认为移动分类"`
+	PrevObjectId     int `description:"上一个对象ID"`
+	NextObjectId     int `description:"下一个对象ID"`
+}
+
+// ExtraPermissionClassifyStrategy 是一个带有额外权限校验的装饰器
+//type ExtraPermissionClassifyStrategy struct {
+//	BaseClassifyStrategy
+//}
+
+// UpdateCommonClassify 覆盖基础策略的UpdateClassify方法,并添加额外的权限校验
+//func (s *ExtraPermissionClassifyStrategy) UpdateCommonClassify(classify *CommonClassify) error {
+//	// 额外的权限校验
+//	if !checkExtraPermission(classify) {
+//		return fmt.Errorf("无操作权限")
+//	}
+//
+//	// 调用基础策略的UpdateClassify方法
+//	return s.BaseClassifyStrategy.UpdateCommonClassify(classify)
+//}
+//
+//// checkExtraPermission 进行额外的权限校验
+//func checkExtraPermission(classify *CommonClassify) bool {
+//	// 实现额外权限校验逻辑
+//	return true
+//}

+ 233 - 0
models/data_manage/base_from_clarksons_classify.go

@@ -0,0 +1,233 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// BaseFromClarksonsClassify 卓创红期原始数据分类表
+type BaseFromClarksonsClassify struct {
+	BaseFromClassifyId int       `orm:"column(base_from_clarksons_classify_id);pk"`
+	ClassifyName       string    `description:"分类名称"`
+	ParentId           int       `description:"父级id"`
+	Level              int       `description:"层级"`
+	Sort               int       `description:"排序字段"`
+	ModifyTime         time.Time `description:"修改时间"`
+	CreateTime         time.Time `description:"创建时间"`
+}
+
+type BaseFromClarksonsClassifyItem struct {
+	BaseFromClassifyId int                              `orm:"column(base_from_clarksons_classify_id);pk"`
+	ClassifyName       string                           `description:"分类名称"`
+	ParentId           int                              `description:"父级id"`
+	Level              int                              `description:"层级"`
+	Sort               int                              `description:"排序字段"`
+	UniqueCode         string                           `description:"唯一code"`
+	ModifyTime         time.Time                        `description:"修改时间"`
+	CreateTime         time.Time                        `description:"创建时间"`
+	ClassifyNameEn     string                           `description:"英文分类名称"`
+	Children           []*BaseFromClarksonsClassifyItem `description:"子分类"`
+}
+
+type BaseFromClarksonsClassifyMaxSort struct {
+	BaseFromClassifyId int `description:"分类id"`
+	MaxSort            int `description:"最大排序"`
+}
+
+func (t *BaseFromClarksonsClassify) Add() (insertId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	insertId, err = o.Insert(t)
+	return
+}
+
+func (t *BaseFromClarksonsClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(t, cols...)
+	return
+}
+
+func BatchAddClarksonsClassify(items []*BaseFromClarksonsClassify) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// 获取所有分类
+func GetClarksonsClassifyAll() (items []*BaseFromClarksonsClassifyItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_classify ORDER BY sort ASC, base_from_clarksons_classify_id ASC`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetChildClarksonsClassifyListById 获取子分类列表
+func GetChildClarksonsClassifyListById(classifyId int) (items []*BaseFromClarksonsClassifyItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_classify WHERE parent_id=? `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+// GetChildClarksonsClassifyIdsById 获取子分类的id集合
+func GetChildClarksonsClassifyIdsById(classifyId int) (items []int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT base_from_clarksons_classify_id FROM base_from_clarksons_classify WHERE parent_id=? `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+// GetChildClarksonsClassifyMaxSortById 获取子分类最大排序
+func GetChildClarksonsClassifyMaxSortById(classifyId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT MAX(sort) AS sort FROM base_from_clarksons_classify WHERE parent_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&sort)
+	return
+}
+
+// GetClarksonsClassifyCountById 获取分类数量
+func GetClarksonsClassifyCountById(classifyId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(*) AS count FROM base_from_clarksons_classify WHERE base_from_clarksons_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+// GetClarksonsClassifyById 通过分类id获取分类
+func GetClarksonsClassifyById(classifyId int) (item *BaseFromClarksonsClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_classify WHERE base_from_clarksons_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+// GetClarksonsChildClassifyById 通过分类id获取子分类
+func GetClarksonsChildClassifyIdsById(classifyId int) (items []int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT base_from_clarksons_classify_id FROM base_from_clarksons_classify WHERE parent_id=? `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+// GetClarksonsClassifyListByIds 通过分类id获取分类列表
+func GetClarksonsClassifyListByIds(classifyIds []int) (items []*BaseFromClarksonsClassify, err error) {
+	if len(classifyIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_classify WHERE base_from_clarksons_classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	_, err = o.Raw(sql, classifyIds).QueryRows(&items)
+	return
+}
+
+// GetClarksonsClassifyCountByName 通过分类名称获取分类
+func GetClarksonsClassifyCountByName(classifyName string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(*) AS count FROM base_from_clarksons_classify WHERE 1=1`
+
+	sql += ` AND classify_name=? `
+	err = o.Raw(sql, classifyName).QueryRow(&count)
+	return
+}
+
+func GetBaseFromClarksonsClassifyEnCount(classifyNameEn string, parentId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM base_from_clarksons_classify WHERE classify_name_en=? AND parent_id=? `
+	err = o.Raw(sql, classifyNameEn, parentId).QueryRow(&count)
+	return
+}
+
+func GetBaseFromClarksonsClassifyCount(classifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM base_from_clarksons_classify WHERE classify_name=? AND parent_id=? `
+	err = o.Raw(sql, classifyName, parentId).QueryRow(&count)
+	return
+}
+
+func DeleteClarksonsClassifyById(classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` DELETE FROM base_from_clarksons_classify WHERE base_from_clarksons_classify_id=? `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+// BatchDeleteClarksonsClassifyById 批量删除分类
+func BatchDeleteClarksonsClassifyById(classifyId []int) (err error) {
+	if len(classifyId) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := ` DELETE FROM base_from_clarksons_classify WHERE base_from_clarksons_classify_id IN (` + utils.GetOrmInReplace(len(classifyId)) + `) `
+	_, err = o.Raw(sql, classifyId).Exec()
+
+	return
+}
+
+// DeleteClarksonsClassifyByClassifyId 根据分类id删除对应的指标分类
+func DeleteClarksonsClassifyByClassifyId(classifyIdList []int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	num := len(classifyIdList)
+	if num <= 0 {
+		return
+	}
+	//删除分类
+	sql := `DELETE FROM base_from_clarksons_classify WHERE base_from_clarksons_classify_id IN (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, classifyIdList).Exec()
+	return
+}
+
+// GetClarksonsIndexClassifyMinSort 获取最小不等于0的排序
+func GetClarksonsIndexClassifyMinSort(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT MIN(sort) FROM base_from_clarksons_classify WHERE parent_id=? AND sort <> 0 `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+// MoveUpClarksonsIndexClassifyBySort 往上移动
+func MoveUpClarksonsIndexClassifyBySort(parentId, nextSort, currentSort int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `update base_from_clarksons_classify set sort = sort + 1 where parent_id=? and sort >= ? and sort< ?`
+	_, err = o.Raw(sql, parentId, nextSort, currentSort).Exec()
+	return
+}
+
+// MoveDownClarksonsIndexClassifyBySort 往下移动
+func MoveDownClarksonsIndexClassifyBySort(parentId, prevSort, currentSort int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `update base_from_clarksons_classify set sort = sort - 1 where parent_id=? and sort <= ? and sort> ? `
+	_, err = o.Raw(sql, parentId, prevSort, currentSort).Exec()
+	return
+}
+
+// UpdateClarksonsClassifySortByParentId 根据父类id更新排序
+func UpdateClarksonsClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update base_from_clarksons_classify set sort = ` + updateSort + ` WHERE parent_id=? and sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( base_from_clarksons_classify_id > ` + fmt.Sprint(classifyId) + ` and sort= ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// GetFirstClarksonsClassifyByParentId 获取当前父级分类下的排序第一条的数据
+func GetFirstClarksonsClassifyByParentId(parentId int) (item *ChartClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_classify WHERE parent_id=? order by sort asc,base_from_clarksons_classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}

+ 81 - 0
models/data_manage/base_from_clarksons_data.go

@@ -0,0 +1,81 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromClarksonsData struct {
+	BaseFromClarksonsDataId          int       `orm:"column(base_from_clarksons_data_id);pk"`
+	BaseFromClarksonsIndexId int       `description:"指标id"`
+	IndexCode            string    `description:"指标编码"`
+	DataTime             string    `description:"数据日期"`
+	Value                float64   `description:"数据值"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"修改时间"`
+	DataTimestamp        int64     `description:"数据时间戳"`
+}
+
+// GetClarksonsDataByIndexId 根据指标id获取指标数据
+func GetClarksonsDataByIndexId(indexId int) (items []*BaseFromClarksonsData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_data WHERE base_from_clarksons_index_id=? ORDER BY data_time DESC`
+	_, err = o.Raw(sql, indexId).QueryRows(&items)
+	return
+}
+
+// GetClarksonsDataDataTimeByIndexId 根据指标id获取指标数据的日期列表
+func GetClarksonsDataDataTimeByIndexId(indexIdList []int) (items []string, err error) {
+	if len(indexIdList) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT DISTINCT data_time FROM base_from_clarksons_data WHERE base_from_clarksons_index_id IN (` + utils.GetOrmInReplace(len(indexIdList)) + `) ORDER BY data_time DESC`
+	_, err = o.Raw(sql, indexIdList).QueryRows(&items)
+	return
+}
+
+func GetClarksonsIndexDataByCode(indexCode string) (items []*BaseFromClarksonsData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_clarksons_data WHERE index_code=? ORDER BY data_time DESC  `
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+	return
+}
+
+func GetClarksonsIndexDataCount(indexCode string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_clarksons_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&count)
+	return
+}
+
+// GetClarksonsLastUpdateTimeLastByIndexCode 根据指标编码查询 返回ModifyTime最后一条数据
+func GetClarksonsLastUpdateTimeLastByIndexCode(indexCodes []string) (items []*BaseFromClarksonsData, err error) {
+	o := orm.NewOrmUsingDB("data")
+
+	// 构造 SQL 查询
+	sql := `SELECT t1.index_code, t1.data_time, t2.value
+			FROM (
+    			SELECT index_code, MAX(data_time) AS data_time
+   				 FROM base_from_clarksons_data
+    			WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodes)) + `)
+    			GROUP BY index_code
+			) AS t1
+			JOIN base_from_clarksons_data AS t2 ON t1.index_code = t2.index_code AND t1.data_time = t2.data_time`
+
+	// 执行 SQL 查询
+	_, err = o.Raw(sql, indexCodes).QueryRows(&items)
+	if err != nil {
+		return nil, err
+	}
+	return items, nil
+}
+
+func GetClarksonsIndexData(indexCode string, startSize, pageSize int) (items []*BaseFromClarksonsData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_clarksons_data WHERE index_code=? ORDER BY data_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, indexCode, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 424 - 0
models/data_manage/base_from_clarksons_index.go

@@ -0,0 +1,424 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromClarksonsIndex struct {
+	BaseFromClarksonsIndexId int       `orm:"pk"`
+	ClassifyId           int       `description:"指标分类id"`
+	IndexCode            string    `description:"指标编码"`
+	IndexName            string    `description:"指标名称"`
+	Unit                 string    `description:"单位"`
+	Frequency            string    `description:"频度"`
+	StartDate            string    `description:"开始日期"`
+	EndDate              string    `description:"结束日期"`
+	Sort                 int       `description:"排序"`
+	CreateTime           time.Time
+	ModifyTime           time.Time
+}
+
+type BaseFromClarksonsIndexView struct {
+	BaseFromClarksonsIndexId int     `orm:"pk"`
+	EdbInfoId            int     `description:"指标库id"`
+	ClassifyId           int     `description:"指标分类id"`
+	IndexCode            string  `description:"指标编码"`
+	IndexName            string  `description:"指标名称"`
+	UniqueCode           string  `description:"唯一code"`
+	Frequency            string  `description:"频度"`
+	Unit                 string  `description:"单位"`
+	StartDate            string  `description:"开始日期"`
+	EndDate              string  `description:"结束日期"`
+	Sort                 int     `description:"排序"`
+	LatestDate           string  `description:"最后更新时间"`
+	EdbExist             int     `description:"edb是否存在"`
+	ModifyTime           string
+}
+
+type BaseFromClarksonsIndexList struct {
+	BaseFromClarksonsIndexId int     `orm:"pk"`
+	IndexCode          string    // 指标编码
+	IndexName          string    // 指标名称
+	ClassifyId         int       // 分类Id
+	Unit               string    // 单位
+	Frequency          string    // 频度
+	Describe           string    // 指标描述
+	CreateTime         string // 创建时间
+	ModifyTime         string // 修改时间
+	DataList           []*BaseFromClarksonsData
+	Paging             *paging.PagingItem `description:"分页数据"`
+}
+func (b *BaseFromClarksonsIndex) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(b, cols...)
+	return
+}
+
+// GetClarksonsIndexByCondition 根据条件获取克拉克森指标列表
+func GetClarksonsIndexByCondition(condition string, pars []interface{}) (items []*BaseFromClarksonsIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_index WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_clarksons_index_id ASC`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetClarksonsIndexByCondition 根据条件获取克拉克森指标列表
+func GetClarksonsIndexByConditionAndFrequency(condition, frequency string, pars []interface{}) (items []*BaseFromClarksonsIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_index WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` AND frequency=?`
+	sql += ` ORDER BY sort ASC, base_from_clarksons_index_id ASC`
+	_, err = o.Raw(sql, pars, frequency).QueryRows(&items)
+	return
+}
+
+func GetClarksonsIndexCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(*) AS count FROM base_from_clarksons_index WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_clarksons_index_id ASC`
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetClarksonsIndexAndEdbInfoByCondition 根据条件获取克拉克森index和指标库的信息
+func GetClarksonsIndexAndEdbInfoByCondition(condition string, pars []interface{}) (items []*BaseFromClarksonsIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT b.*, e.edb_info_id FROM base_from_clarksons_index AS b LEFT JOIN edb_info AS e ON b.index_code=e.edb_code AND e.source=? WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC `
+	_, err = o.Raw(sql, utils.DATA_SOURCE_SCI_HQ, pars).QueryRows(&items)
+	return
+}
+
+// GetClarksonsIndexByIndexCode 根据指标编码获取指标信息
+func GetClarksonsIndexByIndexCode(indexCode string) (item *BaseFromClarksonsIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_index WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&item)
+	return
+}
+
+// GetClarksonsIndexByIndexId 根据指标id获取指标信息
+func GetClarksonsIndexByIndexId(indexId int) (item *BaseFromClarksonsIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_index WHERE base_from_clarksons_index_id=? `
+	err = o.Raw(sql, indexId).QueryRow(&item)
+	return
+}
+
+// GetClarksonsIndexListByIndexIds 根据指标id获取指标信息
+func GetClarksonsIndexListByIndexIds(indexIds []int) (items []*BaseFromClarksonsIndex, err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_index WHERE base_from_clarksons_index_id IN (` + utils.GetOrmInReplace(len(indexIds)) + `) `
+	_, err = o.Raw(sql, indexIds).QueryRows(&items)
+	return
+}
+
+// GetClarksonsIndexCountByClassifyIds 获取分类下指标的个数
+func GetClarksonsIndexCountByClassifyIds(classifyIds []int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	num := len(classifyIds)
+	if num <= 0 {
+		return
+	}
+	sql := `SELECT COUNT(1) AS count FROM base_from_clarksons_index WHERE classify_id IN (` + utils.GetOrmInReplace(num) + `) `
+	err = o.Raw(sql, classifyIds).QueryRow(&count)
+	return
+}
+
+// GetClarksonsIndexByClassifyId 根据分类id获取克拉克森指标列表
+func GetClarksonsIndexByClassifyId(classifyIds []int, startSize, pageSize int) (items []*BaseFromClarksonsIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT b.*, e.edb_info_id,
+	CASE WHEN e.edb_info_id IS NULL THEN 0 ELSE 1 END AS edb_exist
+	FROM base_from_clarksons_index AS b
+	LEFT JOIN edb_info AS e ON b.index_code=e.edb_code AND e.source=101
+	WHERE b.classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) ORDER BY b.sort ASC LIMIT ?,? `
+	_, err = o.Raw(sql, classifyIds, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetClarksonsIndexCountByClassifyId 根据分类id获取克拉克森指标数量
+func GetClarksonsIndexCountByClassifyId(classifyIds []int) (count int, err error) {
+	if len(classifyIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(*) AS count FROM base_from_clarksons_index WHERE classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) `
+	err = o.Raw(sql, classifyIds).QueryRow(&count)
+	return
+}
+
+// GetClarksonsIndexCount 获取克拉克森指标数量
+func GetClarksonsIndexCount() (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(*) AS count FROM base_from_clarksons_index `
+	err = o.Raw(sql).QueryRow(&count)
+	return
+}
+
+func GetClarksonsIndexByPage(startSize, pageSize int) (items []*BaseFromClarksonsIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT b.*, e.edb_info_id,
+	CASE WHEN e.edb_info_id IS NULL THEN 0 ELSE 1 END AS edb_exist
+	FROM base_from_clarksons_index AS b
+	LEFT JOIN edb_info AS e ON b.index_code=e.edb_code AND e.source=101
+	ORDER BY b.modify_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetClarksonsIndexBaseInfoByClassifyId 根据分类id获取克拉克森指标列表
+func GetClarksonsIndexBaseInfoByClassifyId(classifyId int) (items []*BaseFromClarksonsIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT base_from_clarksons_index_id, classify_id, index_code, index_name, CONCAT(classify_id, '_', base_from_clarksons_index_id) AS unique_code  FROM base_from_clarksons_index WHERE classify_id = ? ORDER BY sort ASC `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+// GetClarksonsIndexBaseInfoByClassifyId 根据分类id获取克拉克森指标列表
+func GetClarksonsIndexBaseInfoByCondition(condition string, pars []interface{}) (items []*BaseFromClarksonsIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT base_from_clarksons_index_id, index_code, index_name  FROM base_from_clarksons_index WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC `
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+func GetClarksonsDataMaxCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT MAX(t.num) AS count FROM ( SELECT COUNT(1) AS num  FROM base_from_clarksons_index AS a INNER JOIN base_from_clarksons_data AS b ON a.index_code=b.index_code WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY a.base_from_clarksons_index_id) AS t `
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetClarksonsIndexMaxSortByClassifyId 根据分类id获取指标最大排序
+func GetClarksonsIndexMaxSortByClassifyId(classifyId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT MAX(sort) FROM base_from_clarksons_index WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&sort)
+	return
+}
+
+func GetClarksonsFrequency(classifyId int) (items []*string, err error) {
+	sql := `SELECT DISTINCT frequency FROM base_from_clarksons_index WHERE classify_id=? ORDER BY FIELD(frequency,'日度','周度','月度','季度','半年','年度') `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+func GetClarksonsFrequencyByCondition(condition string, pars []interface{}) (items []*string, err error) {
+	sql := `SELECT DISTINCT frequency FROM base_from_clarksons_index WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY FIELD(frequency,'日度','周度','月度','季度','半年','年度') `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+func GetClarksonsFrequencyByCode(code string) (items []*string, err error) {
+	sql := `SELECT DISTINCT frequency FROM base_from_clarksons_index WHERE index_code=? ORDER BY FIELD(frequency,'日度','周度','月度','季度','半年','年度') `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, code).QueryRows(&items)
+	return
+}
+
+// GetClarksonsClassifyMaxSortByClassifyIds 通过分类id获取对应分类的最大sort
+func GetClarksonsClassifyMaxSortByClassifyIds(classifyIds []int) (items []*BaseFromClarksonsClassifyMaxSort, err error) {
+	if len(classifyIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT bc.base_from_clarksons_classify_id, COALESCE(MAX(bi.sort), 0) AS max_sort FROM base_from_clarksons_classify  AS bc
+	LEFT JOIN base_from_clarksons_index AS bi
+	ON bc.base_from_clarksons_classify_id=bi.classify_id  
+	WHERE bc.base_from_clarksons_classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)
+	GROUP BY bc.base_from_clarksons_classify_id
+	`
+	// sql = ` SELECT classify_id, MAX(sort) AS max_sort FROM base_from_clarksons_index WHERE classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) GROUP BY classify_id `
+	_, err = o.Raw(sql, classifyIds).QueryRows(&items)
+	return
+}
+
+func BatchModifyClarksonsIndexClassify(items []*BaseFromClarksonsIndex) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE base_from_clarksons_index SET classify_id=?, sort=? WHERE base_from_clarksons_index_id=? `
+	p, err := o.Raw(sql).Prepare()
+	if err != nil {
+		return
+	}
+	defer func() {
+		p.Close()
+	}()
+	for _, v := range items {
+		_, err = p.Exec(v.ClassifyId, v.Sort, v.BaseFromClarksonsIndexId)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// MoveDownClarksonsIndexBySort 往下移动
+func MoveDownClarksonsIndexBySort(classifyId, prevSort, currentSort int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `update base_from_clarksons_index set sort = sort - 1 where classify_id=? and sort <= ? and sort> ? `
+	_, err = o.Raw(sql, classifyId, prevSort, currentSort).Exec()
+	return
+}
+
+// MoveUpClarksonsIndexBySort 往上移动
+func MoveUpClarksonsIndexBySort(classifyId, nextSort, currentSort int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `update base_from_clarksons_index set sort = sort + 1 where classify_id=? and sort >= ? and sort< ?`
+	_, err = o.Raw(sql, classifyId, nextSort, currentSort).Exec()
+	return
+}
+
+func DeleteClarksonsIndexById(indexId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := `DELETE FROM base_from_clarksons_index WHERE base_from_clarksons_index_id=? `
+	_, err = to.Raw(sql, indexId).Exec()
+	if err != nil {
+		return
+	}
+	sql = `DELETE FROM base_from_clarksons_data WHERE base_from_clarksons_index_id=? `
+	_, err = to.Raw(sql, indexId).Exec()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func DeleteClarksonsIndexByIds(indexIds []int) (err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	sql := `DELETE FROM base_from_clarksons_index WHERE base_from_clarksons_index_id IN (` + utils.GetOrmInReplace(len(indexIds)) + `) `
+	_, err = o.Raw(sql, indexIds).Exec()
+	if err != nil {
+		return
+	}
+	sql = `DELETE FROM base_from_clarksons_data WHERE base_from_clarksons_index_id IN (` + utils.GetOrmInReplace(len(indexIds)) + `) `
+	_, err = o.Raw(sql, indexIds).Exec()
+	if err != nil {
+		return
+	}
+	return
+}
+
+// MoveClarksonsIndex 移动指标分类
+func MoveClarksonsIndex(indexId, classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` UPDATE base_from_clarksons_index
+			SET
+			  classify_id = ?, modify_time=NOW() 
+			WHERE base_from_clarksons_index_id = ?`
+	_, err = o.Raw(sql, classifyId, indexId).Exec()
+	return
+}
+
+// GetClarksonsIndexMinSortByClassifyId 获取最小不等于0的排序
+func GetClarksonsIndexMinSortByClassifyId(classifyId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT min(sort) FROM base_from_clarksons_index WHERE classify_id=? and sort <> 0 `
+	err = o.Raw(sql, classifyId).QueryRow(&sort)
+	return
+}
+
+// GetClarksonsIndexInfoCount 分页查询指标信息行数
+func GetClarksonsIndexInfoCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT count(1) FROM base_from_clarksons_index WHERE index_code not in (select edb_code from edb_info) `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetClarksonsIndexInfoPage 分页查询指标信息
+func GetClarksonsIndexInfoPage(condition string, pars []interface{}) (items []*BaseFromRzdIndexAndData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_index WHERE index_code not in (select edb_code from edb_info) `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+
+}
+
+func GetClarksonsIndex(condition string, pars interface{}) (items []*BaseFromClarksonsIndexView, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_clarksons_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += `ORDER BY base_from_clarksons_index_id ASC `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func GetClarksonsIndexLatestDate(indexCode string) (ModifyTime string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT modify_time FROM base_from_clarksons_data WHERE index_code=? ORDER BY modify_time DESC limit 1 `
+	err = o.Raw(sql, indexCode).QueryRow(&ModifyTime)
+	return
+}

+ 1 - 0
models/data_manage/base_from_eia_stero.go

@@ -58,6 +58,7 @@ type BaseFromEiaSteoClassifyView struct {
 	ClassifyNameOriginal      string                         `description:"分类名称(原始名称)"`
 	ParentId                  int                            `description:"父级id"`
 	Level                     int                            `description:"层级"`
+	UniqueCode                string                         `description:"唯一编码"`
 	Child                     []*BaseFromEiaSteoClassifyView `description:"子级分类列表"`
 }
 

+ 16 - 0
models/data_manage/base_from_fenwei.go

@@ -283,3 +283,19 @@ func GetFenWeiIndexInfoCount(condition string, pars []interface{}) (count int, e
 	err = o.Raw(sql, pars).QueryRow(&count)
 	return
 }
+
+// GetFenWeiDataLastModifyTime 获取指标数据最新更新时间
+func GetFenWeiDataLastModifyTime(indexCode string) (lastModifyTime string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT MAX(modify_time) AS last_modify_time FROM base_from_fenwei_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&lastModifyTime)
+	return
+}
+
+// GetFenWeiDataLastModifyTimeList 查询指标数据最新更新时间
+func GetFenWeiDataLastModifyTimeList(indexCodes []string) (items []*BaseFromFenweiData, err error) {
+	sql := ` SELECT MAX(modify_time) AS modify_time, index_code FROM base_from_fenwei_data WHERE index_code IN(` + utils.GetOrmInReplace(len(indexCodes)) + `) GROUP BY index_code `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, indexCodes).QueryRows(&items)
+	return
+}

+ 280 - 0
models/data_manage/base_from_gpr_risk.go

@@ -0,0 +1,280 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type BaseFromGprRiskIndex struct {
+	BaseFromGprRiskIndexId int `orm:"column(base_from_gpr_risk_index_id);pk"`
+	ClassifyId             int
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	Sort                   int
+	StartDate              string `description:"开始日期"`
+	EndDate                string `description:"结束日期"`
+	EndValue               float64
+	CreateTime             time.Time
+	ModifyTime             time.Time
+}
+
+type BaseFromGprRiskIndexList struct {
+	BaseFromGprRiskIndexId int `orm:"column(base_from_gpr_risk_index_id);pk"`
+	ClassifyId             int
+	Interface              string
+	EdbInfoId              int
+	EdbUniqueCode          string `description:"指标库唯一编码"`
+	EdbClassifyId          int    `description:"指标库分类ID"`
+	StartDate              string
+	EndDate                string
+	EndValue               float64
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	Sort                   int
+	CreateTime             string
+	ModifyTime             string
+	EdbExist               int `description:"指标库是否已添加:0-否;1-是"`
+	DataList               []*BaseFromGprRiskData
+	Paging                 *paging.PagingItem `description:"分页数据"`
+}
+type BaseFromGprRiskIndexSearchList struct {
+	List   []*BaseFromGprRiskIndexList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+type GprRiskSingleDataResp struct {
+	BaseFromGprRiskIndexId int
+	ClassifyId             int
+	EdbInfoId              int
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	StartTime              string
+	CreateTime             string
+	ModifyTime             string
+	EdbExist               int `description:"指标库是否已添加:0-否;1-是"`
+	Data                   []*GprRiskSingleData
+	Paging                 *paging.PagingItem `description:"分页数据"`
+}
+
+type GprRiskSingleData struct {
+	Value    string `orm:"column(value)" description:"日期"`
+	DataTime string `orm:"column(data_time)" description:"值"`
+}
+
+func GetGprRiskIndexByClassifyId(classifyId int) (items []*BaseFromGprRiskIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT base_from_gpr_risk_index_id, classify_id, index_code, index_name FROM base_from_gpr_risk_index WHERE classify_id=? ORDER BY sort ASC, base_from_gpr_risk_index_id ASC `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+func GetGprRiskIndex(condition string, pars interface{}) (items []*BaseFromGprRiskIndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_gpr_risk_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_gpr_risk_index_id asc`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func GetGprRiskIndexPage(condition string, pars interface{}, startSize, pageSize int) (items []*BaseFromGprRiskIndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_gpr_risk_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_gpr_risk_index_id asc LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetGprRiskIndexPageCount(condition string, pars interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_gpr_risk_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetGprRiskIndexDataCount(indexCode string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_gpr_risk_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&count)
+	return
+}
+
+func GetGprRiskIndexDataByDataTime(indexCodes []string, startDate, endDate string) (items []*BaseFromGprRiskData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_gpr_risk_data WHERE  index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) and data_time >=? and data_time <? ORDER BY data_time DESC `
+	_, err = o.Raw(sql, indexCodes, startDate, endDate).QueryRows(&items)
+	return
+}
+
+func GetGprRiskIndexDataTimePageByCodes(indexCodes []string, startSize, pageSize int) (dataTimes []string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT data_time FROM base_from_gpr_risk_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) GROUP BY data_time ORDER BY data_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, indexCodes, startSize, pageSize).QueryRows(&dataTimes)
+	return
+}
+
+func GetGprRiskIndexDataTimePageCount(indexCodes []string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(DISTINCT data_time) AS count  FROM base_from_gpr_risk_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) `
+	err = o.Raw(sql, indexCodes).QueryRow(&count)
+	return
+}
+
+func GetGprRiskIndexDataByCodes(indexCode []string) (items []*BaseFromGprRiskData, err error) {
+	if len(indexCode) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_gpr_risk_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCode)) + `) ORDER BY data_time DESC  `
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+	return
+}
+
+// GetGprRiskByConditionAndFrequency 根据条件获取涌益咨询指标列表
+func GetGprRiskByConditionAndFrequency(condition, frequency string, pars []interface{}) (items []*BaseFromGprRiskIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_gpr_risk_index WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` AND frequency=?`
+	sql += ` ORDER BY sort ASC, base_from_gpr_risk_index_id ASC`
+	_, err = o.Raw(sql, pars, frequency).QueryRows(&items)
+	return
+}
+
+func GetGprRiskFrequencyByCondition(condition string, pars []interface{}) (items []string, err error) {
+	sql := `SELECT DISTINCT frequency FROM base_from_gpr_risk_index WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY FIELD(frequency,'日度','周度','旬度','月度','季度','半年度','年度') `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+// GetGprRiskDataDataTimeByIndexId 根据指标id获取指标数据的日期列表
+func GetGprRiskDataDataTimeByIndexId(indexIdList []int) (items []string, err error) {
+	if len(indexIdList) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT DISTINCT data_time FROM base_from_gpr_risk_data WHERE base_from_gpr_risk_index_id IN (` + utils.GetOrmInReplace(len(indexIdList)) + `) ORDER BY data_time DESC`
+	_, err = o.Raw(sql, indexIdList).QueryRows(&items)
+	return
+}
+
+type BaseFromGprRiskData struct {
+	BaseFromGprRiskDataId  int `orm:"column(base_from_gpr_risk_data_id);pk"`
+	BaseFromGprRiskIndexId int
+	IndexCode              string
+	DataTime               string
+	Value                  string
+	CreateTime             string
+	ModifyTime             string
+	DataTimestamp          int64
+}
+
+type BaseFromGprRiskIndexSearchItem struct {
+	BaseFromGprRiskIndexId int `orm:"column(base_from_gpr_risk_index_id);pk"`
+	ClassifyId             int
+	ParentClassifyId       int
+	IndexCode              string
+	IndexName              string
+}
+
+// BatchCheckGprRiskEdbReq 指标数据结构体
+type BatchCheckGprRiskEdbReq struct {
+	//IsJoinEdb      int      `form:"IsJoinEdb" description:"是否加到指标库,0:未加到指标库"`
+	Frequencies   string `description:"频度;枚举值:日度、周度、月度、季度、半年度、年度"`
+	Keyword       string `description:"关键字"`
+	ClassifyIds   string `description:"所选品种id列表"`
+	ListAll       bool   `form:"ListAll" json:"ListAll" description:"列表全选"`
+	TradeCodeList string `form:"TradeCodeList" json:"TradeCodeList" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
+}
+
+// GetGprRiskItemList 模糊查询GprRisk数据库指标列表
+func GetGprRiskItemList(condition string) (items []*BaseFromGprRiskIndexSearchItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM base_from_gpr_risk_index  WHERE 1=1"
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetGprRiskIndexDataByCode(indexCode string, pageIndex, pageSize int) (list []*BaseFromGprRiskData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_gpr_risk_data WHERE index_code=? order by data_time desc limit ?,?`
+	_, err = o.Raw(sql, indexCode, pageIndex, pageSize).QueryRows(&list)
+	return
+}
+
+func GetGprRiskIndexDataTotalByCode(indexCode string) (total int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT count(*) FROM base_from_gpr_risk_data WHERE index_code=?`
+	err = o.Raw(sql, indexCode).QueryRow(&total)
+	return
+}
+
+func GetBaseFromGprRiskIndexByIndexCode(indexCode string) (list *BaseFromGprRiskIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_gpr_risk_index WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&list)
+	return
+}
+
+type BaseFromGprRiskIndexType struct {
+	Type2 string `orm:"column(type_2)"`
+	Type3 string `orm:"column(type_3)"`
+}
+
+// Update 更新GprRisk指标基础信息
+func (item *BaseFromGprRiskIndex) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(item, cols...)
+	return
+}
+
+// EditGprRiskIndexInfoResp 新增指标的返回
+type EditGprRiskIndexInfoResp struct {
+	BaseFromGprRiskIndexId int    `description:"指标ID"`
+	IndexCode              string `description:"指标code"`
+}
+
+type GprRiskIndexSource2EdbReq struct {
+	EdbCode       string
+	EdbName       string
+	Frequency     string
+	Unit          string
+	ClassifyId    int
+	AdminId       int
+	AdminRealName string
+}
+
+func GetGprRiskFrequencyByClassifyId(classifyId int) (items []*GlFrequency, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT frequency FROM base_from_gpr_risk_index WHERE classify_id = ? `
+	sql += ` GROUP BY frequency ORDER BY frequency ASC `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}

+ 149 - 0
models/data_manage/base_from_gpr_risk_classify.go

@@ -0,0 +1,149 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// BaseFromUsdaFasClassify UsdaFas原始数据分类表
+type BaseFromGprRiskClassify struct {
+	ClassifyId      int       `orm:"column(classify_id);pk"`
+	ClassifyName    string    `description:"分类名称"`
+	ParentId        int       `description:"父级id"`
+	SysUserId       int       `description:"创建人id"`
+	SysUserRealName string    `description:"创建人姓名"`
+	Level           int       `description:"层级"`
+	Sort            int       `description:"排序字段,越小越靠前,默认值:10"`
+	ModifyTime      time.Time `description:"修改时间"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+// GetBaseFromGprRiskClassifyCount 获取分类名称的个数
+func GetBaseFromGprRiskClassifyCount(classifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM base_from_gpr_risk_classify WHERE classify_name=? AND parent_id=? `
+	err = o.Raw(sql, classifyName, parentId).QueryRow(&count)
+	return
+}
+
+// GetBaseFromGprRiskClassifyById 通过分类id的获取分类信息
+func GetBaseFromGprRiskClassifyById(classifyId int) (item *BaseFromGprRiskClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_gpr_risk_classify WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+// GetBaseFromGprRiskClassifyById 通过分类id的获取分类信息
+func GetBaseFromGprRiskClassifyByIds(classifyIds []int) (items []*BaseFromGprRiskClassify, err error) {
+	if len(classifyIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_gpr_risk_classify WHERE classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) `
+	_, err = o.Raw(sql, classifyIds).QueryRows(&items)
+	return
+}
+
+// EditBaseFromGprRiskClassify 修改GprRisk原始数据分类
+func EditBaseFromGprRiskClassify(classifyId int, classifyName string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE base_from_gpr_risk_classify SET classify_name=?,modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	return
+}
+
+// UpdateBaseFromGprRiskClassifySort 修改GprRisk原始数据分类的排序
+func UpdateBaseFromGprRiskClassifySort(classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE base_from_gpr_risk_classify SET sort=classify_id, modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+type BaseFromGprRiskClassifyItems struct {
+	ClassifyId             int    `description:"分类ID"`
+	BaseFromGprRiskIndexId int    `description:"指标类型ID"`
+	IndexCode              string `description:"指标唯一编码"`
+	ClassifyName           string `description:"分类名称"`
+	ClassifyNameEn         string `description:"分类名称"`
+	UniqueCode             string `description:"分类唯一编码"`
+	ParentId               int    `description:"父级id"`
+	Level                  int    `description:"层级"`
+	Sort                   int    `description:"排序字段,越小越靠前,默认值:10"`
+	Children               []*BaseFromGprRiskClassifyItems
+}
+
+type BaseFromGprRiskClassifyNameItems struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级id"`
+}
+
+type BaseFromGprRiskClassifyResp struct {
+	List []*BaseFromGprRiskClassifyItems
+}
+
+type BaseFromGprRiskClassifyNameResp struct {
+	List []*BaseFromGprRiskClassifyNameItems
+}
+
+type BaseFromGprRiskClassifyItemsButton struct {
+	AddButton    bool `description:"是否可添加"`
+	OpButton     bool `description:"是否可编辑"`
+	DeleteButton bool `description:"是否可删除"`
+	MoveButton   bool `description:"是否可移动"`
+}
+
+// GetBaseFromGprRiskClassifyByParentId 根据上级id获取当下的分类列表数据
+func GetBaseFromGprRiskClassifyByParentId(parentId int) (items []*BaseFromGprRiskClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_gpr_risk_classify WHERE parent_id=? order by sort asc,classify_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetAllBaseFromGprRiskClassify 获取所有的分类列表数据
+func GetAllBaseFromGprRiskClassify() (items []*BaseFromGprRiskClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_gpr_risk_classify order by parent_id asc, sort asc,classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type DeleteBaseFromGprRiskClassifyReq struct {
+	ClassifyId int `description:"分类id"`
+	EdbInfoId  int `description:"指标id"`
+}
+
+type BaseFromGprRiskClassifyListResp struct {
+	AllNodes      []*BaseFromGprRiskClassifyItems
+	CanOpClassify bool `description:"是否允许操作分类"`
+}
+
+type BaseFromGprRiskClassifySimplify struct {
+	ClassifyId   int    `description:"分类id"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int
+}
+
+// GetFirstBaseFromGprRiskClassify 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstBaseFromGprRiskClassify() (item *BaseFromGprRiskClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_gpr_risk_classify order by sort asc,classify_id asc limit 1`
+	err = o.Raw(sql).QueryRow(&item)
+	return
+}
+
+// Update 更新分类基础信息
+func (BaseFromGprRiskClassify *BaseFromGprRiskClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(BaseFromGprRiskClassify, cols...)
+	return
+}
+
+type AddGprRiskClassifyResp struct {
+	ClassifyId int
+}

+ 21 - 14
models/data_manage/base_from_mtjh.go

@@ -2,6 +2,7 @@ package data_manage
 
 import (
 	"eta/eta_api/utils"
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"time"
@@ -35,16 +36,16 @@ type BaseFromMtjhIndex struct {
 }
 
 type BaseFromMtjhIndexItem struct {
-	BaseFromMtjhIndexId int       `orm:"column(base_from_mtjh_index_id);pk"`
-	IndexName           string    `description:"持买单量指标名称"`
-	IndexCode           string    `description:"持买单量指标编码"`
-	DealValue           string    `description:"成交量"`
-	DataTime            string    `description:"数据日期"`
-	Area                string    `description:"区域"`
-	Port                string    `description:"港口或码头"`
-	Unit                string    `description:"单位"`
-	Frequency           string    `description:"频率"`
-	Variety             string    `description:"品种"`
+	BaseFromMtjhIndexId int    `orm:"column(base_from_mtjh_index_id);pk"`
+	IndexName           string `description:"持买单量指标名称"`
+	IndexCode           string `description:"持买单量指标编码"`
+	DealValue           string `description:"成交量"`
+	DataTime            string `description:"数据日期"`
+	Area                string `description:"区域"`
+	Port                string `description:"港口或码头"`
+	Unit                string `description:"单位"`
+	Frequency           string `description:"频率"`
+	Variety             string `description:"品种"`
 	CreateTime          string `description:"插入时间"`
 	ModifyTime          string `description:"修改时间"`
 }
@@ -143,7 +144,6 @@ type BaseFromMtjhIndexList struct {
 	Paging                *paging.PagingItem `description:"分页数据"`
 }
 
-
 func GetMtjhIndexDataCount(indexCode string) (count int, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT COUNT(1) AS count  FROM base_from_mtjh_index WHERE index_code=? `
@@ -151,7 +151,6 @@ func GetMtjhIndexDataCount(indexCode string) (count int, err error) {
 	return
 }
 
-
 func GetMtjhIndexData(indexCode string, startSize, pageSize int) (items []*BaseFromMtjhIndexItem, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT *  FROM base_from_mtjh_index WHERE index_code=? ORDER BY data_time DESC LIMIT ?,? `
@@ -187,7 +186,7 @@ func GetMtjhMappingItemByCode(indexCode string) (item *BaseFromMtjhMappingItem,
 func GetMtjhFrequencyByArea(area string) (items []*string, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := "SELECT frequency FROM base_from_mtjh_index WHERE area=?  group by area "
-	_,err = o.Raw(sql, area).QueryRows(&items)
+	_, err = o.Raw(sql, area).QueryRows(&items)
 	return
 }
 
@@ -233,4 +232,12 @@ func GetMtjhIndexLatestDate(indexCode string) (ModifyTime string, err error) {
 	sql := ` SELECT modify_time FROM base_from_mtjh_index WHERE index_code=? ORDER BY modify_time DESC limit 1 `
 	err = o.Raw(sql, indexCode).QueryRow(&ModifyTime)
 	return
-}
+}
+
+// GetMtjhItemsByCondition 获取煤炭江湖指标
+func GetMtjhItemsByCondition(cond string, pars []interface{}) (items []*BaseFromMtjhMappingItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM base_from_mtjh_mapping WHERE 1=1 %s`, cond)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 1 - 1
models/data_manage/base_from_smm_classify.go

@@ -98,7 +98,7 @@ type BaseFromSmmClassifyItems struct {
 	ParentId             int    `description:"父级id"`
 	Level                int    `description:"层级"`
 	Sort                 int    `description:"排序字段,越小越靠前,默认值:10"`
-	//UniqueCode         string                         `description:"唯一编码"`
+	UniqueCode           string `description:"唯一编码"`
 	//ModifyTime         time.Time                      `description:"修改时间"`
 	//CreateTime         time.Time                      `description:"创建时间"`
 	Children []*BaseFromSmmClassifyItems `description:"下级"`

+ 7 - 0
models/data_manage/chart_framework.go

@@ -4,6 +4,7 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"strings"
 	"time"
 )
@@ -356,3 +357,9 @@ type ChartFrameworkPublicMenuItem struct {
 	MenuName   string                `description:"目录名称"`
 	Frameworks []*ChartFrameworkItem `description:"框架列表"`
 }
+
+// ChartFrameworkListResp 图库框架分页列表
+type ChartFrameworkListResp struct {
+	Paging *paging.PagingItem
+	List   []*ChartFrameworkItem
+}

+ 4 - 3
models/data_manage/chart_info.go

@@ -65,8 +65,9 @@ type ChartInfo struct {
 
 type ChartInfoMore struct {
 	ChartInfo
-	IsEnChart     bool `description:"是否展示英文标识"`
-	HaveOperaAuth bool `description:"是否有数据权限,默认:false"`
+	IsEnChart     bool   `description:"是否展示英文标识"`
+	HaveOperaAuth bool   `description:"是否有数据权限,默认:false"`
+	SearchText    string `description:"搜索结果(含高亮)"`
 }
 
 func AddChartInfo(item *ChartInfo) (lastId int64, err error) {
@@ -1723,7 +1724,7 @@ func ChartInfoSearchByKeyWord(keyword string, showSysId int) (searchList []*Char
 }
 
 // ChartInfoSearchByEmptyKeyWord 没有关键字的时候获取默认100条数据
-func ChartInfoSearchByEmptyKeyWord(showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (total int64, searchList []*ChartInfo, err error) {
+func ChartInfoSearchByEmptyKeyWord(showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (total int64, searchList []*ChartInfoMore, err error) {
 	num := len(sourceList)
 	o := orm.NewOrmUsingDB("data")
 

+ 9 - 1
models/data_manage/data_manage_permission/excel.go

@@ -1043,4 +1043,12 @@ func GetExcelPermissionByExcelIdAndUserId(excelId, userId int) (items []*ExcelIn
 	sql := `SELECT * FROM excel_info_permission WHERE excel_info_id = ? AND sys_user_id = ?`
 	_, err = o.Raw(sql, excelId, userId).QueryRows(&items)
 	return
-}
+}
+
+// GetExcelInfoDataNoPermissionByUserId 获取用户所有无权限表格
+func GetExcelInfoDataNoPermissionByUserId(userId, source int) (items []*DataPermissionNoAuthRecord, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT excel_info_permission_no_auth_record_id as data_permission_no_auth_record_id,op_unique_code,source as sub_source,excel_info_id as data_id,excel_name as data_name,sys_user_id,create_time FROM excel_info_permission_no_auth_record WHERE sys_user_id = ? AND source = ? ORDER BY excel_info_permission_no_auth_record_id desc`
+	_, err = o.Raw(sql, userId, source).QueryRows(&items)
+	return
+}

+ 2 - 0
models/data_manage/edb_classify.go

@@ -76,6 +76,7 @@ func GetEdbClassifyEnCount(classifyNameEn string, parentId int, classifyType uin
 type EditEdbClassifyReq struct {
 	ClassifyName string `description:"分类名称"`
 	ClassifyId   int    `description:"分类名称"`
+	ParentId     int    `description:"父级分类id"`
 }
 
 func GetEdbClassifyById(classifyId int) (item *EdbClassify, err error) {
@@ -253,6 +254,7 @@ type EdbClassifyItems struct {
 	Button           EdbClassifyItemsButton `description:"操作权限"`
 	IsJoinPermission int                    `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth    bool                   `description:"是否有数据权限"`
+	SelectDisable    bool                   `description:"是否可以被选中 前端用"`
 }
 
 type EdbClassifyIdItems struct {

+ 6 - 6
models/data_manage/edb_data_wind.go

@@ -80,14 +80,14 @@ func EdbInfoUpdateStatusByEdbInfoId(edbInfoIds []int, isStop int, calculateEdbIn
 
 	// 更改指标的更新状态
 	if len(edbInfoIds) == 1 {
-		sql := ` UPDATE edb_info SET no_update = ? WHERE edb_info_id=? `
-		_, err = o.Raw(sql, isStop, edbInfoIds[0]).Exec()
+		sql := ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id=? `
+		_, err = o.Raw(sql, isStop, time.Now(), 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()
+		sql := ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), edbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -95,8 +95,8 @@ func EdbInfoUpdateStatusByEdbInfoId(edbInfoIds []int, isStop int, calculateEdbIn
 
 	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()
+		sql := ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), calculateEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}

+ 51 - 35
models/data_manage/edb_info.go

@@ -38,35 +38,37 @@ type EdbInfo struct {
 	CreateTime       time.Time
 	ModifyTime       time.Time
 	BaseModifyTime   time.Time
-	MinValue         float64 `description:"指标最小值"`
-	MaxValue         float64 `description:"指标最大值"`
-	CalculateFormula string  `description:"计算公式"`
-	EdbType          int     `description:"指标类型:1:基础指标,2:计算指标"`
-	Sort             int     `description:"排序字段"`
-	LatestDate       string  `description:"数据最新日期(实际日期)"`
-	LatestValue      float64 `description:"数据最新值(实际值)"`
-	EndValue         float64 `description:"数据的最新值(预测日期的最新值)"`
-	MoveType         int     `description:"移动方式:1:领先(默认),2:滞后"`
-	MoveFrequency    string  `description:"移动频度"`
-	NoUpdate         int8    `description:"是否停止更新,0:继续更新;1:停止更新"`
-	ServerUrl        string  `description:"服务器地址"`
-	ChartImage       string  `description:"图表图片"`
-	Calendar         string  `description:"公历/农历" orm:"default(公历);"`
-	DataDateType     string  `orm:"column(data_date_type);size(255);null;default(交易日)"`
-	ManualSave       int     `description:"是否有手动保存过上下限: 0-否; 1-是"`
-	EmptyType        int     `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
-	MaxEmptyType     int     `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
-	TerminalCode     string  `description:"终端编码,用于配置在机器上"`
-	DataUpdateTime   string  `description:"最近一次数据发生变化的时间"`
-	ErDataUpdateDate string  `description:"本次更新,数据发生变化的最早日期"`
-	SourceIndexName  string  `description:"数据源中的指标名称"`
-	SubSource        int     `description:"子数据来源:0:经济数据库,1:日期序列"`
-	SubSourceName    string  `description:"子数据来源名称"`
-	IndicatorCode    string  `description:"指标代码"`
-	StockCode        string  `description:"证券代码"`
-	Extra            string  `description:"指标额外配置"`
-	IsJoinPermission int     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
-	IsStaticData     int     `description:"是否是静态指标,0否,1是"`
+	MinValue         float64   `description:"指标最小值"`
+	MaxValue         float64   `description:"指标最大值"`
+	CalculateFormula string    `description:"计算公式"`
+	EdbType          int       `description:"指标类型:1:基础指标,2:计算指标"`
+	Sort             int       `description:"排序字段"`
+	LatestDate       string    `description:"数据最新日期(实际日期)"`
+	LatestValue      float64   `description:"数据最新值(实际值)"`
+	EndValue         float64   `description:"数据的最新值(预测日期的最新值)"`
+	MoveType         int       `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string    `description:"移动频度"`
+	NoUpdate         int8      `description:"是否停止更新,0:继续更新;1:停止更新"`
+	ServerUrl        string    `description:"服务器地址"`
+	ChartImage       string    `description:"图表图片"`
+	Calendar         string    `description:"公历/农历" orm:"default(公历);"`
+	DataDateType     string    `orm:"column(data_date_type);size(255);null;default(交易日)"`
+	ManualSave       int       `description:"是否有手动保存过上下限: 0-否; 1-是"`
+	EmptyType        int       `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int       `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	TerminalCode     string    `description:"终端编码,用于配置在机器上"`
+	DataUpdateTime   string    `description:"最近一次数据发生变化的时间"`
+	ErDataUpdateDate string    `description:"本次更新,数据发生变化的最早日期"`
+	SourceIndexName  string    `description:"数据源中的指标名称"`
+	SubSource        int       `description:"子数据来源:0:经济数据库,1:日期序列"`
+	SubSourceName    string    `description:"子数据来源名称"`
+	IndicatorCode    string    `description:"指标代码"`
+	StockCode        string    `description:"证券代码"`
+	Extra            string    `description:"指标额外配置"`
+	IsJoinPermission int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	IsStaticData     int       `description:"是否是静态指标,0否,1是"`
+	SetUpdateTime    time.Time `description:"设置启用/禁用刷新状态的时间"`
+	EndDateType      int       `description:"预测指标截止日期类型:0:未来日期,1未来期数"`
 }
 
 type EdbInfoFullClassify struct {
@@ -469,6 +471,9 @@ type EdbInfoList struct {
 	IsSupplierStop   int                     `description:"是否供应商停更:1:停更,0:未停更"`
 	MoveType         int                     `description:"移动方式:1:领先(默认),2:滞后"`
 	MoveFrequency    string                  `description:"移动频度"`
+	MinValue         float64                 `description:"最小值"`
+	MaxValue         float64                 `description:"最大值"`
+	SearchText       string                  `description:"搜索结果(含高亮)"`
 }
 
 type EdbDataInsertConfigItem struct {
@@ -1464,9 +1469,12 @@ func GetEdbInfoListByCondition(condition string, pars []interface{}, startSize,
 
 	sql += ` ORDER BY edb_info_id `
 	sql += orderDesc
-	sql += ` LIMIT ?,? `
+	if pageSize > 0 {
+		sql += ` LIMIT ?,? `
+		pars = append(pars, startSize, pageSize)
+	}
 
-	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
 }
 
@@ -1821,15 +1829,15 @@ func ModifyEdbInfoUpdateStatus(edbIdList []int, isStop int, calculateEdbInfoIds
 	}()
 
 	// 更改指标的更新状态
-	sql := ` UPDATE edb_info SET no_update = ? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(idNum) + `) `
-	_, err = o.Raw(sql, isStop, edbIdList).Exec()
+	sql := ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(idNum) + `) `
+	_, err = o.Raw(sql, isStop, time.Now(), edbIdList).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()
+		sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), calculateEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -2130,3 +2138,11 @@ type EdbNameCheckResult struct {
 	EdbName string
 	Exist   bool
 }
+
+// UpdateEdbClassifyIdByChartInfoId
+func UpdateEdbClassifyIdByChartInfoId(chartInfoIds []int, classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update edb_info set classify_id = ? WHERE edb_info_id in (` + utils.GetOrmInReplace(len(chartInfoIds)) + `) `
+	_, err = o.Raw(sql, classifyId, chartInfoIds).Exec()
+	return
+}

+ 11 - 11
models/data_manage/edb_info_relation.go

@@ -104,8 +104,8 @@ func AddOrUpdateEdbInfoRelation(objectId, objectType int, relationList []*EdbInf
 
 	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()
+		sql := ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -172,8 +172,8 @@ func AddOrUpdateEdbInfoRelationMulti(relationList []*EdbInfoRelation, refreshEdb
 
 	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()
+		sql := ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -386,7 +386,7 @@ func ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo *EdbInfo, edbRelationIds []
 		return
 	}
 
-	sourceWhere := ` and (refer_object_type in (1,2 ) or (refer_object_type=4 and refer_object_sub_type !=5) )` //平衡表和事件日历中的直接引用无需替换,
+	sourceWhere := ` and (refer_object_type in (1,2,5) 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()
@@ -416,8 +416,8 @@ func ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo *EdbInfo, edbRelationIds []
 
 	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()
+		sql := ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -490,8 +490,8 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 
 	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()
+		sql = ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -500,7 +500,7 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 	//更新数据源钢联化工指标
 	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`
+		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
@@ -513,7 +513,7 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 			relationCodes = append(relationCodes, relationCode)
 		}
 		if len(relationCodes) > 0 {
-			sql := ` UPDATE edb_info_relation e1  
+			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  

+ 32 - 3
models/data_manage/excel/excel_info.go

@@ -34,6 +34,7 @@ type ExcelInfo struct {
 	RelExcelInfoId     int       `description:"平衡表里静态表关联的动态表excel id"`
 	VersionName        string    `description:"静态表版本名称"`
 	SourcesFrom        string    `description:"图表来源"`
+	ExtraConfig        string    `description:"额外配置:如多空分析、相关性表格参数"`
 }
 
 // Update 更新 excel表格基础信息
@@ -89,11 +90,12 @@ func AddExcelInfo(excelInfo *ExcelInfo, excelEdbMappingList []*ExcelEdbMapping,
 	if childExcel != nil {
 		// 表格信息入库
 		childExcel.ParentId = excelInfo.ExcelInfoId
-		_, err = o.Insert(childExcel)
-		if err != nil {
-			err = fmt.Errorf("新增子表失败:%v", err)
+		childId, e := o.Insert(childExcel)
+		if e != nil {
+			err = fmt.Errorf("新增子表失败:%v", e)
 			return
 		}
+		childExcel.ExcelInfoId = int(childId)
 	}
 	// excel与指标的关联关系
 	dataNum := len(excelEdbMappingList)
@@ -813,3 +815,30 @@ func UpdateExcelInfoClassifyIdByIds(classifyId int, excelIds []int) (err error)
 	_, err = o.Raw(sql, classifyId, excelIds).Exec()
 	return
 }
+
+// SearchExcelInfo 表格搜索
+type SearchExcelInfo struct {
+	ExcelInfo
+	SearchText    string                `description:"搜索结果(含高亮)"`
+	HaveOperaAuth bool                  `description:"是否有数据权限"`
+	Button        ExcelInfoDetailButton `description:"操作权限"`
+	CanEdit       bool                  `description:"是否可编辑"`
+	Editor        string                `description:"编辑人"`
+}
+
+func (m *ExcelInfo) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ExcelInfoId = int(id)
+	return
+}
+
+func (m *ExcelInfo) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM excel_info WHERE 1=1 %s`, condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}

+ 8 - 0
models/data_manage/excel/referenced_excel_config.go

@@ -37,6 +37,14 @@ func AddReferencedExcelConfig(items []*ReferencedExcelConfig) (err error) {
 	return
 }
 
+// getByCode
+func GetReferencedExcelConfigByUniqueCode(uniqueCode string) (item ReferencedExcelConfig, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM referenced_excel_config WHERE referenced_excel_unique_code = ? `
+	err = o.Raw(sql, uniqueCode).QueryRow(&item)
+	return
+}
+
 // getByCode
 func GetReferencedExcelConfig(referencedId, fromScene int, uniqueCode, uuid string) (item ReferencedExcelConfig, err error) {
 	o := orm.NewOrmUsingDB("data")

+ 6 - 0
models/data_manage/excel/response/excel_info.go

@@ -127,3 +127,9 @@ type ShareExcelInfoDetail struct {
 type ExcelRuleListResp struct {
 	List []*excel2.ExcelInfoRuleMappingView
 }
+
+// SearchExcelListResp 搜索表格列表返回数据
+type SearchExcelListResp struct {
+	Paging *paging.PagingItem
+	List   []*excel2.SearchExcelInfo
+}

+ 33 - 7
models/data_manage/gl_data.go

@@ -2,6 +2,7 @@ package data_manage
 
 import (
 	"eta/eta_api/utils"
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
 )
@@ -114,13 +115,21 @@ func GetGlIndexDataCount(indexCode string) (count int, err error) {
 }
 
 type GlSearchIndex struct {
-	Id             int    `orm:"column(ID)"`
-	IndexCode      string `orm:"column(INDEX_CODE)"`
-	IndexName      string `orm:"column(INDEX_NAME)"`
-	UnitName       string `orm:"column(UNIT_NAME)"`
-	FrequencyName  string `orm:"column(FREQUENCY_NAME)"`
-	UpdateTime     string `orm:"column(UPDATE_TIME)"`
-	BreedShortName string `orm:"column(BREED_SHORT_NAME)"`
+	Id             int     `orm:"column(ID)"`
+	IndexCode      string  `orm:"column(INDEX_CODE)"`
+	IndexName      string  `orm:"column(INDEX_NAME)"`
+	UnitName       string  `orm:"column(UNIT_NAME)"`
+	FrequencyName  string  `orm:"column(FREQUENCY_NAME)"`
+	UpdateTime     string  `orm:"column(UPDATE_TIME)"`
+	BreedShortName string  `orm:"column(BREED_SHORT_NAME)"`
+	StartDate      string  `orm:"column(BEGIN_DATE)"`
+	EndDate        string  `orm:"column(END_DATE)"`
+	LatestValue    float64 `orm:"column(DATA_VALUE)"`
+	CreateTime     string  `orm:"column(CREATE_TIME)"`
+	ModifyTime     string  `orm:"column(UPDATE_TIME)"`
+	Source         int
+	SourceName     string
+	SearchText     string `description:"搜索结果(含高亮)"`
 }
 
 // GetGlItemList 模糊查询隆众数据库指标列表
@@ -155,3 +164,20 @@ GROUP BY
 	_, err = o.Raw(sql, indexCode).QueryRows(&items)
 	return
 }
+
+// GlDataPageListResp 钢联原始指标库-分页列表
+type GlDataPageListResp struct {
+	Paging *paging.PagingItem
+	List   []*GlSearchIndex
+}
+
+// GetGlItemListByIds IDs获取钢联原始指标库
+func GetGlItemListByIds(ids []int) (items []*GlSearchIndex, err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("gl")
+	sql := fmt.Sprintf(`SELECT * FROM mb_index_main_info WHERE ID IN (%s)`, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).QueryRows(&items)
+	return
+}

+ 1 - 0
models/data_manage/my_chart.go

@@ -329,6 +329,7 @@ type MyChartList struct {
 	Source              int    `description:"1:ETA图库;2:商品价格曲线"`
 	IsJoinPermission    int    `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth       bool   `description:"是否有数据权限,默认:false"`
+	SearchText          string `description:"搜索结果(含高亮)"`
 }
 
 type MyChartListResp struct {

+ 10 - 10
models/data_manage/mysteel_chemical_index.go

@@ -620,8 +620,8 @@ func ModifyMysteelChemicalUpdateStatus(edbIdList []int, indexCodeList []string,
 	}
 
 	// 更改指标的更新状态
-	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND sub_source= ? AND edb_code IN (` + utils.GetOrmInReplace(codeNum) + `) `
-	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, indexCodeList).Exec()
+	sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE source = ? AND sub_source= ? AND edb_code IN (` + utils.GetOrmInReplace(codeNum) + `) `
+	_, err = o.Raw(sql, isStop, time.Now(), utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, indexCodeList).Exec()
 	if err != nil {
 		return
 	}
@@ -658,15 +658,15 @@ func ModifyMysteelChemicalUpdateStatusByEdbInfoId(edbInfoId, isStop int, edbCode
 	}
 
 	// 更改指标的更新状态
-	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()
+	sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE source = ? AND sub_source= ? AND edb_info_id=? `
+	_, err = o.Raw(sql, isStop, time.Now(), 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()
+		sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), calculateEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -696,15 +696,15 @@ func ModifyMysteelChemicalUpdateStatusByEdbInfoIds(edbInfoIds []int, isStop int,
 	}
 
 	// 更改指标的更新状态
-	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()
+	sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE source = ? AND edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+	_, err = o.Raw(sql, isStop, time.Now(), 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()
+		sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), calculateEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}

+ 3 - 0
models/data_manage/predict_edb_conf.go

@@ -19,6 +19,7 @@ type PredictEdbConf struct {
 	EndDate          time.Time `description:"截止日期"`
 	ModifyTime       time.Time `description:"修改时间"`
 	CreateTime       time.Time `description:"添加时间"`
+	EndNum           int       `description:"截止期数"`
 }
 
 // PredictEdbConfDetail 预测规则 和 规则相关联的指标
@@ -35,6 +36,7 @@ type PredictEdbConfDetail struct {
 	ModifyTime       time.Time                               `description:"修改时间"`
 	CreateTime       time.Time                               `description:"添加时间"`
 	CalculateList    []*PredictEdbConfCalculateMappingDetail `description:"配置与指标的关联信息"`
+	EndNum           int                                     `description:"截止期数"`
 }
 
 // PredictEdbConfAndData 预测规则和其对应的动态数据
@@ -46,6 +48,7 @@ type PredictEdbConfAndData struct {
 	FixedValue       float64        `description:"固定值"`
 	Value            string         `description:"配置的值"`
 	EndDate          time.Time      `description:"截止日期"`
+	EndNum           int            `description:"截止期数"`
 	ModifyTime       time.Time      `description:"修改时间"`
 	CreateTime       time.Time      `description:"添加时间"`
 	DataList         []*EdbDataList `description:"动态数据"`

+ 74 - 0
models/data_manage/request/clarksons_data.go

@@ -0,0 +1,74 @@
+package request
+
+type AddBaseFromClarksonsClassifyReq struct {
+	ParentId     int    `description:"上级id"`
+	ClassifyName string `description:"分类名称"`
+}
+
+// DelBaseFromClarksonsReq 删除卓创红期分类
+type DelBaseFromClarksonsClassifyReq struct {
+	BaseFromClassifyId int `description:"分类id"`
+}
+
+// EditBaseFromSciClassifyReq 编辑卓创红期分类
+type EditBaseFromClarksonsClassifyReq struct {
+	ClassifyName       string `description:"分类名称"`
+	BaseFromClassifyId int    `description:"分类id"`
+}
+
+// EditBaseFromClarksonsReq 编辑卓创红期指标所属分类
+type EditBaseFromClarksonsReq struct {
+	BaseFromClarksonsIndexId int `description:"指标id"`
+	BaseFromClassifyId       int `description:"分类id"`
+}
+
+// DelBaseFromClarksonsReq 删除卓创红期指标
+type DelBaseFromClarksonsReq struct {
+	BaseFromClarksonsIndexId int `description:"指标id"`
+}
+
+// ResetBaseFromClarksonsReq 重置指标所属分类
+type ResetBaseFromClarksonsReq struct {
+	BaseFromClarksonsIndexId int `description:"指标id"`
+}
+
+// ClarksonsDataBatchAddCheckReq 卓创红期指标批量添加校验
+type ClarksonsDataBatchAddCheckReq struct {
+	// MysteelChemicalDataListReq
+	IndexCodes []string `description:"指标编码"`
+}
+
+// ClarksonsDataBatchListReq 卓创红期指标批量列表
+type ClarksonsDataBatchListReq struct {
+	ClassifyIds  string `description:"分类id"`
+	KeyWord      string `description:"关键字"`
+	Frequencies  string `description:"频度"`
+	SelectedId   []int  `description:"已选指标id, 为true时表示反选"`
+	IsSelectAll  bool   `description:"是否查询全部, 默认false, true:全选, false:查询已选"`
+	PageSize     int    `description:"每页条数"`
+	CurrentIndex int    `description:"当前页"`
+}
+
+// MoveBaseFromClarksonsClassifyReq 移动分类请求参数
+type MoveBaseFromClarksonsClassifyReq struct {
+	BaseFromClassifyId int `description:"分类id"`
+	ParentClassifyId   int `description:"父级分类id"`
+	PrevClassifyId     int `description:"上一个兄弟节点分类id"`
+	NextClassifyId     int `description:"下一个兄弟节点分类id"`
+}
+
+// MoveBaseFromClarksonsReq 移动指标请求参数
+type MoveBaseFromClarksonsReq struct {
+	BaseFromClassifyId           int `description:"分类id"`
+	BaseFromClarksonsIndexId     int `description:"指标id"`
+	PrevBaseFromClarksonsIndexId int `description:"上一个兄弟节点id"`
+	NextBaseFromClarksonsIndexId int `description:"下一个兄弟节点id"`
+}
+
+// ExportClarksonsExcelReq导出卓创红期excel指标
+type ExportClarksonsExcelReq struct {
+	KeyWord            string   `description:"关键字, 指标编码或指标ID"`
+	IndexCode          []string `description:"指标编码,全选时,表示反选"`
+	IsSelectedAll      bool     `description:"是否全选:true:全选|false: 无"`
+	BaseFromClassifyId int      `description:"指标id"`
+}

+ 12 - 0
models/data_manage/request/edb_info.go

@@ -6,3 +6,15 @@ type ModifyEdbInfoReq struct {
 	MaxValue  float64 `description:"最大值"`
 	MinValue  float64 `description:"最小值"`
 }
+
+// ModifyEdbListReq 批量编辑指标请求参数
+type ModifyEdbListReq struct {
+	SelectAll        bool
+	SubClassify      bool `description:"是否关联指标子分类"`
+	EdbClassifyIds string
+	SysUserIds       string
+	KeyWord          string
+	EdbInfoIds       string `description:"图表ID"`
+	ClassifyId       int    `description:"新图表分类"`
+	Sources          string
+}

+ 9 - 7
models/data_manage/request/predict_edb_info.go

@@ -18,6 +18,7 @@ type PredictEdbInfoChartDataReq struct {
 	RuleList     []RuleConfig `description:"配置规则列表"`
 	DataDateType string       `description:"数据日期类型,枚举值:交易日、自然日"`
 	StartYear    int          `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+	EndDateType  int          `description:"截止日期类型:0:未来日期,1未来期数"`
 }
 
 // AddPredictEdbInfoReq 添加预测指标请求
@@ -29,13 +30,13 @@ type AddPredictEdbInfoReq struct {
 	RuleType        int          `description:"预测规则,1:最新,2:固定值"`
 	FixedValue      float64      `description:"固定值"`
 	RuleList        []RuleConfig `description:"配置规则列表"`
-
-	DataDateType string  `description:"日期类型,枚举值:交易日、自然日"`
-	MaxValue     float64 `description:"最大值"`
-	MinValue     float64 `description:"最小值"`
-	EdbInfoId    int     `description:"指标ID"`
-	AdminId      int     `description:"添加人id"`
-	AdminName    string  `description:"添加人名称"`
+	DataDateType    string       `description:"日期类型,枚举值:交易日、自然日"`
+	MaxValue        float64      `description:"最大值"`
+	MinValue        float64      `description:"最小值"`
+	EdbInfoId       int          `description:"指标ID"`
+	AdminId         int          `description:"添加人id"`
+	AdminName       string       `description:"添加人名称"`
+	EndDateType     int          `description:"截止日期类型:0:未来日期,1未来期数"`
 }
 
 // RuleConfig 预测规则配置
@@ -45,6 +46,7 @@ type RuleConfig struct {
 	EmptyType    int                          `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 	MaxEmptyType int                          `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	EndDate      string                       `description:"截止日期"`
+	EndNum       int                          `description:"截止期数"`
 	EdbInfoIdArr []data_manage.EdbInfoFromTag `description:"指标信息"`
 }
 

+ 35 - 0
models/data_manage/response/clarksons_data.go

@@ -0,0 +1,35 @@
+package response
+
+import (
+	"eta/eta_api/models/data_manage"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type ClarksonsIndexPageListResp struct {
+	List   []*data_manage.BaseFromClarksonsIndexView
+	Paging *paging.PagingItem
+}
+
+type EditClarksonsIndexInfoResp struct {
+	BaseFromClarksonsIndexId int
+	IndexCode            string
+}
+
+type ClarksonsSingleDataResp struct {
+	BaseFromClarksonsIndexId int
+	ClassifyId           int
+	EdbInfoId            int
+	Name                 string
+	IndexCode            string
+	IndexName            string
+	Frequency            string
+	Unit                 string
+	ApiStartTime         string
+	ApiUpdateTime        string
+	StartTime            string
+	FinishTime           string
+	CreateTime           string
+	ModifyTime           string
+	Data                 []*data_manage.BaseFromClarksonsData
+}

+ 3 - 2
models/data_manage/smm_data.go

@@ -4,6 +4,7 @@ import (
 	"eta/eta_api/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
 )
 
 type SmmClassify struct {
@@ -341,8 +342,8 @@ func ModifySmmUpdateStatus(edbIdList []int, indexCodeList []string, isStop int)
 	}
 
 	// 更改指标的更新状态
-	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND sub_source= ? AND edb_code IN (` + utils.GetOrmInReplace(codeNum) + `) `
-	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_YS, 0, indexCodeList).Exec()
+	sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE source = ? AND sub_source= ? AND edb_code IN (` + utils.GetOrmInReplace(codeNum) + `) `
+	_, err = o.Raw(sql, isStop, time.Now(), utils.DATA_SOURCE_YS, 0, indexCodeList).Exec()
 	if err != nil {
 		return
 	}

+ 10 - 0
models/data_manage/trade_analysis/base_from_trade_exchange.go

@@ -120,3 +120,13 @@ func (m *BaseFromTradeExchange) Format2Item() (item *BaseFromTradeExchangeItem)
 	item.Sort = m.Sort
 	return
 }
+
+// TradeExchangeClassifyNode 交易所-分类树节点
+type TradeExchangeClassifyNode struct {
+	UniqueFlag string                       `description:"唯一标识"`
+	ParentFlag string                       `description:"父级标识"`
+	NodeType   int                          `description:"类型: 1-交易所; 2-品种"`
+	NodeName   string                       `description:"节点名称"`
+	NodeValue  string                       `description:"节点实际值"`
+	Children   []*TradeExchangeClassifyNode `description:"子节点"`
+}

+ 35 - 0
models/data_manage/trade_analysis/request/trade_analysis_correlation.go

@@ -0,0 +1,35 @@
+package request
+
+import tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
+
+// CorrelationTablePreviewReq 相关性表格预览
+type CorrelationTablePreviewReq struct {
+	TableName   string                                         `description:"表格名称"`
+	ClassifyId  int                                            `description:"分类ID"`
+	TableConfig tradeAnalysisModel.CorrelationTableExtraConfig `description:"表格参数"`
+}
+
+// CorrelationTableSaveReq 保存表格
+type CorrelationTableSaveReq struct {
+	ExcelInfoId int                                            `description:"表格ID"`
+	TableName   string                                         `description:"表格名称"`
+	ClassifyId  int                                            `description:"分类ID"`
+	TableConfig tradeAnalysisModel.CorrelationTableExtraConfig `description:"表格参数"`
+}
+
+// CorrelationTableSaveAsReq 表格另存为
+type CorrelationTableSaveAsReq struct {
+	ExcelInfoId int    `description:"表格ID"`
+	TableName   string `description:"表格名称"`
+	ClassifyId  int    `description:"分类ID"`
+}
+
+// CorrelationTableRefreshReq 刷新表格请求
+type CorrelationTableRefreshReq struct {
+	ExcelInfoId int `description:"表格ID"`
+}
+
+// CorrelationTableRemoveReq 删除表格请求
+type CorrelationTableRemoveReq struct {
+	ExcelInfoId int `description:"表格ID"`
+}

+ 37 - 0
models/data_manage/trade_analysis/request/trade_analysis_table.go

@@ -0,0 +1,37 @@
+package request
+
+import tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
+
+// TablePreviewReq 预览表格
+type TablePreviewReq struct {
+	TableName   string                                             `description:"表格名称"`
+	ClassifyId  int                                                `description:"分类ID"`
+	TableConfig tradeAnalysisModel.TableExtraConfig                `description:"表格参数"`
+	TableCols   []*tradeAnalysisModel.TradeAnalysisTableColumnItem `description:"自定义表头"`
+}
+
+// TableSaveReq 保存表格
+type TableSaveReq struct {
+	ExcelInfoId int                                                `description:"表格ID"`
+	TableName   string                                             `description:"表格名称"`
+	ClassifyId  int                                                `description:"分类ID"`
+	TableConfig tradeAnalysisModel.TableExtraConfig                `description:"表格参数"`
+	TableCols   []*tradeAnalysisModel.TradeAnalysisTableColumnItem `description:"自定义表头"`
+}
+
+// TableSaveAsReq 表格另存为
+type TableSaveAsReq struct {
+	ExcelInfoId int    `description:"表格ID"`
+	TableName   string `description:"表格名称"`
+	ClassifyId  int    `description:"分类ID"`
+}
+
+// TableRefreshReq 刷新表格请求
+type TableRefreshReq struct {
+	ExcelInfoId int `description:"表格ID"`
+}
+
+// TableRemoveReq 删除表格请求
+type TableRemoveReq struct {
+	ExcelInfoId int `description:"表格ID"`
+}

+ 17 - 0
models/data_manage/trade_analysis/response/trade_analysis_correlation.go

@@ -0,0 +1,17 @@
+package response
+
+import (
+	excelModel "eta/eta_api/models/data_manage/excel"
+	tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
+)
+
+// CorrelationTableResp 相关性表格响应
+type CorrelationTableResp struct {
+	ExcelInfoId int                                            `description:"表格ID"`
+	TableName   string                                         `description:"表格名称"`
+	ClassifyId  int                                            `description:"分类ID"`
+	ModifyTime  string                                         `description:"最近保存时间"`
+	TableConfig tradeAnalysisModel.CorrelationTableExtraConfig `description:"表格配置"`
+	TableData   []*tradeAnalysisModel.CorrelationTableData     `description:"表格数据"`
+	Button      excelModel.ExcelInfoDetailButton               `description:"表格的按钮权限"`
+}

+ 18 - 0
models/data_manage/trade_analysis/response/trade_analysis_table.go

@@ -0,0 +1,18 @@
+package response
+
+import (
+	excelModel "eta/eta_api/models/data_manage/excel"
+	tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
+)
+
+// TableResp 表格响应
+type TableResp struct {
+	ExcelInfoId int                                                `description:"表格ID"`
+	TableName   string                                             `description:"表格名称"`
+	ClassifyId  int                                                `description:"分类ID"`
+	ModifyTime  string                                             `description:"最近保存时间"`
+	TableConfig tradeAnalysisModel.TableExtraConfig                `description:"表格配置"`
+	TableCols   []*tradeAnalysisModel.TradeAnalysisTableColumnItem `description:"表头"`
+	TableData   []*tradeAnalysisModel.TableRowData                 `description:"表格数据"`
+	Button      excelModel.ExcelInfoDetailButton                   `description:"表格的按钮权限"`
+}

+ 246 - 359
models/data_manage/trade_analysis/trade_analysis.go

@@ -4,10 +4,68 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
+	"strings"
 	"time"
 )
 
-// 上期能源持仓榜单表
+const (
+	TradeDataTypeNull      = 0 // 无值
+	TradeDataTypeOrigin    = 1 // 原始值
+	TradeDataTypeCalculate = 2 // 推算值
+
+	WarehouseBuyChartType     = 1 // 多单图
+	WarehouseSoldChartType    = 2 // 空单图
+	WarehousePureBuyChartType = 3 // 净多单图
+
+	WarehouseDefaultUnit      = "手"
+	WarehouseDefaultFrequency = "日度"
+
+	GuangZhouTopCompanyAliasName = "日成交持仓排名" // 广期所TOP20对应的公司名称
+	GuangZhouSeatNameBuy         = "持买单量"    // 广期所指标名称中的多单名称
+	GuangZhouSeatNameSold        = "持卖单量"    // 广期所指标名称中的空单名称
+	GuangZhouTopSeatNameBuy      = "持买单量总计"  // 广期所指标名称中的TOP20多单名称
+	GuangZhouTopSeatNameSold     = "持卖单量总计"  // 广期所指标名称中的TOP20空单名称
+	GuangZhouTopSeatNameDeal     = "成交量总计"   // 广期所指标名称中的TOP20成交量名称
+)
+
+const (
+	TradeExchangeDalian    = "dalian"
+	TradeExchangeZhengzhou = "zhengzhou"
+	TradeExchangeGuangzhou = "guangzhou"
+)
+
+var WarehouseTypeSuffixNames = map[int]string{
+	WarehouseBuyChartType:     "席位多单",
+	WarehouseSoldChartType:    "席位空单",
+	WarehousePureBuyChartType: "席位净多单",
+}
+
+// GuangzhouSeatNameValType 广期所数据名称对应的席位方向
+var GuangzhouSeatNameValType = map[string]int{
+	GuangZhouSeatNameBuy:     1,
+	GuangZhouSeatNameSold:    2,
+	GuangZhouTopSeatNameBuy:  1,
+	GuangZhouTopSeatNameSold: 2,
+	GuangZhouTopSeatNameDeal: 3,
+}
+
+// 合约查询方式
+var (
+	ContractQueryTypeTop   = 1 // 主力合约
+	ContractQueryTypeTop2  = 2 // 成交量前2
+	ContractQueryTypeTop3  = 3 // 成交量前3
+	ContractQueryTypeAll   = 4 // 所有合约(多个)
+	ContractQueryTypeTotal = 5 // 合约加总(1个)
+)
+
+// 合约方向
+var (
+	ContractPositionBuy     = 1 // 多单
+	ContractPositionSold    = 2 // 空单
+	ContractPositionPureBuy = 3 // 净多单
+)
+
+// TradePositionTop 持仓榜单
 type TradePositionTop struct {
 	Id            uint64    `gorm:"primaryKey;column:id" json:"id"`
 	ClassifyName  string    `gorm:"column:classify_name" json:"classify_name"`     //分类名称
@@ -173,220 +231,90 @@ type OriginTradeData struct {
 	ValType      int       `description:"数据类型: 1-多单; 2-空单"`
 }
 
-// GetTradeDataByClassifyAndCompany 根据品种和公司名称获取持仓数据
-func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts, companies []string) (items []*OriginTradeData, err error) {
+// BaseFromTradeCommonIndex 郑商所/大商所/上期所/上期能源指标表通用字段
+type BaseFromTradeCommonIndex struct {
+	Rank          int       `description:"排名"`
+	DealShortName string    `description:"成交量公司简称"`
+	DealName      string    `description:"成交量指标名称"`
+	DealCode      string    `description:"成交量指标编码"`
+	DealValue     int       `description:"成交量"`
+	DealChange    int       `description:"成交变化量"`
+	BuyShortName  string    `description:"持买单量公司简称"`
+	BuyName       string    `description:"持买单量指标名称"`
+	BuyCode       string    `description:"持买单量指标编码"`
+	BuyValue      int       `description:"持买单量"`
+	BuyChange     int       `description:"持买单量变化量"`
+	SoldShortName string    `description:"持卖单量公司简称"`
+	SoldName      string    `description:"持卖单量指标名称"`
+	SoldCode      string    `description:"持卖单量指标编码"`
+	SoldValue     int       `description:"持卖单量"`
+	SoldChange    int       `description:"持卖单变化量"`
+	Frequency     string    `description:"频度"`
+	ClassifyName  string    `description:"品种"`
+	ClassifyType  string    `description:"合约"`
+	CreateTime    time.Time `description:"创建时间"`
+	ModifyTime    time.Time `description:"更新时间"`
+	DataTime      time.Time `description:"数据日期"`
+}
+
+// GetTradeDataByContracts 根据合约获取持仓数据
+func GetTradeDataByContracts(exchange string, classifyNames, contracts []string, startDate, endDate time.Time) (items []*BaseFromTradeCommonIndex, err error) {
 	if exchange == "" {
 		err = fmt.Errorf("数据表名称有误")
 		return
 	}
-	if len(contracts) == 0 || len(companies) == 0 {
-		return
+	var cond string
+	var pars []interface{}
+	if len(classifyNames) > 0 {
+		cond += fmt.Sprintf(` AND classify_name IN (%s)`, utils.GetOrmInReplace(len(classifyNames)))
+		pars = append(pars, classifyNames)
 	}
-	condBuy := fmt.Sprintf(`classify_name = ? AND classify_type IN (%s)`, utils.GetOrmInReplace(len(contracts)))
-	parsBuy := make([]interface{}, 0)
-	parsBuy = append(parsBuy, classifyName, contracts)
-
-	condSold := fmt.Sprintf(`classify_name = ? AND classify_type IN (%s)`, utils.GetOrmInReplace(len(contracts)))
-	parsSold := make([]interface{}, 0)
-	parsSold = append(parsSold, classifyName, contracts)
-
-	// 是否含有TOP20
-	var hasTop bool
-	var condCompanies []string
-	for _, v := range companies {
-		if v == TradeFuturesCompanyTop20 {
-			hasTop = true
-			continue
-		}
-		condCompanies = append(condCompanies, v)
+	if len(contracts) > 0 {
+		cond += fmt.Sprintf(` AND classify_type IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+		pars = append(pars, contracts)
 	}
-	if !hasTop {
-		if len(condCompanies) == 0 {
-			err = fmt.Errorf("查询条件-期货公司异常")
-			return
-		}
-		condBuy += fmt.Sprintf(` AND buy_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
-		parsBuy = append(parsBuy, condCompanies)
-		condSold += fmt.Sprintf(` AND sold_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
-		parsSold = append(parsSold, condCompanies)
-	} else {
-		// 这里rank=0或者999是因为大商所的数据并不只有999
-		if len(condCompanies) > 0 {
-			condBuy += fmt.Sprintf(` AND (rank = 999 OR rank = 0 OR buy_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
-			condSold += fmt.Sprintf(` AND (rank = 999 OR rank = 0 OR sold_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
-			parsBuy = append(parsBuy, condCompanies)
-			parsSold = append(parsSold, condCompanies)
-		} else {
-			condBuy += ` AND (rank = 999 OR rank = 0)`
-			condSold += ` AND (rank = 999 OR rank = 0)`
-		}
+	if !startDate.IsZero() && !endDate.IsZero() {
+		cond += ` AND (data_time BETWEEN ? AND ?)`
+		pars = append(pars, startDate.Format(utils.FormatDate), endDate.Format(utils.FormatDate))
 	}
-
+	fields := []string{"rank", "buy_short_name", "buy_value", "buy_change", "sold_short_name", "sold_value", "sold_change", "classify_name", "classify_type", "data_time"}
 	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
-	sql := `SELECT
-			rank,
-			buy_short_name AS company_name,
-			buy_value AS val,
-			buy_change AS val_change,
-			classify_name,
-			classify_type,
-			data_time,
-			1 AS val_type 
-		FROM
-			%s 
-		WHERE
-			%s
-		UNION ALL
-		(
-		SELECT
-			rank,
-			sold_short_name,
-			sold_value,
-			sold_change,
-			classify_name,
-			classify_type,
-			data_time,
-			2 AS val_type 
-		FROM
-			%s 
-		WHERE
-			%s
-		)`
-	sql = fmt.Sprintf(sql, tableName, condBuy, tableName, condSold)
-	o := orm.NewOrmUsingDB("data")
-	_, err = o.Raw(sql, parsBuy, parsSold).QueryRows(&items)
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s ORDER BY data_time DESC`, strings.Join(fields, ","), tableName, cond)
+	_, err = orm.NewOrmUsingDB("data").Raw(sql, pars).QueryRows(&items)
 	return
 }
 
-// GetTradeZhengzhouDataByClassifyAndCompany 郑商所-根据品种和公司名称获取持仓数据
-func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, companies []string) (items []*OriginTradeData, err error) {
-	if exchange == "" {
-		err = fmt.Errorf("数据表名称有误")
-		return
+// GetZhengzhouTradeDataByContracts 郑商所-根据合约获取持仓数据
+func GetZhengzhouTradeDataByContracts(classifyNames []string, startDate, endDate time.Time) (items []*BaseFromTradeCommonIndex, err error) {
+	var cond string
+	var pars []interface{}
+	if len(classifyNames) > 0 {
+		cond += fmt.Sprintf(` AND classify_name IN (%s)`, utils.GetOrmInReplace(len(classifyNames)))
+		pars = append(pars, classifyNames)
 	}
-	if len(contracts) == 0 || len(companies) == 0 {
-		return
+	if !startDate.IsZero() && !endDate.IsZero() {
+		cond += ` AND (data_time BETWEEN ? AND ?)`
+		pars = append(pars, startDate.Format(utils.FormatDate), endDate.Format(utils.FormatDate))
 	}
-	condBuy := fmt.Sprintf(`classify_name IN (%s)`, utils.GetOrmInReplace(len(contracts)))
-	parsBuy := make([]interface{}, 0)
-	parsBuy = append(parsBuy, contracts)
-
-	condSold := fmt.Sprintf(`classify_name IN (%s)`, utils.GetOrmInReplace(len(contracts)))
-	parsSold := make([]interface{}, 0)
-	parsSold = append(parsSold, contracts)
-
-	// 是否含有TOP20
-	var hasTop bool
-	var condCompanies []string
-	for _, v := range companies {
-		if v == TradeFuturesCompanyTop20 {
-			hasTop = true
-			continue
-		}
-		condCompanies = append(condCompanies, v)
-	}
-	if !hasTop {
-		if len(condCompanies) == 0 {
-			err = fmt.Errorf("查询条件-期货公司异常")
-			return
-		}
-		condBuy += fmt.Sprintf(` AND buy_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
-		parsBuy = append(parsBuy, condCompanies)
-		condSold += fmt.Sprintf(` AND sold_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
-		parsSold = append(parsSold, condCompanies)
-	} else {
-		if len(condCompanies) > 0 {
-			condBuy += fmt.Sprintf(` AND (rank = 999 OR buy_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
-			condSold += fmt.Sprintf(` AND (rank = 999 OR sold_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
-			parsBuy = append(parsBuy, condCompanies)
-			parsSold = append(parsSold, condCompanies)
-		} else {
-			condBuy += ` AND rank = 999`
-			condSold += ` AND rank = 999`
-		}
-	}
-
-	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
-	sql := `SELECT
-			rank,
-			buy_short_name AS company_name,
-			buy_value AS val,
-			buy_change AS val_change,
-			classify_name AS classify_type,
-			data_time,
-			1 AS val_type 
-		FROM
-			%s 
-		WHERE
-			%s
-		UNION ALL
-		(
-		SELECT
-			rank,
-			sold_short_name,
-			sold_value,
-			sold_change,
-			classify_name AS classify_type,
-			data_time,
-			2 AS val_type 
-		FROM
-			%s 
-		WHERE
-			%s
-		)`
-	sql = fmt.Sprintf(sql, tableName, condBuy, tableName, condSold)
-	o := orm.NewOrmUsingDB("data")
-	_, err = o.Raw(sql, parsBuy, parsSold).QueryRows(&items)
+	// ps.classify_name实为合约代码
+	fields := []string{"rank", "buy_short_name", "buy_value", "buy_change", "sold_short_name", "sold_value", "sold_change", "classify_name AS classify_type", "data_time"}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_trade_zhengzhou_index WHERE 1=1 %s ORDER BY data_time DESC`, strings.Join(fields, ","), cond)
+	_, err = orm.NewOrmUsingDB("data").Raw(sql, pars).QueryRows(&items)
 	return
 }
 
 // ContractCompanyTradeData [合约-期货公司]持仓数据
 type ContractCompanyTradeData struct {
-	CompanyName  string                          `description:"期货公司名称"`
+	Exchange     string                          `description:"交易所"`
+	ClassifyName string                          `description:"品种"`
 	ClassifyType string                          `description:"合约代码"`
+	CompanyName  string                          `description:"期货公司名称"`
+	IsTotal      bool                            `description:"是否为合约加总"`
 	StartDate    time.Time                       `description:"数据开始日期"`
 	EndDate      time.Time                       `description:"数据结束日期"`
 	DataList     []*ContractCompanyTradeDataList `description:"数据序列"`
 }
 
-const (
-	TradeDataTypeNull      = 0 // 无值
-	TradeDataTypeOrigin    = 1 // 原始值
-	TradeDataTypeCalculate = 2 // 推算值
-
-	WarehouseBuyChartType     = 1 // 多单图
-	WarehouseSoldChartType    = 2 // 空单图
-	WarehousePureBuyChartType = 3 // 净多单图
-
-	WarehouseDefaultUnit      = "手"
-	WarehouseDefaultFrequency = "日度"
-
-	GuangZhouTopCompanyAliasName = "日成交持仓排名" // 广期所TOP20对应的公司名称
-	GuangZhouSeatNameBuy         = "持买单量"    // 广期所指标名称中的多单名称
-	GuangZhouSeatNameSold        = "持卖单量"    // 广期所指标名称中的空单名称
-	GuangZhouTopSeatNameBuy      = "持买单量总计"  // 广期所指标名称中的TOP20多单名称
-	GuangZhouTopSeatNameSold     = "持卖单量总计"  // 广期所指标名称中的TOP20空单名称
-)
-
-const (
-	TradeExchangeZhengzhou = "zhengzhou"
-	TradeExchangeGuangzhou = "guangzhou"
-)
-
-var WarehouseTypeSuffixNames = map[int]string{
-	WarehouseBuyChartType:     "席位多单",
-	WarehouseSoldChartType:    "席位空单",
-	WarehousePureBuyChartType: "席位净多单",
-}
-
-// GuangzhouSeatNameValType 广期所数据名称对应的席位方向
-var GuangzhouSeatNameValType = map[string]int{
-	GuangZhouSeatNameBuy:     1,
-	GuangZhouSeatNameSold:    2,
-	GuangZhouTopSeatNameBuy:  1,
-	GuangZhouTopSeatNameSold: 2,
-}
-
 // ContractCompanyTradeDataList [合约-期货公司]持仓数据详情
 type ContractCompanyTradeDataList struct {
 	Date              time.Time `description:"数据日期"`
@@ -404,139 +332,6 @@ type ContractCompanyTradeDataList struct {
 	PureBuyChangeType int       `description:"净多单持仓增减类型: 0-无值; 1-原始值; 2-推算值"`
 }
 
-// GetLastTradeDataByClassify 获取[合约]末位多空单数据
-func GetLastTradeDataByClassify(exchange, classifyName string, contracts []string) (items []*OriginTradeData, err error) {
-	if exchange == "" {
-		err = fmt.Errorf("数据表名称有误")
-		return
-	}
-	if len(contracts) == 0 {
-		return
-	}
-	contractReplacer := utils.GetOrmInReplace(len(contracts))
-
-	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
-	sql := `SELECT 
-			tpt.rank,
-			tpt.buy_short_name AS company_name,
-			tpt.buy_value AS val,
-			tpt.buy_change AS val_change,
-			tpt.classify_name,
-			tpt.classify_type,
-			tpt.data_time,
-			1 AS val_type
-		FROM 
-			%s tpt
-		JOIN 
-			(
-				SELECT
-					data_time, classify_type, MAX(rank) AS max_rank
-				FROM 
-					%s
-				WHERE 
-					classify_name = ? AND classify_type IN (%s) AND buy_short_name <> ''
-				GROUP BY 
-					data_time,
-					classify_type
-			) sub
-		ON
-			tpt.data_time = sub.data_time AND tpt.classify_type = sub.classify_type AND tpt.rank = sub.max_rank
-		WHERE 
-			tpt.classify_name = ? AND tpt.classify_type IN (%s)
-		UNION ALL
-		(
-		SELECT 
-			tpt.rank, tpt.sold_short_name, tpt.sold_value, tpt.sold_change, tpt.classify_name, tpt.classify_type, tpt.data_time, 2 AS val_type
-		FROM 
-			%s tpt
-		JOIN 
-			(
-				SELECT 
-					data_time, classify_type, MAX(rank) AS max_rank
-				FROM 
-					%s
-				WHERE 
-					classify_name = ? AND classify_type IN (%s) AND sold_short_name <> ''
-				GROUP BY 
-					data_time, classify_type
-			) sub
-		ON 
-			tpt.data_time = sub.data_time AND tpt.classify_type = sub.classify_type AND tpt.rank = sub.max_rank
-		WHERE 
-			tpt.classify_name = ? AND tpt.classify_type IN (%s)
-		)`
-	sql = fmt.Sprintf(sql, tableName, tableName, contractReplacer, contractReplacer, tableName, tableName, contractReplacer, contractReplacer)
-	o := orm.NewOrmUsingDB("data")
-	_, err = o.Raw(sql, classifyName, contracts, classifyName, contracts, classifyName, contracts, classifyName, contracts).QueryRows(&items)
-	return
-}
-
-// GetLastTradeZhengzhouDataByClassify 郑商所-获取[合约]末位多空单数据
-func GetLastTradeZhengzhouDataByClassify(exchange string, contracts []string) (items []*OriginTradeData, err error) {
-	if exchange == "" {
-		err = fmt.Errorf("数据表名称有误")
-		return
-	}
-	if len(contracts) == 0 {
-		return
-	}
-	contractReplacer := utils.GetOrmInReplace(len(contracts))
-
-	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
-	sql := `SELECT 
-			tpt.rank,
-			tpt.buy_short_name AS company_name,
-			tpt.buy_value AS val,
-			tpt.buy_change AS val_change,
-  			tpt.classify_name AS classify_type,
-			tpt.data_time,
-			1 AS val_type
-		FROM 
-			%s tpt
-		JOIN 
-			(
-				SELECT
-					data_time, classify_name, MAX(rank) AS max_rank
-				FROM 
-					%s
-				WHERE 
-					classify_name IN (%s) AND buy_short_name <> ''
-				GROUP BY 
-					data_time,
-					classify_name
-			) sub
-		ON
-			tpt.data_time = sub.data_time AND tpt.classify_name = sub.classify_name AND tpt.rank = sub.max_rank
-		WHERE 
-			tpt.classify_name IN (%s)
-		UNION ALL
-		(
-		SELECT 
-			tpt.rank, tpt.sold_short_name, tpt.sold_value, tpt.sold_change, tpt.classify_name AS classify_type, tpt.data_time, 2 AS val_type
-		FROM 
-			%s tpt
-		JOIN 
-			(
-				SELECT 
-					data_time, classify_name, MAX(rank) AS max_rank
-				FROM 
-					%s
-				WHERE 
-					classify_name IN (%s) AND sold_short_name <> ''
-				GROUP BY 
-					data_time, classify_name
-			) sub
-		ON 
-			tpt.data_time = sub.data_time AND tpt.classify_name = sub.classify_name AND tpt.rank = sub.max_rank
-		WHERE 
-			tpt.classify_name IN (%s)
-		)`
-	sql = fmt.Sprintf(sql, tableName, tableName, contractReplacer, contractReplacer, tableName, tableName, contractReplacer, contractReplacer)
-	o := orm.NewOrmUsingDB("data")
-	_, err = o.Raw(sql, contracts, contracts, contracts, contracts).QueryRows(&items)
-	return
-}
-
 type BaseFromTradeGuangzhouIndex struct {
 	BaseFromTradeGuangzhouIndexId    int       `orm:"column(base_from_trade_guangzhou_index_id);pk"`
 	BaseFromTradeGuangzhouClassifyId int       `description:"分类id"`
@@ -550,10 +345,27 @@ type BaseFromTradeGuangzhouIndex struct {
 	ModifyTime                       time.Time `description:"修改日期"`
 }
 
-func GetBaseFromTradeGuangzhouIndexByClassifyId(classifyId int) (list []*BaseFromTradeGuangzhouIndex, err error) {
+func GetBaseFromTradeGuangzhouIndex(classifyIds []int, contracts []string, indexKeyword string) (list []*BaseFromTradeGuangzhouIndex, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := `SELECT * FROM base_from_trade_guangzhou_index WHERE base_from_trade_guangzhou_classify_id = ?`
-	_, err = o.Raw(sql, classifyId).QueryRows(&list)
+	cond := ``
+	pars := make([]interface{}, 0)
+	if len(classifyIds) > 0 {
+		cond += fmt.Sprintf(` AND b.base_from_trade_guangzhou_classify_id IN (%s)`, utils.GetOrmInReplace(len(classifyIds)))
+		pars = append(pars, classifyIds)
+	}
+	if len(contracts) > 0 {
+		cond += fmt.Sprintf(` AND b.contract IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+		pars = append(pars, contracts)
+	}
+	if indexKeyword != "" {
+		cond += fmt.Sprintf(` AND a.index_name LIKE ?`)
+		pars = append(pars, indexKeyword)
+	}
+	sql := `SELECT a.* FROM base_from_trade_guangzhou_index AS a
+		JOIN base_from_trade_guangzhou_contract AS b ON a.base_from_trade_guangzhou_contract_id = b.base_from_trade_guangzhou_contract_id
+		WHERE 1=1 %s`
+	sql = fmt.Sprintf(sql, cond)
+	_, err = o.Raw(sql, pars).QueryRows(&list)
 	return
 }
 
@@ -568,45 +380,120 @@ type BaseFromTradeGuangzhouData struct {
 	ModifyTime                    time.Time `description:"修改日期"`
 }
 
-// GetBaseFromTradeGuangzhouDataByIndexIds 获取指标数据
-func GetBaseFromTradeGuangzhouDataByIndexIds(indexIds []int) (list []*BaseFromTradeGuangzhouData, err error) {
+// GetBaseFromTradeGuangzhouDataByIndexIds 广期所-获取指标数据
+func GetBaseFromTradeGuangzhouDataByIndexIds(indexIds []int, startDate, endDate time.Time) (list []*BaseFromTradeGuangzhouData, err error) {
 	if len(indexIds) == 0 {
 		return
 	}
-	o := orm.NewOrmUsingDB("data")
-	sql := fmt.Sprintf(`SELECT * FROM base_from_trade_guangzhou_data WHERE base_from_trade_guangzhou_index_id IN (%s) ORDER BY base_from_trade_guangzhou_index_id`, utils.GetOrmInReplace(len(indexIds)))
-	_, err = o.Raw(sql, indexIds).QueryRows(&list)
+	cond := fmt.Sprintf(` AND base_from_trade_guangzhou_index_id IN (%s)`, utils.GetOrmInReplace(len(indexIds)))
+	pars := make([]interface{}, 0)
+	pars = append(pars, indexIds)
+	if !startDate.IsZero() && !endDate.IsZero() {
+		if startDate.Equal(endDate) {
+			cond += ` AND data_time = ?`
+			pars = append(pars, startDate.Format(utils.FormatDate))
+		}
+		if !startDate.Equal(endDate) {
+			cond += ` AND (data_time BETWEEN ? AND ?)`
+			pars = append(pars, startDate.Format(utils.FormatDate), endDate.Format(utils.FormatDate))
+		}
+	}
+	sql := fmt.Sprintf(`SELECT * FROM base_from_trade_guangzhou_data WHERE 1=1 %s ORDER BY base_from_trade_guangzhou_index_id`, cond)
+	_, err = orm.NewOrmUsingDB("data").Raw(sql, pars).QueryRows(&list)
 	return
 }
 
-// GetBaseFromTradeGuangzhouMinDataByIndexIds 获取指标中的末位数据
-func GetBaseFromTradeGuangzhouMinDataByIndexIds(indexIds []int) (list []*BaseFromTradeGuangzhouData, err error) {
-	indexLen := len(indexIds)
-	if indexLen == 0 {
+// ContractTopRankData TOP20合约排名数据
+type ContractTopRankData struct {
+	Exchange      string    `description:"交易所"`
+	DealValue     int       `description:"成交量"`
+	BuyValue      int       `description:"多单持仓量"`
+	BuyChange     int       `description:"多单变化"`
+	SoldValue     int       `description:"空单持仓量"`
+	SoldChange    int       `description:"空单变化"`
+	PureBuyValue  int       `description:"净多单持仓量"`
+	PureBuyChange int       `description:"净多单变化"`
+	ClassifyName  string    `description:"品种名称"`
+	ClassifyType  string    `description:"合约代码"`
+	DataTime      time.Time `description:"数据日期"`
+}
+
+// GetContractTopRankData 获取合约TOP20根据当日成交量排名
+func GetContractTopRankData(exchange string, classifyNames []string, dataDate time.Time) (items []*ContractTopRankData, err error) {
+	if exchange == "" {
+		err = fmt.Errorf("数据表名称有误")
 		return
 	}
-	o := orm.NewOrmUsingDB("data")
-	sql := fmt.Sprintf(`SELECT 
-			t1.data_time,
-			t1.min_value AS value
-		FROM 
-			(
-				SELECT 
-					data_time,
-					MIN(value) AS min_value
-				FROM 
-					base_from_trade_guangzhou_data
-				WHERE 
-					base_from_trade_guangzhou_index_id IN (%s)
-				GROUP BY 
-					data_time
-			) t1
-		JOIN 
-			base_from_trade_guangzhou_data t2
-		ON 
-			t1.data_time = t2.data_time AND t1.min_value = t2.value AND t2.base_from_trade_guangzhou_index_id IN (%s)
-		GROUP BY 
-			t1.data_time`, utils.GetOrmInReplace(indexLen), utils.GetOrmInReplace(indexLen))
-	_, err = o.Raw(sql, indexIds, indexIds).QueryRows(&list)
+	if len(classifyNames) == 0 {
+		return
+	}
+	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
+	// 大商所存在TOP20的rank=0
+	queryRank := ` rank = 999`
+	if exchange == TradeExchangeDalian {
+		queryRank = ` (rank = 999 OR rank = 0)`
+	}
+	sql := `SELECT * FROM %s WHERE data_time = ? AND classify_name IN (%s) AND %s GROUP BY classify_type ORDER BY deal_value DESC`
+	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(classifyNames)), queryRank)
+	_, err = orm.NewOrmUsingDB("data").Raw(sql, dataDate.Format(utils.FormatDate), classifyNames).QueryRows(&items)
+	return
+}
+
+// GetZhengzhouContractTopRankData 郑商所-获取合约根据当日成交量排名
+func GetZhengzhouContractTopRankData(classifyNames []string, dataDate time.Time) (items []*ContractTopRankData, err error) {
+	if len(classifyNames) == 0 {
+		return
+	}
+	sql := `SELECT * FROM base_from_trade_zhengzhou_index WHERE data_time = ? AND classify_name IN (%s) AND rank = 999 GROUP BY classify_name ORDER BY deal_value DESC`
+	sql = fmt.Sprintf(sql, utils.GetOrmInReplace(len(classifyNames)))
+	_, err = orm.NewOrmUsingDB("data").Raw(sql, dataDate.Format(utils.FormatDate), classifyNames).QueryRows(&items)
+	return
+}
+
+// ContractCompanyTradeEdb [合约-期货公司]指标
+type ContractCompanyTradeEdb struct {
+	Exchange         string                         `description:"交易所"`
+	ClassifyName     string                         `description:"品种"`
+	ClassifyType     string                         `description:"合约代码"`
+	CompanyName      string                         `description:"期货公司名称"`
+	IsTotal          bool                           `description:"是否为合约加总"`
+	ContractPosition int                            `description:"合约方向"`
+	StartDate        time.Time                      `description:"数据开始日期"`
+	EndDate          time.Time                      `description:"数据结束日期"`
+	DataList         []*ContractCompanyTradeEdbData `description:"数据序列"`
+}
+
+// ContractCompanyTradeEdbData [合约-期货公司]指标数据
+type ContractCompanyTradeEdbData struct {
+	DataTime time.Time `description:"数据日期"`
+	Val      int       `description:"数据值"`
+}
+
+// GetClassifyNewestDataTime 获取品种最新数据日期
+func GetClassifyNewestDataTime(exchange string, classifyNames []string) (dateTime time.Time, err error) {
+	if exchange == "" {
+		err = fmt.Errorf("数据表名称有误")
+		return
+	}
+	if len(classifyNames) == 0 {
+		return
+	}
+	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
+	sql := `SELECT data_time FROM %s WHERE classify_name IN (%s) ORDER BY data_time DESC LIMIT 1`
+	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(classifyNames)))
+	err = orm.NewOrmUsingDB("data").Raw(sql, classifyNames).QueryRow(&dateTime)
+	return
+}
+
+// GetGuangzhouClassifyNewestDataTime 广期所-获取品种最新数据日期
+func GetGuangzhouClassifyNewestDataTime(indexIds []int) (dateTime time.Time, err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	cond := fmt.Sprintf(` AND base_from_trade_guangzhou_index_id IN (%s)`, utils.GetOrmInReplace(len(indexIds)))
+	pars := make([]interface{}, 0)
+	pars = append(pars, indexIds)
+	sql := fmt.Sprintf(`SELECT data_time FROM base_from_trade_guangzhou_data WHERE 1=1 %s ORDER BY data_time DESC LIMIT 1`, cond)
+	err = orm.NewOrmUsingDB("data").Raw(sql, pars).QueryRow(&dateTime)
 	return
 }

+ 43 - 0
models/data_manage/trade_analysis/trade_analysis_correlation.go

@@ -0,0 +1,43 @@
+package trade_analysis
+
+// CorrelationTableExtraConfig 相关性表格配置
+type CorrelationTableExtraConfig struct {
+	BaseEdbInfoId    int                          `description:"标的指标ID"`
+	BaseEdbName      string                       `description:"标的指标名称(仅回显时用)"`
+	Exchange         string                       `description:"交易所标识"`
+	ClassifyName     string                       `description:"(单选)品种"`
+	CompanyNames     []string                     `description:"(多选)期货公司"`
+	ContractType     int                          `description:"合约类型: 1-主力合约; 2-成交量前2; 3-成交量前3; 4-所有合约(多个); 5-合约加总(1个)"`
+	ContractPosition int                          `description:"(单选)合约方向: 1-多; 2-空; 3-净多"`
+	PredictRatio     float64                      `description:"预估参数, 0-1之间"`
+	RollConfig       []CorrelationTableRollConfig `description:"滚动相关性配置"`
+}
+
+// CorrelationTableRollConfig 滚动相关性配置
+type CorrelationTableRollConfig struct {
+	CalculateValue int `description:"计算窗口"`
+	LeadValue      int `description:"领先期数"`
+}
+
+// CorrelationTableRowData 相关性表格行数据
+type CorrelationTableRowData struct {
+	Exchange     string                        `description:"交易所"`
+	ClassifyName string                        `description:"品种"`
+	ClassifyType string                        `description:"合约"`
+	CompanyName  string                        `description:"公司名称"`
+	RowName      string                        `description:"合约持仓名称"`
+	DayData      []*CorrelationTableRowDayData `description:"天数对应的相关性系数"`
+}
+
+// CorrelationTableRowDayData 相关性表格行数据值
+type CorrelationTableRowDayData struct {
+	Day      int     `description:"天数: 从0到-10, 0为最新数据, -1表示前一天的滚动相关性"`
+	DataDate string  `description:"对应的日期"`
+	DataVal  float64 `description:"相关性系数"`
+}
+
+// CorrelationTableData 相关性表格数据
+type CorrelationTableData struct {
+	CorrelationTableRollConfig `description:"滚动相关性配置, 每个配置单独为一张表"`
+	RowsData                   []*CorrelationTableRowData `description:"表格行数据"`
+}

+ 41 - 0
models/data_manage/trade_analysis/trade_analysis_table.go

@@ -0,0 +1,41 @@
+package trade_analysis
+
+// TableExtraConfig 表格配置
+type TableExtraConfig struct {
+	CompanyName  string                     `description:"期货公司"`
+	ClassifyList []TableExtraConfigClassify `description:"交易所品种信息"`
+	ContractType int                        `description:"合约类型: 1-主力合约; 2-成交量前2; 3-成交量前3; 4-所有合约(多个); 5-合约加总(1个)"`
+	DateType     int                        `description:"0-最新日期(默认); 1-固定日期"`
+	IntervalMove int                        `description:"前移期数"`
+	FixedDate    string                     `description:"固定日期"`
+	PredictRatio float64                    `description:"预估参数, 0-1之间"`
+}
+
+// TableExtraConfigClassify 表格配置品种
+type TableExtraConfigClassify struct {
+	Exchange      string   `description:"交易所"`
+	ClassifyNames []string `description:"品种"`
+}
+
+// TableRowData 表格行数据
+type TableRowData struct {
+	Exchange         string  `description:"交易所"`
+	ClassifyName     string  `description:"品种"`
+	ClassifyType     string  `description:"合约"`
+	BuyValue         int     `description:"多单持仓量"`
+	BuyChange        int     `description:"多单变化"`
+	SoldValue        int     `description:"空单持仓量"`
+	SoldChange       int     `description:"空单变化"`
+	PureBuyVal       int     `description:"净多单持仓量"`
+	PureBuyChange    int     `description:"净多单持仓增减"`
+	BuySoldRatio     float64 `description:"多空比"`
+	BuyTopRatio      float64 `description:"多单占前20比例"`
+	SoldTopRatio     float64 `description:"空单占前20比例"`
+	TopBuyValue      int     `description:"前20多单"`
+	TopSoldValue     int     `description:"前20空单"`
+	TopBuyChange     int     `description:"前20多单变动"`
+	TopSoldChange    int     `description:"前20空单变动"`
+	TopPureBuy       int     `description:"前20净多单"`
+	TopPureBuyChange int     `description:"前20净多单变动"`
+	TopBuySoldRatio  float64 `description:"前20多空比"`
+}

+ 188 - 0
models/data_manage/trade_analysis/trade_analysis_table_column.go

@@ -0,0 +1,188 @@
+package trade_analysis
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// TradeAnalysisTableColumn 持仓分析-多空分析自定义列
+type TradeAnalysisTableColumn struct {
+	Id           int       `orm:"column(id);pk"`
+	ExcelInfoId  int       `description:"表格ID,0为模板"`
+	ColumnKey    string    `description:"字段标识"`
+	ColumnName   string    `description:"字段名称"`
+	ColumnNameEn string    `description:"英文字段名称"`
+	Sort         int       `description:"排序"`
+	IsMust       int       `description:"是否必选列:0-否;1-是"`
+	IsSort       int       `description:"是否允许排序:0-否;1-是"`
+	IsShow       int       `description:"是否展示:0-隐藏;1-显示"`
+	CreateTime   time.Time `description:"创建时间"`
+	ModifyTime   time.Time `description:"修改时间"`
+}
+
+func (m *TradeAnalysisTableColumn) TableName() string {
+	return "trade_analysis_table_column"
+}
+
+type TradeAnalysisTableColumnCols struct {
+	PrimaryId    string
+	ExcelInfoId  string
+	ColumnKey    string
+	ColumnName   string
+	ColumnNameEn string
+	Sort         string
+	IsMust       string
+	IsSort       string
+	IsShow       string
+	CreateTime   string
+	ModifyTime   string
+}
+
+func (m *TradeAnalysisTableColumn) Cols() TradeAnalysisTableColumnCols {
+	return TradeAnalysisTableColumnCols{
+		PrimaryId:    "id",
+		ExcelInfoId:  "excel_info_id",
+		ColumnKey:    "column_key",
+		ColumnName:   "column_name",
+		ColumnNameEn: "column_name_en",
+		Sort:         "sort",
+		IsMust:       "is_must",
+		IsSort:       "is_sort",
+		IsShow:       "is_show",
+		CreateTime:   "create_time",
+		ModifyTime:   "modify_time",
+	}
+}
+
+func (m *TradeAnalysisTableColumn) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *TradeAnalysisTableColumn) CreateMulti(items []*TradeAnalysisTableColumn) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *TradeAnalysisTableColumn) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *TradeAnalysisTableColumn) 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 *TradeAnalysisTableColumn) 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 *TradeAnalysisTableColumn) 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 *TradeAnalysisTableColumn) GetItemById(id int) (item *TradeAnalysisTableColumn, 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 *TradeAnalysisTableColumn) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *TradeAnalysisTableColumn, 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 *TradeAnalysisTableColumn) 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 *TradeAnalysisTableColumn) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*TradeAnalysisTableColumn, 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 *TradeAnalysisTableColumn) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*TradeAnalysisTableColumn, 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
+}
+
+// TradeAnalysisTableColumnItem 多因子系列信息
+type TradeAnalysisTableColumnItem struct {
+	ColumnKey    string `description:"字段标识"`
+	ColumnName   string `description:"字段名称"`
+	ColumnNameEn string `description:"英文字段名称"`
+	Sort         int    `description:"排序"`
+	IsMust       int    `description:"是否必选列:0-否;1-是"`
+	IsSort       int    `description:"是否允许排序:0-否;1-是"`
+	IsShow       int    `description:"是否展示:0-隐藏;1-显示"`
+}
+
+func (m *TradeAnalysisTableColumn) Format2Item() (item *TradeAnalysisTableColumnItem) {
+	item = new(TradeAnalysisTableColumnItem)
+	item.ColumnKey = m.ColumnKey
+	item.ColumnName = m.ColumnName
+	item.ColumnNameEn = m.ColumnNameEn
+	item.Sort = m.Sort
+	item.IsMust = m.IsMust
+	item.IsSort = m.IsSort
+	item.IsShow = m.IsShow
+	return
+}

+ 13 - 13
models/data_manage/trade_analysis/warehouse_process_classify.go

@@ -1,7 +1,7 @@
 package trade_analysis
 
 import (
-	"eta/eta_api/models"
+	"eta/eta_api/models/common"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
@@ -201,8 +201,8 @@ func (m *WareHouseProcessClassify) Format2Item() (item *WareHouseProcessClassify
 // ------------------------------------------------ 通用分类 ------------------------------------------------
 
 // GetCommonClassifyCols 通用分类字段映射
-func (m *WareHouseProcessClassify) GetCommonClassifyCols() models.CommonClassifyCols {
-	return models.CommonClassifyCols{
+func (m *WareHouseProcessClassify) GetCommonClassifyCols() common.CommonClassifyCols {
+	return common.CommonClassifyCols{
 		ClassifyId:   m.Cols().PrimaryId,
 		ClassifyName: m.Cols().ClassifyName,
 		ParentId:     m.Cols().ParentId,
@@ -216,13 +216,13 @@ func (m *WareHouseProcessClassify) GetCommonClassifyCols() models.CommonClassify
 }
 
 // GetCommonClassifyById 获取通用分类
-func (m *WareHouseProcessClassify) GetCommonClassifyById(classifyId int) (commonClassify *models.CommonClassify, err error) {
+func (m *WareHouseProcessClassify) GetCommonClassifyById(classifyId int) (commonClassify *common.CommonClassify, err error) {
 	item, e := m.GetItemById(classifyId)
 	if e != nil {
 		err = e
 		return
 	}
-	commonClassify = new(models.CommonClassify)
+	commonClassify = new(common.CommonClassify)
 	commonClassify.ClassifyId = item.WareHouseProcessClassifyId
 	commonClassify.ClassifyName = item.ClassifyName
 	commonClassify.ParentId = item.ParentId
@@ -236,13 +236,13 @@ func (m *WareHouseProcessClassify) GetCommonClassifyById(classifyId int) (common
 }
 
 // GetClassifyByParentIdAndName 实现获取分类信息的方法
-func (m *WareHouseProcessClassify) GetClassifyByParentIdAndName(parentId int, name string, excludeId int) (*models.CommonClassify, error) {
+func (m *WareHouseProcessClassify) GetClassifyByParentIdAndName(parentId int, name string, excludeId int) (*common.CommonClassify, error) {
 	// 实现获取分类信息的逻辑
 	return nil, nil
 }
 
 // UpdateCommonClassify 实现更新分类信息的方法
-func (m *WareHouseProcessClassify) UpdateCommonClassify(classify *models.CommonClassify, updateCols []string) (err error) {
+func (m *WareHouseProcessClassify) UpdateCommonClassify(classify *common.CommonClassify, updateCols []string) (err error) {
 	return
 }
 
@@ -271,7 +271,7 @@ func (m *WareHouseProcessClassify) GetClassifySortMaxByParentId(parentId int) (s
 	return
 }
 
-func (m *WareHouseProcessClassify) GetFirstClassifyByParentId(parentId int) (item *models.CommonClassify, err error) {
+func (m *WareHouseProcessClassify) GetFirstClassifyByParentId(parentId int) (item *common.CommonClassify, err error) {
 	//o := orm.NewOrmUsingDB("data")
 	//sql := ` SELECT * FROM edb_classify WHERE parent_id=? order by sort asc,classify_id asc limit 1`
 	//err = o.Raw(sql, parentId).QueryRow(&item)
@@ -290,16 +290,16 @@ func (m *WareHouseProcessClassify) SetClassifySortByParentId(parentId, classifyI
 }
 
 // GetCommonClassifyObjCols 通用分类对象字段映射
-func (m *WareHouseProcessClassify) GetCommonClassifyObjCols() models.CommonClassifyObjCols {
+func (m *WareHouseProcessClassify) GetCommonClassifyObjCols() common.CommonClassifyObjCols {
 	// TODO: 完善
-	return models.CommonClassifyObjCols{
+	return common.CommonClassifyObjCols{
 		ObjectId:   m.Cols().ClassifyName,
 		ClassifyId: m.Cols().PrimaryId,
 		Sort:       m.Cols().ParentId,
 	}
 }
 
-func (m *WareHouseProcessClassify) GetObjectById(objectId int) (*models.CommonClassifyObj, error) {
+func (m *WareHouseProcessClassify) GetObjectById(objectId int) (*common.CommonClassifyObj, error) {
 	// 实现获取分类信息的逻辑
 	return nil, nil
 }
@@ -313,7 +313,7 @@ func (m *WareHouseProcessClassify) GetObjectSortMaxByClassifyId(classifyId int)
 	return
 }
 
-func (m *WareHouseProcessClassify) GetFirstObjectByClassifyId(classifyId int) (item *models.CommonClassifyObj, err error) {
+func (m *WareHouseProcessClassify) GetFirstObjectByClassifyId(classifyId int) (item *common.CommonClassifyObj, err error) {
 	//o := orm.NewOrmUsingDB("data")
 	//sql := ` SELECT * FROM edb_info WHERE classify_id=? order by sort asc,edb_info_id asc limit 1`
 	//err = o.Raw(sql, classifyId).QueryRow(&item)
@@ -333,7 +333,7 @@ func (m *WareHouseProcessClassify) SetObjectSortByClassifyId(classifyId, sort, p
 }
 
 // UpdateCommonClassifyObj 更新通用分类对象
-func (m *WareHouseProcessClassify) UpdateCommonClassifyObj(object *models.CommonClassifyObj, updateCols []string) (err error) {
+func (m *WareHouseProcessClassify) UpdateCommonClassifyObj(object *common.CommonClassifyObj, updateCols []string) (err error) {
 	return
 }
 

+ 18 - 16
models/data_source/base_from_sci99.go

@@ -17,6 +17,9 @@ type BaseFromSci99Index struct {
 	Describe           string    // 指标描述
 	CreateTime         time.Time // 创建时间
 	ModifyTime         time.Time // 修改时间
+	StartDate          time.Time `description:"开始日期"`
+	EndDate            time.Time `description:"结束日期"`
+	LatestValue        float64   `description:"最新值"`
 }
 
 // BaseFromSci99Data 代表卓创资讯-原始指标数据表的结构
@@ -40,23 +43,23 @@ type BaseFromSci99Classify struct {
 }
 
 type BaseFromSci99DataItem struct {
-	BaseFromSciDataId  int       `orm:"column(base_from_sci_data_id);pk"` // 主键,自动递增
-	BaseFromSciIndexId int       // 指标id
-	IndexCode          string    // 指标编码
-	DataTime           string    // 数据日期
-	Value              float64   // 数据值
-	CreateTime         string // 创建时间
-	ModifyTime         string // 修改时间
+	BaseFromSciDataId  int     `orm:"column(base_from_sci_data_id);pk"` // 主键,自动递增
+	BaseFromSciIndexId int     // 指标id
+	IndexCode          string  // 指标编码
+	DataTime           string  // 数据日期
+	Value              float64 // 数据值
+	CreateTime         string  // 创建时间
+	ModifyTime         string  // 修改时间
 }
 
 type BaseFromSci99IndexList struct {
-	BaseFromSciIndexId int       `orm:"column(base_from_sci_index_id);pk"` // 主键,自动递增
-	IndexCode          string    // 指标编码
-	IndexName          string    // 指标名称
-	ClassifyId         int       // 分类Id
-	Unit               string    // 单位
-	Frequency          string    // 频度
-	Describe           string    // 指标描述
+	BaseFromSciIndexId int    `orm:"column(base_from_sci_index_id);pk"` // 主键,自动递增
+	IndexCode          string // 指标编码
+	IndexName          string // 指标名称
+	ClassifyId         int    // 分类Id
+	Unit               string // 单位
+	Frequency          string // 频度
+	Describe           string // 指标描述
 	CreateTime         string // 创建时间
 	ModifyTime         string // 修改时间
 	DataList           []*BaseFromSci99DataItem
@@ -100,7 +103,6 @@ func GetSci99Index(condition string, pars interface{}) (items []*BaseFromSci99In
 	return
 }
 
-
 func GetSci99IndexDataCount(indexCode string) (count int, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT COUNT(1) AS count  FROM base_from_sci99_data WHERE index_code=? `
@@ -139,4 +141,4 @@ func GetSci99IndexLatestDate(indexCode string) (ModifyTime string, err error) {
 	sql := ` SELECT modify_time FROM base_from_sci99_data WHERE index_code=? ORDER BY modify_time DESC limit 1 `
 	err = o.Raw(sql, indexCode).QueryRow(&ModifyTime)
 	return
-}
+}

+ 2119 - 0
models/data_source/data_source.go

@@ -0,0 +1,2119 @@
+package data_source
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// SearchDataSource 数据源ES搜索
+type SearchDataSource struct {
+	PrimaryId   int    `description:"主键ID"`
+	IndexCode   string `description:"指标编码"`
+	IndexName   string `description:"指标名称"`
+	ClassifyId  int    `description:"分类ID"`
+	Unit        string `description:"单位"`
+	Frequency   string `description:"频度"`
+	StartDate   string `description:"开始日期"`
+	EndDate     string `description:"结束日期"`
+	LatestValue string `description:"最新值(部分数据源如中国煤炭网存在97%这种数据...)"`
+	Source      int    `description:"来源"`
+	SourceName  string `description:"数据源名称"`
+	SubSource   int    `description:"子来源"`
+	IsDeleted   int    `description:"是否已删除:0-正常;1-已删除"`
+	CreateTime  string `description:"创建时间"`
+	ModifyTime  string `description:"修改时间"`
+}
+
+// SearchDataSourceItem 数据源ES搜索
+type SearchDataSourceItem struct {
+	SearchDataSource
+	SearchText string `description:"搜索结果(含高亮)"`
+}
+
+// ToMap 为了方便前端那边的修改,这里兼容一下部分Key的变动,不然改动量很大
+func (s *SearchDataSourceItem) ToMap(primaryIdKey, indexNameKey, classifyIdKey string) map[string]interface{} {
+	data := make(map[string]interface{})
+	if primaryIdKey != "" {
+		data[primaryIdKey] = s.PrimaryId
+	} else {
+		data["PrimaryId"] = s.PrimaryId
+	}
+	if indexNameKey != "" {
+		data[indexNameKey] = s.IndexName
+	} else {
+		data["IndexName"] = s.IndexName
+	}
+	if classifyIdKey != "" {
+		data[classifyIdKey] = s.ClassifyId
+	} else {
+		data["ClassifyId"] = s.ClassifyId
+	}
+	data["IndexCode"] = s.IndexCode
+	data["Unit"] = s.Unit
+	data["Frequency"] = s.Frequency
+	data["StartDate"] = s.StartDate
+	data["EndDate"] = s.EndDate
+	data["LatestValue"] = s.LatestValue
+	data["Source"] = s.Source
+	data["SourceName"] = s.SourceName
+	data["IsDeleted"] = s.IsDeleted
+	data["CreateTime"] = s.CreateTime
+	data["ModifyTime"] = s.ModifyTime
+	data["SearchText"] = s.SearchText
+	return data
+}
+
+// SearchDataSourceResp 数据源ES-分页搜索响应
+type SearchDataSourceResp struct {
+	Paging *paging.PagingItem
+	List   []map[string]interface{}
+}
+
+// SearchEsCols 数据源ES搜索字段
+type SearchEsCols struct {
+	PrimaryId   string
+	IndexCode   string
+	IndexName   string
+	ClassifyId  string
+	Unit        string
+	Frequency   string
+	StartDate   string
+	EndDate     string
+	LatestValue string
+	CreateTime  string
+	ModifyTime  string
+}
+
+// EsBaseFromIndex 数据源ES统一实现的接口
+type EsBaseFromIndex interface {
+	EsCols() SearchEsCols
+	SourceInfo() (int, int, string)
+}
+
+// GetEsBaseFromIndexByTableName 根据表名获取对应数据源
+func GetEsBaseFromIndexByTableName(tableName string) EsBaseFromIndex {
+	switch tableName {
+	case "base_from_rzd_index":
+		return &BaseFromRzdIndex{}
+	case "base_from_hisugar_index":
+		return &BaseFromHisugarIndex{}
+	case "base_from_ly_index":
+		return &BaseFromLyIndex{}
+	case "base_from_sci_hq_index":
+		return &BaseFromSciHqIndex{}
+	case "base_from_oilchem_index":
+		return &BaseFromOilchemIndex{}
+	case "base_from_ths_hf_index":
+		return &BaseFromThsHfIndex{}
+	case "base_from_ccf_index":
+		return &BaseFromCcfIndex{}
+	case "base_from_usda_fas_index":
+		return &BaseFromUsdaFasIndex{}
+	case "base_from_mysteel_chemical_index":
+		return &BaseFromMysteelChemicalIndex{}
+	case "base_from_smm_index":
+		return &BaseFromSmmIndex{}
+	case "base_from_baiinfo_index":
+		return &BaseFromBaiinfoIndex{}
+	case "base_from_sci_index":
+		return &BaseFromSciIndex{}
+	case "base_from_coalmine_mapping":
+		return &BaseFromCoalmineMapping{}
+	case "base_from_eia_steo_index":
+		return &BaseFromEiaSteoIndex{}
+	case "base_from_icpi_index":
+		return &BaseFromIcpiIndex{}
+	case "base_from_yongyi_index":
+		return &BaseFromYongyiIndex{}
+	case "base_from_fenwei_index":
+		return &BaseFromFenweiIndex{}
+	case "base_from_sci99_index":
+		return &BaseFromSci99Index{}
+	case "mb_index_main_info":
+		return &BaseFromGlIndex{}
+	case "edbinfo":
+		return &BaseFromManualEdb{}
+	case "base_from_business_index":
+		return &BaseFromBusinessIndex{}
+	case "base_from_bloomberg_index":
+		return &BaseFromBloombergIndex{}
+	case "base_from_mtjh_mapping":
+		return &BaseFromMtjhMapping{}
+	}
+
+	return nil
+}
+
+// BaseFromRzdIndex 睿咨得
+type BaseFromRzdIndex struct {
+	BaseFromRzdIndexId    int       `orm:"column(base_from_rzd_index_id);pk"`
+	BaseFromRzdClassifyId int       `description:"分类ID"`
+	IndexCode             string    `description:"指标编码"`
+	IndexName             string    `description:"指标名称"`
+	Unit                  string    `description:"单位"`
+	Frequency             string    `description:"频度"`
+	StartDate             time.Time `description:"开始日期"`
+	EndDate               time.Time `description:"结束日期"`
+	LatestValue           float64   `description:"最新值"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromRzdIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_RZD, 0, "睿咨得"
+}
+
+func (m *BaseFromRzdIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_rzd_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "base_from_rzd_classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromRzdIndex) Format2SearchDataSource(origin *BaseFromRzdIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromRzdIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.BaseFromRzdClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromRzdIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromRzdIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_rzd_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromHisugarIndex 泛糖科技
+type BaseFromHisugarIndex struct {
+	BaseFromHisugarIndexId int       `orm:"column(base_from_hisugar_index_id);pk"`
+	ClassifyId             int       `description:"分类ID"`
+	IndexCode              string    `description:"指标编码"`
+	IndexName              string    `description:"指标名称"`
+	Unit                   string    `description:"单位"`
+	Frequency              string    `description:"频度"`
+	Source                 string    `description:"数据来源"`
+	Describe               string    `description:"指标描述"`
+	Sort                   int       `description:"排序"`
+	StartDate              time.Time `description:"开始日期"`
+	EndDate                time.Time `description:"结束日期"`
+	LatestValue            float64   `description:"最新值"`
+	CreateTime             time.Time `description:"创建时间"`
+	ModifyTime             time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromHisugarIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_HISUGAR, 0, "泛糖科技"
+}
+
+func (m *BaseFromHisugarIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_hisugar_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromHisugarIndex) Format2SearchDataSource(origin *BaseFromHisugarIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromHisugarIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromHisugarIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromHisugarIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_hisugar_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromLyIndex 粮油商务网
+type BaseFromLyIndex struct {
+	BaseFromLyIndexId    int       `orm:"column(base_from_ly_index_id);pk"`
+	BaseFromLyClassifyId int       `description:"分类ID"`
+	IndexCode            string    `description:"指标编码"`
+	IndexName            string    `description:"指标名称"`
+	Unit                 string    `description:"单位"`
+	Frequency            string    `description:"频度"`
+	StartDate            time.Time `description:"开始日期"`
+	EndDate              time.Time `description:"结束日期"`
+	LatestValue          float64   `description:"最新值"`
+	EdbExist             int       `description:"指标库是否已添加:0-否;1-是"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromLyIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_LY, 0, "粮油商务网"
+}
+
+func (m *BaseFromLyIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_ly_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "base_from_ly_classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromLyIndex) Format2SearchDataSource(origin *BaseFromLyIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromLyIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.BaseFromLyClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromLyIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromLyIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_ly_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromSciHqIndex 卓创红期
+type BaseFromSciHqIndex struct {
+	BaseFromSciHqIndexId int       `orm:"column(base_from_sci_hq_index_id);pk"`
+	ClassifyId           int       `description:"分类ID"`
+	IndexCode            string    `description:"指标编码"`
+	IndexName            string    `description:"指标名称"`
+	Unit                 string    `description:"单位"`
+	Frequency            string    `description:"频度"`
+	Sort                 int       `description:"排序"`
+	StartDate            time.Time `description:"开始日期"`
+	EndDate              time.Time `description:"结束日期"`
+	LatestValue          float64   `description:"最新值"`
+	LatestDate           time.Time `description:"最新更新时间"`
+	TerminalCode         string    `description:"指标描述"`
+	FilePath             string    `description:"文件路径"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromSciHqIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_SCI_HQ, 0, "卓创红期"
+}
+
+func (m *BaseFromSciHqIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_sci_hq_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromSciHqIndex) Format2SearchDataSource(origin *BaseFromSciHqIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromSciHqIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromSciHqIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromSciHqIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_sci_hq_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromOilchemIndex 隆众资讯
+type BaseFromOilchemIndex struct {
+	BaseFromOilchemIndexId int       `orm:"column(base_from_oilchem_index_id);pk"`
+	ClassifyId             int       `description:"分类ID"`
+	IndexCode              string    `description:"指标编码"`
+	IndexName              string    `description:"指标名称"`
+	Unit                   string    `description:"单位"`
+	Frequency              string    `description:"频度"`
+	StartDate              time.Time `description:"开始日期"`
+	EndDate                time.Time `description:"结束日期"`
+	LatestValue            float64   `description:"最新值"`
+	CreateTime             time.Time `description:"创建时间"`
+	ModifyTime             time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromOilchemIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_OILCHEM, 0, "隆众资讯"
+}
+
+func (m *BaseFromOilchemIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_oilchem_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromOilchemIndex) Format2SearchDataSource(origin *BaseFromOilchemIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromOilchemIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromOilchemIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromOilchemIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_oilchem_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// 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) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_THS, utils.DATA_SUB_SOURCE_HIGH_FREQUENCY, "同花顺高频"
+}
+
+func (m *BaseFromThsHfIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_ths_hf_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "base_from_ths_hf_classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromThsHfIndex) Format2SearchDataSource(origin *BaseFromThsHfIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromThsHfIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.BaseFromThsHfClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDateTime, origin.StartDate) // 高频这里是到时分秒
+	item.EndDate = utils.TimeTransferString(utils.FormatDateTime, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	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 := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_ths_hf_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromCcfIndex CCF化纤信息
+type BaseFromCcfIndex struct {
+	BaseFromCcfIndexId int       `orm:"column(base_from_ccf_index_id);pk"`
+	ClassifyId         int       `description:"分类ID"`
+	IndexCode          string    `description:"指标编码"`
+	IndexName          string    `description:"指标名称"`
+	Unit               string    `description:"单位"`
+	Frequency          string    `description:"频度"`
+	StartDate          time.Time `description:"开始日期"`
+	EndDate            time.Time `description:"结束日期"`
+	LatestValue        float64   `description:"最新值"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromCcfIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_CCF, 0, "CCF化纤信息"
+}
+
+func (m *BaseFromCcfIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_ccf_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromCcfIndex) Format2SearchDataSource(origin *BaseFromCcfIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromCcfIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromCcfIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromCcfIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_ccf_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromUsdaFasIndex 美国农业部
+type BaseFromUsdaFasIndex struct {
+	BaseFromUsdaFasIndexId int       `orm:"column(base_from_usda_fas_index_id);pk"`
+	ClassifyId             int       `description:"分类ID"`
+	IndexCode              string    `description:"指标编码"`
+	IndexName              string    `description:"指标名称"`
+	Unit                   string    `description:"单位"`
+	Frequency              string    `description:"频度"`
+	StartDate              time.Time `description:"开始日期"`
+	EndDate                time.Time `description:"结束日期"`
+	LatestValue            float64   `description:"最新值"`
+	CreateTime             time.Time `description:"创建时间"`
+	ModifyTime             time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromUsdaFasIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_USDA_FAS, 0, "美国农业部"
+}
+
+func (m *BaseFromUsdaFasIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_usda_fas_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "end_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromUsdaFasIndex) Format2SearchDataSource(origin *BaseFromUsdaFasIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromUsdaFasIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromUsdaFasIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromUsdaFasIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s, end_value AS latest_value FROM base_from_usda_fas_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromMysteelChemicalIndex 上海钢联
+type BaseFromMysteelChemicalIndex struct {
+	BaseFromMysteelChemicalIndexId    int       `orm:"column(base_from_mysteel_chemical_index_id);pk"`
+	BaseFromMysteelChemicalClassifyId int       `description:"分类ID"`
+	IndexCode                         string    `description:"指标编码"`
+	IndexName                         string    `description:"指标名称"`
+	Unit                              string    `description:"单位"`
+	Frequency                         string    `description:"频度"`
+	StartDate                         time.Time `description:"开始日期"`
+	EndDate                           time.Time `description:"结束日期"`
+	LatestValue                       float64   `description:"最新值"`
+	CreateTime                        time.Time `description:"创建时间"`
+	ModifyTime                        time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromMysteelChemicalIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, "上海钢联"
+}
+
+func (m *BaseFromMysteelChemicalIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_mysteel_chemical_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "base_from_mysteel_chemical_classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "end_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromMysteelChemicalIndex) Format2SearchDataSource(origin *BaseFromMysteelChemicalIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromMysteelChemicalIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.BaseFromMysteelChemicalClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromMysteelChemicalIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromMysteelChemicalIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s, end_value AS latest_value FROM base_from_mysteel_chemical_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromSmmIndex 有色原始数据库
+type BaseFromSmmIndex struct {
+	BaseFromSmmIndexId int       `orm:"column(base_from_smm_index_id);pk"`
+	ClassifyId         int       `description:"分类ID"`
+	IndexCode          string    `description:"指标编码"`
+	IndexName          string    `description:"指标名称"`
+	Unit               string    `description:"单位"`
+	Frequency          string    `description:"频度"`
+	StartDate          time.Time `description:"开始日期"`
+	EndDate            time.Time `description:"结束日期"`
+	LatestValue        float64   `description:"最新值"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromSmmIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_YS, 0, "SMM原始数据库"
+}
+
+func (m *BaseFromSmmIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_smm_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "end_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromSmmIndex) Format2SearchDataSource(origin *BaseFromSmmIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromSmmIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromSmmIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromSmmIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s, end_value AS latest_value FROM base_from_smm_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromBaiinfoIndex 百川盈孚
+type BaseFromBaiinfoIndex struct {
+	BaseFromBaiinfoIndexId int       `orm:"column(base_from_baiinfo_index_id);pk"`
+	ClassifyId             int       `description:"分类ID"`
+	IndexCode              string    `description:"指标编码"`
+	IndexName              string    `description:"指标名称"`
+	Unit                   string    `description:"单位"`
+	Frequency              string    `description:"频度"`
+	StartDate              time.Time `description:"开始日期"`
+	EndDate                time.Time `description:"结束日期"`
+	LatestValue            float64   `description:"最新值"`
+	CreateTime             time.Time `description:"创建时间"`
+	ModifyTime             time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromBaiinfoIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_BAIINFO, 0, "百川盈孚"
+}
+
+func (m *BaseFromBaiinfoIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_baiinfo_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromBaiinfoIndex) Format2SearchDataSource(origin *BaseFromBaiinfoIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromBaiinfoIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromBaiinfoIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromBaiinfoIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_baiinfo_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromSciIndex 卓创数据(红桃3)
+type BaseFromSciIndex struct {
+	BaseFromSciIndexId int       `orm:"column(base_from_sci_index_id);pk"`
+	ClassifyId         int       `description:"分类ID"`
+	IndexCode          string    `description:"指标编码"`
+	IndexName          string    `description:"指标名称"`
+	Unit               string    `description:"单位"`
+	Frequency          string    `description:"频度"`
+	StartDate          time.Time `description:"开始日期"`
+	EndDate            time.Time `description:"结束日期"`
+	LatestValue        float64   `description:"最新值"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromSciIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_SCI, 0, "卓创数据(红桃3)"
+}
+
+func (m *BaseFromSciIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_sci_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromSciIndex) Format2SearchDataSource(origin *BaseFromSciIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromSciIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromSciIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromSciIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_sci_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromCoalmineMapping 中国煤炭市场网
+type BaseFromCoalmineMapping struct {
+	BaseFromCoalmineMappingId int       `orm:"column(base_from_coalmine_mapping_id);pk"`
+	ClassifyId                int       `description:"分类ID"`
+	IndexCode                 string    `description:"指标编码"`
+	IndexName                 string    `description:"指标名称"`
+	Unit                      string    `description:"单位"`
+	Frequency                 string    `description:"频度"`
+	StartDate                 time.Time `description:"开始日期"`
+	EndDate                   time.Time `description:"结束日期"`
+	LatestValue               string    `description:"最新值"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromCoalmineMapping) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_COAL, 0, "中国煤炭市场网"
+}
+
+func (m *BaseFromCoalmineMapping) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_coalmine_mapping_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromCoalmineMapping) Format2SearchDataSource(origin *BaseFromCoalmineMapping) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromCoalmineMappingId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = origin.LatestValue
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromCoalmineMapping) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromCoalmineMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_coalmine_mapping WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromEiaSteoIndex EIA STEO报告
+type BaseFromEiaSteoIndex struct {
+	BaseFromEiaSteoIndexId    int       `orm:"column(base_from_eia_steo_index_id);pk"`
+	BaseFromEiaSteoClassifyId int       `description:"分类ID"`
+	IndexCode                 string    `description:"指标编码"`
+	IndexName                 string    `description:"指标名称"`
+	Unit                      string    `description:"单位"`
+	Frequency                 string    `description:"频度"`
+	StartDate                 time.Time `description:"开始日期"`
+	EndDate                   time.Time `description:"结束日期"`
+	LatestValue               float64   `description:"最新值"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromEiaSteoIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_EIA_STEO, 0, "EIA STEO报告"
+}
+
+func (m *BaseFromEiaSteoIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_eia_steo_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "base_from_eia_steo_classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromEiaSteoIndex) Format2SearchDataSource(origin *BaseFromEiaSteoIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromEiaSteoIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.BaseFromEiaSteoClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromEiaSteoIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromEiaSteoIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_eia_steo_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromIcpiIndex ICPI消费价格指数
+//type BaseFromIcpiIndex struct {
+//	BaseFromIcpiIndexId    int       `orm:"column(base_from_icpi_index_id);pk"`
+//	BaseFromIcpiClassifyId int       `description:"分类ID"`
+//	IndexCode              string    `description:"指标编码"`
+//	IndexName              string    `description:"指标名称"`
+//	Unit                   string    `description:"单位"`
+//	Frequency              string    `description:"频度"`
+//	StartDate              time.Time `description:"开始日期"`
+//	EndDate                time.Time `description:"结束日期"`
+//	LatestValue            float64   `description:"最新值"`
+//	CreateTime             time.Time `description:"创建时间"`
+//	ModifyTime             time.Time `description:"更新时间"`
+//}
+
+func (m *BaseFromIcpiIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_ICPI, 0, "ICPI消费价格指数"
+}
+
+func (m *BaseFromIcpiIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_icpi_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "base_from_icpi_classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromIcpiIndex) Format2SearchDataSource(origin *BaseFromIcpiIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromIcpiIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.BaseFromIcpiClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromIcpiIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromIcpiIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_icpi_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromYongyiIndex 涌益咨询
+type BaseFromYongyiIndex struct {
+	YongyiIndexId int       `orm:"column(yongyi_index_id);pk"`
+	ClassifyId    int       `description:"分类ID"`
+	IndexCode     string    `description:"指标编码"`
+	IndexName     string    `description:"指标名称"`
+	Unit          string    `description:"单位"`
+	Frequency     string    `description:"频度"`
+	StartDate     time.Time `description:"开始日期"`
+	EndDate       time.Time `description:"结束日期"`
+	LatestValue   float64   `description:"最新值"`
+	CreateTime    time.Time `description:"创建时间"`
+	ModifyTime    time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromYongyiIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_YONYI, 0, "涌益咨询"
+}
+
+func (m *BaseFromYongyiIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "yongyi_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromYongyiIndex) Format2SearchDataSource(origin *BaseFromYongyiIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.YongyiIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromYongyiIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromYongyiIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_yongyi_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromFenweiIndex 汾渭数据
+type BaseFromFenweiIndex struct {
+	FenweiIndexId int       `orm:"column(fenwei_index_id);pk"`
+	ClassifyId    int       `description:"分类ID"`
+	IndexCode     string    `description:"指标编码"`
+	IndexName     string    `description:"指标名称"`
+	Unit          string    `description:"单位"`
+	Frequency     string    `description:"频度"`
+	StartDate     time.Time `description:"开始日期"`
+	EndDate       time.Time `description:"结束日期"`
+	LatestValue   float64   `description:"最新值"`
+	CreateTime    time.Time `description:"创建时间"`
+	ModifyTime    time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromFenweiIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_FENWEI, 0, "汾渭数据"
+}
+
+func (m *BaseFromFenweiIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "fenwei_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromFenweiIndex) Format2SearchDataSource(origin *BaseFromFenweiIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.FenweiIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromFenweiIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromFenweiIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_fenwei_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromSci99Index 卓创资讯
+//type BaseFromSci99Index struct {
+//	BaseFromSciIndexId int       `orm:"column(base_from_sci_index_id);pk"`
+//	ClassifyId         int       `description:"分类ID"`
+//	IndexCode          string    `description:"指标编码"`
+//	IndexName          string    `description:"指标名称"`
+//	Unit               string    `description:"单位"`
+//	Frequency          string    `description:"频度"`
+//	StartDate          time.Time `description:"开始日期"`
+//	EndDate            time.Time `description:"结束日期"`
+//	LatestValue        float64   `description:"最新值"`
+//	CreateTime         time.Time `description:"创建时间"`
+//	ModifyTime         time.Time `description:"更新时间"`
+//}
+
+func (m *BaseFromSci99Index) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_SCI99, 0, "卓创资讯"
+}
+
+func (m *BaseFromSci99Index) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_sci_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "classify_id",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromSci99Index) Format2SearchDataSource(origin *BaseFromSci99Index) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromSciIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromSci99Index) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromSci99Index, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_sci99_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromGlIndex 钢联原始指标库
+type BaseFromGlIndex struct {
+	PrimaryId   int       `orm:"column(ID);pk"`
+	IndexCode   string    `orm:"column(INDEX_CODE)" description:"指标编码"`
+	IndexName   string    `orm:"column(INDEX_NAME)" description:"指标名称"`
+	Unit        string    `orm:"column(UNIT_NAME)" description:"单位"`
+	Frequency   string    `orm:"column(FREQUENCY_NAME)" description:"频度"`
+	StartDate   time.Time `orm:"column(BEGIN_DATE)" description:"开始日期"`
+	EndDate     time.Time `orm:"column(END_DATE)" description:"结束日期"`
+	LatestValue float64   `orm:"column(DATA_VALUE)" description:"最新值"`
+	CreateTime  time.Time `orm:"column(CREATE_TIME)" description:"创建时间"`
+	ModifyTime  time.Time `orm:"column(UPDATE_TIME)" description:"更新时间"`
+}
+
+func (m *BaseFromGlIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_GL, 0, "钢联原始数据库"
+}
+
+func (m *BaseFromGlIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "ID",
+		IndexCode:   "INDEX_CODE",
+		IndexName:   "INDEX_NAME",
+		ClassifyId:  "",
+		Unit:        "UNIT_NAME",
+		Frequency:   "FREQUENCY_NAME",
+		StartDate:   "BEGIN_DATE",
+		EndDate:     "END_DATE",
+		LatestValue: "DATA_VALUE",
+		CreateTime:  "CREATE_TIME",
+		ModifyTime:  "UPDATE_TIME",
+	}
+}
+
+func (m *BaseFromGlIndex) Format2SearchDataSource(origin *BaseFromGlIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.PrimaryId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	//item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromGlIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromGlIndex, err error) {
+	o := orm.NewOrmUsingDB("gl")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY CREATE_TIME DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM mb_index_main_info WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromManualEdb 手工数据录入
+type BaseFromManualEdb struct {
+	//PrimaryId   int       `orm:"column(TRADE_CODE)"` // 注手工指标没自增ID...
+	IndexCode   string    `orm:"column(TRADE_CODE)" description:"指标编码"`
+	IndexName   string    `orm:"column(SEC_NAME)" description:"指标名称"`
+	ClassifyId  int       `orm:"column(classify_id)" description:"分类ID"`
+	Unit        string    `orm:"column(UNIT)" description:"单位"`
+	Frequency   string    `orm:"column(frequency)" description:"频度"`
+	StartDate   time.Time `orm:"column(start_date)" description:"开始日期"`
+	EndDate     time.Time `orm:"column(end_date)" description:"结束日期"`
+	LatestValue float64   `orm:"column(latest_value)" description:"最新值"`
+	CreateTime  time.Time `orm:"column(create_date)" description:"创建时间"`
+	ModifyTime  time.Time `orm:"column(modify_time)" description:"更新时间"`
+}
+
+func (m *BaseFromManualEdb) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_MANUAL, 0, "手工指标录入"
+}
+
+func (m *BaseFromManualEdb) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "",
+		IndexCode:   "TRADE_CODE",
+		IndexName:   "SEC_NAME",
+		ClassifyId:  "classify_id",
+		Unit:        "UNIT",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_date",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromManualEdb) Format2SearchDataSource(origin *BaseFromManualEdb) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	//item.PrimaryId = origin.PrimaryId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromManualEdb) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromManualEdb, err error) {
+	o := orm.NewOrmUsingDB("edb")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_date DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM edbinfo WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromBusinessIndex 自有数据
+type BaseFromBusinessIndex struct {
+	BaseFromBusinessIndexId int `orm:"column(base_from_business_index_id);pk"`
+	//ClassifyId              int       `description:"分类ID"`
+	IndexCode   string    `description:"指标编码"`
+	IndexName   string    `description:"指标名称"`
+	Unit        string    `description:"单位"`
+	Frequency   string    `description:"频度"`
+	StartDate   time.Time `description:"开始日期"`
+	EndDate     time.Time `description:"结束日期"`
+	LatestValue float64   `description:"最新值"`
+	CreateTime  time.Time `description:"创建时间"`
+	ModifyTime  time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromBusinessIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_BUSINESS, 0, "自有数据"
+}
+
+func (m *BaseFromBusinessIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_business_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromBusinessIndex) Format2SearchDataSource(origin *BaseFromBusinessIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromBusinessIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	//item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromBusinessIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromBusinessIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_business_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromBloombergIndex Bloomberg
+type BaseFromBloombergIndex struct {
+	BaseFromBloombergIndexId int `orm:"column(base_from_bloomberg_index_id);pk"`
+	//ClassifyId              int       `description:"分类ID"`
+	IndexCode   string    `description:"指标编码"`
+	IndexName   string    `description:"指标名称"`
+	Unit        string    `description:"单位"`
+	Frequency   string    `description:"频度"`
+	StartDate   time.Time `description:"开始日期"`
+	EndDate     time.Time `description:"结束日期"`
+	LatestValue float64   `description:"最新值"`
+	CreateTime  time.Time `description:"创建时间"`
+	ModifyTime  time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromBloombergIndex) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_BLOOMBERG, 0, "Bloomberg"
+}
+
+func (m *BaseFromBloombergIndex) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_bloomberg_index_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromBloombergIndex) Format2SearchDataSource(origin *BaseFromBloombergIndex) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromBloombergIndexId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	//item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromBloombergIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromBloombergIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_bloomberg_index WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseFromMtjhMapping 煤炭江湖
+type BaseFromMtjhMapping struct {
+	BaseFromMtjhMappingId int `orm:"column(base_from_mtjh_mapping_id);pk"`
+	//ClassifyId            int       `description:"分类ID"`
+	IndexCode   string    `description:"指标编码"`
+	IndexName   string    `description:"指标名称"`
+	Unit        string    `description:"单位"`
+	Frequency   string    `description:"频度"`
+	StartDate   time.Time `description:"开始日期"`
+	EndDate     time.Time `description:"结束日期"`
+	LatestValue float64   `description:"最新值"`
+	CreateTime  time.Time `description:"创建时间"`
+	ModifyTime  time.Time `description:"更新时间"`
+}
+
+func (m *BaseFromMtjhMapping) SourceInfo() (int, int, string) {
+	return utils.DATA_SOURCE_MTJH, 0, "煤炭江湖"
+}
+
+func (m *BaseFromMtjhMapping) EsCols() SearchEsCols {
+	return SearchEsCols{
+		PrimaryId:   "base_from_mtjh_mapping_id",
+		IndexCode:   "index_code",
+		IndexName:   "index_name",
+		ClassifyId:  "",
+		Unit:        "unit",
+		Frequency:   "frequency",
+		StartDate:   "start_date",
+		EndDate:     "end_date",
+		LatestValue: "latest_value",
+		CreateTime:  "create_time",
+		ModifyTime:  "modify_time",
+	}
+}
+
+func (m *BaseFromMtjhMapping) Format2SearchDataSource(origin *BaseFromMtjhMapping) (item *SearchDataSource) {
+	if origin == nil {
+		return
+	}
+	source, subSource, sourceName := m.SourceInfo()
+	item = new(SearchDataSource)
+	item.PrimaryId = origin.BaseFromMtjhMappingId
+	item.IndexCode = origin.IndexCode
+	item.IndexName = origin.IndexName
+	//item.ClassifyId = origin.ClassifyId
+	item.Unit = origin.Unit
+	item.Frequency = origin.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, origin.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, origin.EndDate)
+	item.LatestValue = fmt.Sprint(origin.LatestValue)
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
+	return
+}
+
+func (m *BaseFromMtjhMapping) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromMtjhMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM base_from_mtjh_mapping WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// BaseIndexDataMinMax 数据源极值信息
+type BaseIndexDataMinMax struct {
+	MinDate     string `description:"最小日期"`
+	MaxDate     string `description:"最大日期"`
+	LatestValue string `description:"最新值"`
+	//MinValue    float64 `description:"最小值"`
+	//MaxValue    float64 `description:"最大值"`
+}
+
+// GetBaseIndexDataTableName 根据来源获取原始指标表和数据表名(很大一部分来源不存在原始指标)
+func GetBaseIndexDataTableName(source, subSource int) (indexTable, dataTable string) {
+	switch source {
+	case utils.DATA_SOURCE_THS:
+		if subSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY {
+			indexTable = "base_from_ths_hf_index"
+			dataTable = "base_from_ths_hf_data"
+		}
+	case utils.DATA_SOURCE_RZD:
+		indexTable = "base_from_rzd_index"
+		dataTable = "base_from_rzd_data"
+	case utils.DATA_SOURCE_HISUGAR:
+		indexTable = "base_from_hisugar_index"
+		dataTable = "base_from_hisugar_data"
+	case utils.DATA_SOURCE_LY:
+		indexTable = "base_from_ly_index"
+		dataTable = "base_from_ly_data"
+	case utils.DATA_SOURCE_SCI_HQ:
+		indexTable = "base_from_sci_hq_index"
+		dataTable = "base_from_sci_hq_data"
+	case utils.DATA_SOURCE_OILCHEM:
+		indexTable = "base_from_oilchem_index"
+		dataTable = "base_from_oilchem_data"
+	case utils.DATA_SOURCE_CCF:
+		indexTable = "base_from_ccf_index"
+		dataTable = "base_from_ccf_data"
+	case utils.DATA_SOURCE_USDA_FAS:
+		indexTable = "base_from_usda_fas_index"
+		dataTable = "base_from_usda_fas_data"
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL:
+		indexTable = "base_from_mysteel_chemical_index"
+		dataTable = "base_from_mysteel_chemical_data"
+	case utils.DATA_SOURCE_YS:
+		indexTable = "base_from_smm_index"
+		dataTable = "base_from_smm_data"
+	case utils.DATA_SOURCE_BAIINFO:
+		indexTable = "base_from_baiinfo_index"
+		dataTable = "base_from_baiinfo_data"
+	case utils.DATA_SOURCE_SCI:
+		indexTable = "base_from_sci_index"
+		dataTable = "base_from_sci_data"
+	case utils.DATA_SOURCE_EIA_STEO:
+		indexTable = "base_from_eia_steo_index"
+		dataTable = "base_from_eia_steo_data"
+	case utils.DATA_SOURCE_ICPI:
+		indexTable = "base_from_icpi_index"
+		dataTable = "base_from_icpi_data"
+	case utils.DATA_SOURCE_YONYI:
+		indexTable = "base_from_yongyi_index"
+		dataTable = "base_from_yongyi_data"
+	case utils.DATA_SOURCE_FENWEI:
+		indexTable = "base_from_fenwei_index"
+		dataTable = "base_from_fenwei_data"
+	case utils.DATA_SOURCE_SCI99:
+		indexTable = "base_from_sci99_index"
+		dataTable = "base_from_sci99_data"
+	case utils.DATA_SOURCE_BUSINESS:
+		indexTable = "base_from_business_index"
+		dataTable = "base_from_business_data"
+	case utils.DATA_SOURCE_BLOOMBERG:
+		indexTable = "base_from_bloomberg_index"
+		dataTable = "base_from_bloomberg_data"
+	default:
+		utils.FileLog.Info(fmt.Sprintf("数据源无对应表名, source: %d, sub: %d", source, subSource))
+	}
+	return
+}
+
+// getCoalmineDataTableName 获取中国煤炭市场网数据表名
+func getCoalmineDataTableName(indexCode string) string {
+	if strings.Contains(indexCode, "jsm") {
+		return "base_from_coalmine_jsm_index"
+	}
+	if strings.Contains(indexCode, "company") {
+		return "base_from_coalmine_company_index"
+	}
+	if strings.Contains(indexCode, "firm") {
+		return "base_from_coalmine_firm_index"
+	}
+	if strings.Contains(indexCode, "coastal") {
+		return "base_from_coalmine_coastal_index"
+	}
+	if strings.Contains(indexCode, "inland") {
+		return "base_from_coalmine_inland_index"
+	}
+	return ""
+}
+
+// GetBaseIndexDataMinMax 获取数据源极值
+func GetBaseIndexDataMinMax(source, subSource int, indexCode string) (item *BaseIndexDataMinMax, err error) {
+	o := orm.NewOrmUsingDB("data")
+	var sql string
+	var latestVal string
+
+	// 煤炭江湖
+	if source == utils.DATA_SOURCE_MTJH {
+		sql = `SELECT MIN(data_time) AS min_date, MAX(data_time) AS max_date FROM base_from_mtjh_index WHERE index_code = ?`
+		if err = o.Raw(sql, indexCode).QueryRow(&item); err != nil {
+			return
+		}
+
+		sql = `SELECT deal_value AS latest_value FROM base_from_mtjh_index WHERE index_code = ? ORDER BY data_time DESC LIMIT 1`
+		if err = o.Raw(sql, indexCode).QueryRow(&latestVal); err != nil {
+			return
+		}
+		item.LatestValue = latestVal
+		return
+	}
+
+	// 中国煤炭市场网
+	if source == utils.DATA_SOURCE_COAL {
+		dataTable := getCoalmineDataTableName(indexCode)
+		if dataTable == "" {
+			err = fmt.Errorf("中国煤炭市场网-指标无对应表名: %s", indexCode)
+			return
+		}
+		fieldDataTime := "data_time"
+		if dataTable == "base_from_coalmine_firm_index" {
+			fieldDataTime = "data_time_date"
+		}
+		sql = `SELECT MIN(%s) AS min_date, MAX(%s) AS max_date FROM %s WHERE index_code = ?`
+		sql = fmt.Sprintf(sql, fieldDataTime, fieldDataTime, dataTable)
+		if err = o.Raw(sql, indexCode).QueryRow(&item); err != nil {
+			return
+		}
+
+		sql = `SELECT deal_value AS latest_value FROM %s WHERE index_code = ? ORDER BY %s DESC LIMIT 1`
+		sql = fmt.Sprintf(sql, dataTable, fieldDataTime)
+		if err = o.Raw(sql, indexCode).QueryRow(&latestVal); err != nil {
+			return
+		}
+		item.LatestValue = latestVal
+		return
+	}
+
+	// 其他数据源
+	_, dataTable := GetBaseIndexDataTableName(source, subSource)
+	if dataTable == "" {
+		err = fmt.Errorf("数据源无对应数据表, source: %d, sub: %d, code: %s", source, subSource, indexCode)
+		return
+	}
+
+	sql = `SELECT MIN(data_time) AS min_date, MAX(data_time) AS max_date FROM %s WHERE index_code = ?`
+	sql = fmt.Sprintf(sql, dataTable)
+	if err = o.Raw(sql, indexCode).QueryRow(&item); err != nil {
+		return
+	}
+
+	sql = `SELECT value AS latest_value FROM %s WHERE index_code = ? ORDER BY data_time DESC LIMIT 1`
+	sql = fmt.Sprintf(sql, dataTable)
+	if err = o.Raw(sql, indexCode).QueryRow(&latestVal); err != nil {
+		return
+	}
+	item.LatestValue = latestVal
+	return
+}
+
+// BaseFromMtjhIndex 煤炭江湖数据表
+type BaseFromMtjhIndex struct {
+	BaseFromMtjhIndexId int       `orm:"column(base_from_mtjh_index_id);pk"`
+	IndexCode           string    `description:"指标编码"`
+	IndexName           string    `description:"指标名称"`
+	DealValue           float64   `description:"成交量"`
+	DataTime            time.Time `description:"数据日期"`
+	Unit                string    `description:"单位"`
+	Frequency           string    `description:"频度"`
+	StartDate           time.Time `description:"开始日期"`
+	EndDate             time.Time `description:"结束日期"`
+	LatestValue         float64   `description:"最新值"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"更新时间"`
+}
+
+// GetMtjhBaseInfoFromDataTable 煤炭江湖-从数据表获取基础信息
+func GetMtjhBaseInfoFromDataTable(codes []string) (items []*BaseFromMtjhIndex, err error) {
+	codeLens := len(codes)
+	if codeLens == 0 {
+		return
+	}
+	sql := fmt.Sprintf(`SELECT * FROM base_from_mtjh_index WHERE index_code IN (%s) GROUP BY index_code`, utils.GetOrmInReplace(codeLens))
+	_, err = orm.NewOrmUsingDB("data").Raw(sql, codes).QueryRows(&items)
+	return
+}
+
+// BaseFromCoalmineIndexBase 中国煤炭市场网数据表基础信息
+type BaseFromCoalmineIndexBase struct {
+	IndexCode  string    `description:"指标编码"`
+	IndexName  string    `description:"指标名称"`
+	Unit       string    `description:"单位"`
+	Frequency  string    `description:"频度"`
+	CreateTime time.Time `description:"创建时间"`
+	ModifyTime time.Time `description:"更新时间"`
+}
+
+// GetCoalmineBaseInfoFromDataTable 中国煤炭市场网-从数据表获取基础信息
+func GetCoalmineBaseInfoFromDataTable(codes []string) (items []*BaseFromCoalmineIndexBase, err error) {
+	codeLens := len(codes)
+	if codeLens == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	items = make([]*BaseFromCoalmineIndexBase, 0)
+	var jsmCodes, companyCodes, firmCodes, coastalCodes, inlandCodes []string
+	for _, code := range codes {
+		if strings.Contains(code, "jsm") {
+			jsmCodes = append(jsmCodes, code)
+		}
+		if strings.Contains(code, "company") {
+			companyCodes = append(companyCodes, code)
+		}
+		if strings.Contains(code, "firm") {
+			firmCodes = append(firmCodes, code)
+		}
+		if strings.Contains(code, "coastal") {
+			coastalCodes = append(coastalCodes, code)
+		}
+		if strings.Contains(code, "inland") {
+			inlandCodes = append(inlandCodes, code)
+		}
+	}
+	var sql string
+	itemsOnce := make([]*BaseFromCoalmineIndexBase, 0)
+	if len(jsmCodes) > 0 {
+		sql = fmt.Sprintf(`SELECT * FROM base_from_coalmine_jsm_index WHERE index_code IN (%s) GROUP BY index_code`, utils.GetOrmInReplace(len(jsmCodes)))
+		_, err = o.Raw(sql, jsmCodes).QueryRows(&itemsOnce)
+		if err != nil {
+			return
+		}
+		items = append(items, itemsOnce...)
+	}
+	if len(companyCodes) > 0 {
+		sql = fmt.Sprintf(`SELECT * FROM base_from_coalmine_company_index WHERE index_code IN (%s) GROUP BY index_code`, utils.GetOrmInReplace(len(companyCodes)))
+		_, err = o.Raw(sql, companyCodes).QueryRows(&itemsOnce)
+		if err != nil {
+			return
+		}
+		items = append(items, itemsOnce...)
+	}
+	if len(firmCodes) > 0 {
+		sql = fmt.Sprintf(`SELECT * FROM base_from_coalmine_firm_index WHERE index_code IN (%s) GROUP BY index_code`, utils.GetOrmInReplace(len(firmCodes)))
+		_, err = o.Raw(sql, firmCodes).QueryRows(&itemsOnce)
+		if err != nil {
+			return
+		}
+		items = append(items, itemsOnce...)
+	}
+	if len(coastalCodes) > 0 {
+		sql = fmt.Sprintf(`SELECT * FROM base_from_coalmine_coastal_index WHERE index_code IN (%s) GROUP BY index_code`, utils.GetOrmInReplace(len(coastalCodes)))
+		_, err = o.Raw(sql, coastalCodes).QueryRows(&itemsOnce)
+		if err != nil {
+			return
+		}
+		items = append(items, itemsOnce...)
+	}
+	if len(inlandCodes) > 0 {
+		sql = fmt.Sprintf(`SELECT * FROM base_from_coalmine_inland_index WHERE index_code IN (%s) GROUP BY index_code`, utils.GetOrmInReplace(len(inlandCodes)))
+		_, err = o.Raw(sql, inlandCodes).QueryRows(&itemsOnce)
+		if err != nil {
+			return
+		}
+		items = append(items, itemsOnce...)
+	}
+	return
+}

+ 2 - 0
models/data_source/icpi.go

@@ -16,6 +16,8 @@ type BaseFromIcpiIndex struct {
 	EndDate                time.Time `description:"结束日期"`
 	CreateTime             time.Time `description:"创建时间"`
 	ModifyTime             time.Time `description:"修改时间"`
+	Unit                   string    `description:"单位"`
+	LatestValue            float64   `description:"最新值"`
 }
 
 type BaseFromIcpiData struct {

+ 21 - 2
models/db.go

@@ -16,10 +16,12 @@ import (
 	future_good2 "eta/eta_api/models/data_manage/future_good"
 	"eta/eta_api/models/data_manage/stl"
 	"eta/eta_api/models/data_manage/supply_analysis"
+	tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
 	"eta/eta_api/models/data_stat"
 	edbmonitor "eta/eta_api/models/edb_monitor"
 	"eta/eta_api/models/eta_trial"
 	"eta/eta_api/models/fe_calendar"
+	"eta/eta_api/models/material"
 	"eta/eta_api/models/ppt_english"
 	"eta/eta_api/models/report"
 	"eta/eta_api/models/report_approve"
@@ -29,7 +31,6 @@ import (
 	"eta/eta_api/models/speech_recognition"
 	"eta/eta_api/models/system"
 	"eta/eta_api/models/yb"
-	binlogSvr "eta/eta_api/services/binlog"
 	"eta/eta_api/utils"
 	"time"
 
@@ -210,6 +211,9 @@ func init() {
 	// 初始化因子指标系列
 	initFactorEdbSeries()
 
+	// 注册素材库表
+	initMaterial()
+
 	// 初始化STL指标系列
 	initStlEdbInfo()
 
@@ -219,8 +223,9 @@ func init() {
 	// 开启mysql binlog监听
 	if utils.MYSQL_DATA_BINLOG_URL != "" {
 		initBinlog()
-		go binlogSvr.ListenMysql()
+		//go binlogSvr.ListenMysql()
 	}
+
 	// 初始化部分数据表变量(直接init会有顺序问题=_=!)
 	afterInitTable()
 
@@ -378,6 +383,9 @@ func initEdbData() {
 		new(data_manage.BaseFromSciHqClassify),
 		new(data_manage.BaseFromSciHqIndex),
 		new(data_manage.BaseFromSciHqData),
+		new(data_manage.BaseFromClarksonsClassify),
+		new(data_manage.BaseFromClarksonsIndex),
+		new(data_manage.BaseFromClarksonsData),
 	)
 }
 
@@ -539,6 +547,7 @@ func initExcel() {
 		new(excel.ExcelChartEdb),         // 平衡表做图指标
 		new(excel.ExcelChartData),        // 平衡表作图数据
 		new(excel.ExcelInfoRuleMapping),  //表格的管理规则
+		new(tradeAnalysisModel.TradeAnalysisTableColumn), // 持仓分析表格-自定义列
 		new(excel.ReferencedExcelConfig), //表格的样式引用
 	)
 }
@@ -660,6 +669,14 @@ func initFactorEdbSeries() {
 	)
 }
 
+// initMaterial 注册素材库表
+func initMaterial() {
+	//注册对象
+	orm.RegisterModel(
+		new(material.Material),         //素材库表
+		new(material.MaterialClassify), //素材库分类表
+	)
+}
 func initStlEdbInfo() {
 	orm.RegisterModel(
 		new(stl.CalculateStlConfig),        // STL指标配置
@@ -701,6 +718,8 @@ func initAiPredictModel() {
 		new(aiPredictModel.AiPredictModelData),
 		new(aiPredictModel.AiPredictModelDashboard),
 		new(aiPredictModel.AiPredictModelDashboardDetail),
+		new(aiPredictModel.AiPredictModelFramework),
+		new(aiPredictModel.AiPredictModelFrameworkNode),
 	)
 }
 

+ 25 - 21
models/manual_edb.go

@@ -66,27 +66,31 @@ func GetEdbDataListByCode(tradeCode string) (items []*Edbdata, err error) {
 
 // EdbInfoListItem
 type EdbInfoListItem struct {
-	TradeCode    string `orm:"column(TRADE_CODE);pk" description:"指标code"`
-	SecName      string `orm:"column(SEC_NAME);" description:"指标名称"`
-	Unit         string `orm:"column(UNIT);" description:"单位"`
-	Remark       string `orm:"column(REMARK);" description:"备注"`
-	Frequency    string `description:"频度"`
-	ClassifyId   int    `description:"分类id"`
-	ClassifyName string `description:"分类名称"`
-	CreateDate   string `description:"创建时间"`
-	UserId       int    `description:"录入用户id"`
-	NoticeTime   string `description:"通知时间"`
-	Mobile       string `description:"录入者手机号"`
-	ModifyDate   string `description:"待更新日期"`
-	ModifyTime   string `description:"数据更新时间"`
-	Status       string `description:"状态:未完成/完成"`
-	UniqueCode   string
-	IsJoinEdb    int8    `description:"指标库是否已添加:0-否;1-是"`
-	UserName     string  `description:"录入用户名称"`
-	StartDate    string  `description:"数据开始日期"`
-	EndDate      string  `description:"数据结束日期"`
-	LatestValue  float64 `description:"指标最新值"`
-	NextDateTime string  `description:"下期时间"`
+	TradeCode          string `orm:"column(TRADE_CODE);pk" description:"指标code"`
+	SecName            string `orm:"column(SEC_NAME);" description:"指标名称"`
+	Unit               string `orm:"column(UNIT);" description:"单位"`
+	Remark             string `orm:"column(REMARK);" description:"备注"`
+	Frequency          string `description:"频度"`
+	ClassifyId         int    `description:"分类id"`
+	ClassifyName       string `description:"分类名称"`
+	CreateDate         string `description:"创建时间"`
+	UserId             int    `description:"录入用户id"`
+	NoticeTime         string `description:"通知时间"`
+	Mobile             string `description:"录入者手机号"`
+	ModifyDate         string `description:"待更新日期"`
+	ModifyTime         string `description:"数据更新时间"`
+	Status             string `description:"状态:未完成/完成"`
+	UniqueCode         string
+	IsJoinEdb          int8    `description:"指标库是否已添加:0-否;1-是"`
+	UserName           string  `description:"录入用户名称"`
+	StartDate          string  `description:"数据开始日期"`
+	EndDate            string  `description:"数据结束日期"`
+	LatestValue        float64 `description:"指标最新值"`
+	NextDateTime       string  `description:"下期时间"`
+	Source             int     `description:"来源"`
+	SourceName         string  `description:"数据源名称"`
+	SearchText         string  `description:"搜索结果(含高亮)"`
+	ClassifyUniqueCode string  `description:"分类唯一编码(前端定位用)"`
 }
 
 // EdbListResp 指标数据结构体

Some files were not shown because too many files changed in this diff