Browse Source

处理冲突

kobe6258 2 months ago
parent
commit
0d2ea6492a
100 changed files with 12737 additions and 1037 deletions
  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. 71 25
      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. 28 5
      controllers/data_manage/chart_info.go
  11. 3 0
      controllers/data_manage/chart_theme.go
  12. 1653 0
      controllers/data_manage/clarksons_data.go
  13. 18 20
      controllers/data_manage/correlation/correlation_chart_classify.go
  14. 6 2
      controllers/data_manage/correlation/correlation_chart_info.go
  15. 8 3
      controllers/data_manage/cross_variety/chart_info.go
  16. 161 3
      controllers/data_manage/edb_classify.go
  17. 237 3
      controllers/data_manage/edb_info.go
  18. 10 1
      controllers/data_manage/edb_info_refresh.go
  19. 35 7
      controllers/data_manage/edb_info_relation.go
  20. 1 0
      controllers/data_manage/eia_steo.go
  21. 5 0
      controllers/data_manage/excel/balance_table.go
  22. 11 0
      controllers/data_manage/excel/custom_analysis.go
  23. 406 11
      controllers/data_manage/excel/excel_info.go
  24. 51 28
      controllers/data_manage/fenwei_data.go
  25. 8 3
      controllers/data_manage/future_good/future_good_chart_info.go
  26. 59 10
      controllers/data_manage/gl_data.go
  27. 1045 0
      controllers/data_manage/gpr_risk_data.go
  28. 22 0
      controllers/data_manage/jiayue_edb_source.go
  29. 6 2
      controllers/data_manage/line_equation/line_chart_info.go
  30. 18 2
      controllers/data_manage/line_feature/chart_info.go
  31. 45 11
      controllers/data_manage/manual_edb.go
  32. 9 0
      controllers/data_manage/my_chart.go
  33. 2 2
      controllers/data_manage/predict_edb_classify.go
  34. 186 70
      controllers/data_manage/predict_edb_info.go
  35. 6 2
      controllers/data_manage/range_analysis/chart_info.go
  36. 50 25
      controllers/data_manage/smm_api.go
  37. 3 0
      controllers/data_manage/smm_data.go
  38. 6 1
      controllers/data_manage/stl/stl.go
  39. 272 0
      controllers/data_source/data_source.go
  40. 19 21
      controllers/data_stat/edb_source_stat.go
  41. 31 0
      controllers/english_report/report.go
  42. 256 0
      controllers/eta_forum/eta_forum.go
  43. 1908 0
      controllers/material/material.go
  44. 32 0
      controllers/ppt_english.go
  45. 37 0
      controllers/ppt_v2.go
  46. 133 110
      controllers/report.go
  47. 108 119
      controllers/report_chapter.go
  48. 1 0
      controllers/report_chapter_type.go
  49. 45 15
      controllers/report_v2.go
  50. 286 0
      controllers/residual_analysis/residual_analysis_controller.go
  51. 280 227
      controllers/resource.go
  52. 35 20
      controllers/sandbox/sandbox.go
  53. 4 1
      controllers/sys_department.go
  54. 9 2
      controllers/sys_group.go
  55. 5 2
      controllers/sys_role.go
  56. 93 3
      controllers/trade_analysis/trade_analysis.go
  57. 719 0
      controllers/trade_analysis/trade_analysis_correlation.go
  58. 842 0
      controllers/trade_analysis/trade_analysis_table.go
  59. 8 4
      controllers/trade_analysis/warehouse.go
  60. 14 11
      controllers/voice.go
  61. 366 0
      models/ai_predict_model/ai_predict_model_framework.go
  62. 144 0
      models/ai_predict_model/ai_predict_model_framework_node.go
  63. 2 0
      models/ai_predict_model/ai_predict_model_index.go
  64. 46 38
      models/business_conf.go
  65. 80 0
      models/common/common_classify.go
  66. 233 0
      models/data_manage/base_from_clarksons_classify.go
  67. 81 0
      models/data_manage/base_from_clarksons_data.go
  68. 424 0
      models/data_manage/base_from_clarksons_index.go
  69. 1 0
      models/data_manage/base_from_eia_stero.go
  70. 16 0
      models/data_manage/base_from_fenwei.go
  71. 280 0
      models/data_manage/base_from_gpr_risk.go
  72. 149 0
      models/data_manage/base_from_gpr_risk_classify.go
  73. 21 14
      models/data_manage/base_from_mtjh.go
  74. 1 1
      models/data_manage/base_from_smm_classify.go
  75. 7 0
      models/data_manage/chart_framework.go
  76. 15 3
      models/data_manage/chart_info.go
  77. 26 4
      models/data_manage/data_manage_permission/edb.go
  78. 9 1
      models/data_manage/data_manage_permission/excel.go
  79. 2 0
      models/data_manage/edb_classify.go
  80. 6 6
      models/data_manage/edb_data_wind.go
  81. 51 35
      models/data_manage/edb_info.go
  82. 11 11
      models/data_manage/edb_info_relation.go
  83. 32 3
      models/data_manage/excel/excel_info.go
  84. 177 0
      models/data_manage/excel/referenced_excel_config.go
  85. 1 0
      models/data_manage/excel/request/excel_info.go
  86. 1 0
      models/data_manage/excel/request/mixed_table.go
  87. 17 10
      models/data_manage/excel/response/excel_info.go
  88. 33 7
      models/data_manage/gl_data.go
  89. 1 0
      models/data_manage/my_chart.go
  90. 10 10
      models/data_manage/mysteel_chemical_index.go
  91. 3 0
      models/data_manage/predict_edb_conf.go
  92. 74 0
      models/data_manage/request/clarksons_data.go
  93. 12 0
      models/data_manage/request/edb_info.go
  94. 9 7
      models/data_manage/request/predict_edb_info.go
  95. 35 0
      models/data_manage/response/clarksons_data.go
  96. 3 2
      models/data_manage/smm_data.go
  97. 10 0
      models/data_manage/trade_analysis/base_from_trade_exchange.go
  98. 35 0
      models/data_manage/trade_analysis/request/trade_analysis_correlation.go
  99. 37 0
      models/data_manage/trade_analysis/request/trade_analysis_table.go
  100. 17 0
      models/data_manage/trade_analysis/response/trade_analysis_correlation.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 = "操作成功"
+}

+ 71 - 25
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)
 	}
 
@@ -255,13 +298,7 @@ func (this *AiPredictModelIndexController) Import() {
 				// 其余信息
 				imports[indexName].Index.ModelFramework = strings.TrimSpace(cells[2].String())
 				strDate := strings.TrimSpace(cells[4].String())
-				predictDate, _ := time.Parse("2006/01/02", strDate)
-				if predictDate.IsZero() {
-					predictDate, _ = time.Parse("01-02-06", strDate)
-					if predictDate.IsZero() {
-						predictDate, _ = time.Parse("2006/1/2", strDate)
-					}
-				}
+				predictDate, _ := utils.GetExcelDate(strDate)
 				imports[indexName].Index.PredictDate = predictDate
 
 				strVal := strings.TrimSpace(cells[5].String())
@@ -344,15 +381,9 @@ func (this *AiPredictModelIndexController) Import() {
 					case 1:
 						// 日期列
 						strDate := strings.TrimSpace(cell.String())
-						dataDate, _ := time.Parse("2006/01/02", strDate)
+						dataDate, _ := utils.GetExcelDate(strDate)
 						if dataDate.IsZero() {
-							dataDate, _ = time.Parse("01-02-06", strDate)
-							if dataDate.IsZero() {
-								dataDate, _ = time.Parse("2006/1/2", strDate)
-								if dataDate.IsZero() {
-									continue
-								}
-							}
+							continue
 						}
 						colKeys[ck].DataDate = dataDate
 						colKeys[ck+1].DataDate = dataDate
@@ -487,15 +518,9 @@ func (this *AiPredictModelIndexController) Import() {
 					case 1:
 						// 日期列
 						strDate := strings.TrimSpace(cell.String())
-						dataDate, _ := time.Parse("2006/01/02", strDate)
+						dataDate, _ := utils.GetExcelDate(strDate)
 						if dataDate.IsZero() {
-							dataDate, _ = time.Parse("01-02-06", strDate)
-							if dataDate.IsZero() {
-								dataDate, _ = time.Parse("2006/1/2", strDate)
-								if dataDate.IsZero() {
-									continue
-								}
-							}
+							continue
 						}
 						colKeys[ck].DataDate = dataDate
 						colKeys[ck+1].DataDate = dataDate
@@ -564,6 +589,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

+ 28 - 5
controllers/data_manage/chart_info.go

@@ -1060,6 +1060,10 @@ func (this *ChartInfoController) ChartInfoDetail() {
 			}
 		}
 	}
+	if chartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+		// 季度图,的至今N年,需要特殊处理,将日期范围扩大到下一年
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
 	// 开始/结束日期
 	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, dateMax)
 
@@ -1670,7 +1674,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 = "获取失败"
@@ -2291,7 +2295,7 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 		showSysId = sysUser.AdminId
 	}
 
-	var searchList []*data_manage.ChartInfo
+	var searchList []*data_manage.ChartInfoMore
 	var total int64
 	var err error
 
@@ -2400,7 +2404,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)
@@ -2410,7 +2414,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)
 		}
 	}
@@ -2826,6 +2833,10 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 			}
 		}
 	}
+	if chartInfo.ChartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+		// 季度图,的至今N年,需要特殊处理,将日期范围扩大到下一年
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
 	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, dateMax)
 
 	extraConfigStr := chartInfo.ExtraConfig //图表额外数据参数
@@ -3285,7 +3296,10 @@ func (this *ChartInfoController) CopyChartInfo() {
 	{
 		mapList := make([]*data_manage.ChartEdbMapping, 0)
 		for _, v := range edbMappingList {
+			// windows server环境这里得加个延时,不然生成时间戳都是一样的
+			time.Sleep(100 * time.Millisecond)
 			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			//utils.FileLog.Info("IndexCode: %s, UniqueCode: %s", v.EdbInfoId, utils.MD5(utils.CHART_PREFIX+"_"+timestamp))
 			mapItem := &data_manage.ChartEdbMapping{
 				//ChartEdbMappingId: 0,
 				ChartInfoId:   chartInfo.ChartInfoId,
@@ -4267,6 +4281,10 @@ func (this *ChartInfoController) ChartInfoConvertDetail() {
 				}
 			}
 		}
+		if chartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+			// 季节性图,最近N年需要特殊处理
+			dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+		}
 		// 开始/结束日期
 		startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, dateMax)
 
@@ -4744,7 +4762,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, ",")

+ 3 - 0
controllers/data_manage/chart_theme.go

@@ -250,6 +250,9 @@ func (c *ChartThemeController) GetThemePreviewData() {
 			}
 		}
 	}
+	if chartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
 	// 开始/结束日期
 	startDate, endDate := utils.GetDateByDateTypeV2(dateType, tmpStartDate, tmpEndDate, startYear, dateMax)
 

+ 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
+}

+ 18 - 20
controllers/data_manage/correlation/correlation_chart_classify.go

@@ -449,6 +449,7 @@ func (this *CorrelationChartClassifyController) DeleteChartClassify() {
 		}
 	}
 	resp := new(data_manage.AddChartInfoResp)
+
 	//删除图表
 	if req.ChartInfoId > 0 {
 		chartInfo, err := data_manage.GetChartInfoById(req.ChartInfoId)
@@ -492,13 +493,23 @@ func (this *CorrelationChartClassifyController) DeleteChartClassify() {
 		}
 
 		source := chartInfo.Source // 相关性图表(滚动相关性)
-		//删除图表及关联指标
+
+		// 删除图表及关联指标
 		err = data_manage.DeleteChartInfoAndData(chartInfo.ChartInfoId)
 		if err != nil {
 			br.Msg = "删除失败"
 			br.ErrMsg = "删除失败,Err:" + err.Error()
 			return
 		}
+
+		// 删除图表关联
+		e = correlationServ.RemoveCorrelationRelate(chartInfo.ChartInfoId)
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = fmt.Sprintf("删除相关性图表关联失败, %v", e)
+			return
+		}
+
 		//删除ES
 		{
 			go data.EsDeleteChartInfo(chartInfo.ChartInfoId)
@@ -507,13 +518,11 @@ func (this *CorrelationChartClassifyController) DeleteChartClassify() {
 			go data.EsDeleteMyChartInfoByMyChartIds(myIds)
 		}
 
+		// 删除后定位至其他图(不知道原需求具体定位到哪张...修复的时候只是多加上了个source条件)
 		var condition string
 		var pars []interface{}
-		condition += " AND chart_classify_id=? AND source = ? "
-		pars = append(pars, chartInfo.ChartClassifyId, source)
-
-		condition += " AND chart_info_id>? ORDER BY create_time ASC LIMIT 1 "
-		pars = append(pars, req.ChartInfoId)
+		condition += ` AND chart_classify_id = ? AND source = ? AND chart_info_id > ? ORDER BY create_time ASC LIMIT 1`
+		pars = append(pars, chartInfo.ChartClassifyId, source, req.ChartInfoId)
 
 		nextItem, err := data_manage.GetChartInfoByCondition(condition, pars)
 		if err != nil && err.Error() != utils.ErrNoRow() {
@@ -529,11 +538,8 @@ func (this *CorrelationChartClassifyController) DeleteChartClassify() {
 			var condition string
 			var pars []interface{}
 
-			condition += " AND level=1 "
-			//pars = append(pars, chartInfo.ChartClassifyId)
-
-			condition += " AND chart_classify_id>? ORDER BY chart_classify_id ASC LIMIT 1 "
-			pars = append(pars, chartInfo.ChartClassifyId)
+			condition += ` AND level = 1 AND chart_classify_id > ? AND source = ? ORDER BY chart_classify_id ASC LIMIT 1`
+			pars = append(pars, chartInfo.ChartClassifyId, source)
 
 			classifyItem, err := data_manage.GetChartClassifyByCondition(condition, pars)
 			if err != nil && err.Error() != utils.ErrNoRow() {
@@ -542,7 +548,7 @@ func (this *CorrelationChartClassifyController) DeleteChartClassify() {
 				return
 			}
 			if classifyItem != nil {
-				nextItem, err = data_manage.GetNextChartInfo(chartInfo.ChartClassifyId)
+				nextItem, err = data_manage.GetNextChartByClassifyIdAndSource(chartInfo.ChartClassifyId, source)
 				if err != nil && err.Error() != utils.ErrNoRow() {
 					br.Msg = "删除失败"
 					br.ErrMsg = "获取下一级图库信息失败,Err:" + err.Error()
@@ -555,14 +561,6 @@ func (this *CorrelationChartClassifyController) DeleteChartClassify() {
 			}
 		}
 
-		// 删除图表关联
-		e = correlationServ.RemoveCorrelationRelate(chartInfo.ChartInfoId)
-		if e != nil {
-			br.Msg = "删除失败"
-			br.ErrMsg = fmt.Sprintf("删除相关性图表关联失败, %v", e)
-			return
-		}
-
 		//新增操作日志
 		{
 			chartLog := new(data_manage.ChartInfoLog)

+ 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{

+ 406 - 11
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)
+			}
 		}
 
 	}
@@ -1091,6 +1106,16 @@ func (c *ExcelInfoController) Edit() {
 		err = excel3.AddExcelDraft(excelDraftInfo)
 	}
 
+	// 判断是否清除该表引用
+	if req.IsColChange {
+		err = excel3.DeleteReferencedExcelConfig(excelInfo.UniqueCode)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = err.Error()
+			return
+		}
+	}
+
 	resp := response.AddExcelInfoResp{
 		ExcelInfoId: excelInfo.ExcelInfoId,
 		UniqueCode:  excelInfo.UniqueCode,
@@ -1100,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 = "修改成功"
@@ -1421,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
@@ -1556,6 +1591,8 @@ func (c *ExcelInfoController) GetExcelTableData() {
 	}
 	uniqueCode := c.GetString("UniqueCode")
 	fromScene, _ := c.GetInt("FromScene", 0)
+	referencedId, _ := c.GetInt("ReferencedId", 0)
+	uuid := c.GetString("Uuid")
 
 	var err error
 	if uniqueCode == `` {
@@ -1621,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 {
@@ -1687,17 +1724,26 @@ func (c *ExcelInfoController) GetExcelTableData() {
 		}
 	}
 
+	// 获取表格引用
+	reference, err := excel3.GetReferencedExcelConfig(referencedId, fromScene, uniqueCode, uuid)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取表格引用失败,Err:" + err.Error()
+		return
+	}
+
 	resp := response.ExcelTableDetailResp{
-		UniqueCode:    excelInfo.UniqueCode,
-		ExcelImage:    excelInfo.ExcelImage,
-		ExcelName:     excelInfo.ExcelName,
-		TableInfo:     tableData,
-		Config:        config,
-		SourcesFrom:   excelInfo.SourcesFrom,
-		ExcelSource:   excelSource,
-		ExcelSourceEn: excelSourceEn,
-		ExcelInfoId:   excelInfo.ExcelInfoId,
-		Source:        excelInfo.Source,
+		UniqueCode:            excelInfo.UniqueCode,
+		ExcelImage:            excelInfo.ExcelImage,
+		ExcelName:             excelInfo.ExcelName,
+		TableInfo:             tableData,
+		Config:                config,
+		SourcesFrom:           excelInfo.SourcesFrom,
+		ExcelSource:           excelSource,
+		ExcelSourceEn:         excelSourceEn,
+		ExcelInfoId:           excelInfo.ExcelInfoId,
+		Source:                excelInfo.Source,
+		ReferencedExcelConfig: reference,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -3370,3 +3416,352 @@ func (c *ExcelInfoController) GetExcelRuleDetail() {
 	br.Ret = 200
 	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 表格引用配置详情
+// @Param   ExcelInfoRuleMappingId   query   int  true       "id"
+// @Success Ret=200 获取成功
+// @router /excel_info/reference/detail [get]
+//func (c *ExcelInfoController) GetExcelReferenceDetail() {
+//	br := new(models.BaseResponse).Init()
+//	defer func() {
+//		c.Data["json"] = br
+//		c.ServeJSON()
+//	}()
+//	sysUser := c.SysUser
+//	if sysUser == nil {
+//		br.Msg = "请登录"
+//		br.ErrMsg = "请登录,SysUser Is Empty"
+//		br.Ret = 408
+//		return
+//	}
+//	excelReferenceCode := c.GetString("ExcelReferenceCode")
+//
+//	item, err := excel3.GetReferencedExcelConfigByUniqueCode(excelReferenceCode)
+//	if err != nil && err.Error() != utils.ErrNoRow() {
+//		br.Msg = "获取规则"
+//		br.ErrMsg = "管理规则添加失败,Err:" + err.Error()
+//		return
+//	}
+//
+//	br.Data = item
+//	br.Msg = "获取成功"
+//	br.Ret = 200
+//	br.Success = true
+//}
+
+// GetExcelReferenceDetail
+// @Title 表格引用配置保存
+// @Description 表格引用配置保存
+// @Param   ExcelInfoRuleMappingId   query   int  true       "id"
+// @Success Ret=200 获取成功
+// @router /excel_info/reference/save [post]
+func (c *ExcelInfoController) SaveExcelReference() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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 excel3.ExcelReferencesReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	addList := make([]*excel3.ReferencedExcelConfig, 0)
+
+	exist, e := excel3.GetReferencedExcelConfig(req.ReferencedId, req.FromScene, req.UniqueCode, req.Uuid)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "查找引用失败"
+		br.ErrMsg = "查找引用失败,Err:" + e.Error()
+		return
+	}
+	if exist.ReferencedExcelConfigId == 0 {
+		item := excel3.ReferencedExcelConfig{
+			UniqueCode:   req.UniqueCode,
+			ReferencedId: req.ReferencedId,
+			FromScene:    req.FromScene,
+			Uuid:         req.Uuid,
+			WidthList:    req.WidthList,
+			HeightList:   req.HeightList,
+			OpUserId:     sysUser.AdminId,
+			OpUserName:   sysUser.RealName,
+			CreateTime:   time.Now(),
+			ModifyTime:   time.Now(),
+		}
+		addList = append(addList, &item)
+	} else {
+		exist.WidthList = req.WidthList
+		exist.HeightList = req.HeightList
+		exist.OpUserId = sysUser.AdminId
+		exist.OpUserName = sysUser.RealName
+		exist.ModifyTime = time.Now()
+		err = excel3.UpdateReferencedExcelConfig(&exist)
+		if err != nil {
+			br.Msg = "更新引用失败"
+			br.ErrMsg = "更新引用失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	if len(addList) > 0 {
+		err = excel3.AddReferencedExcelConfig(addList)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "添加引用失败"
+			br.ErrMsg = "添加引用失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	excelInfo, err := excel3.GetExcelInfoByUnicode(req.UniqueCode)
+	if err != nil {
+		br.Msg = "获取表格信息失败"
+		br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
+		return
+	}
+	if excelInfo.ExcelInfoId > 0 {
+		//删除公共图库那边的缓存
+		_ = utils.Rc.Delete(utils.HZ_CHART_LIB_EXCEL_TABLE_DETAIL + ":" + excelInfo.UniqueCode)
+	}
+
+	br.Msg = "添加成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 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)
 		}
 	}

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

@@ -1851,6 +1851,9 @@ func (this *LineFeaturesChartInfoController) Detail() {
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
 			maxDate = latestDateT
 		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+		}
 		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, errMsg = lineFeatureServ.GetStandardDeviationData(0, startDate, endDate, edbMapping, calculateValue)
 	case utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE:
@@ -1866,6 +1869,9 @@ func (this *LineFeaturesChartInfoController) Detail() {
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
 			maxDate = latestDateT
 		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+		}
 		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, errMsg = lineFeatureServ.GetPercentileData(0, startDate, endDate, edbMapping, percentileConfig.CalculateValue, percentileConfig.CalculateUnit, percentileConfig.PercentType)
 	case utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
@@ -2480,6 +2486,9 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
 			maxDate = latestDateT
 		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+		}
 		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, msg = lineFeatureServ.GetStandardDeviationData(0, startDate, endDate, edbMapping, calculateValue)
 	case utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE:
@@ -2495,6 +2504,9 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
 			maxDate = latestDateT
 		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+		}
 		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, msg = lineFeatureServ.GetPercentileData(0, startDate, endDate, edbMapping, percentileConfig.CalculateValue, percentileConfig.CalculateUnit, percentileConfig.PercentType)
 	case utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
@@ -2745,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
 
@@ -2798,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 = "获取成功"
+}

+ 19 - 21
controllers/data_stat/edb_source_stat.go

@@ -804,24 +804,26 @@ func (this *EdbSourceStatController) EdbUpdateFailedList() {
 
 	terminalCode := this.GetString("TerminalCode", "")
 	createTime := this.GetString("CreateTime", "")
+	condition := " and source = ? and terminal_code = ?"
+	var pars []interface{}
+	pars = append(pars, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, terminalCode)
 
-	if terminalCode == "" {
-		br.Msg = "请选择对应的终端信息"
-		return
-	}
-	terminalInfo, err := data_manage.GetEdbTerminalByTerminalCode(terminalCode)
-	if err != nil {
-		if err.Error() == utils.ErrNoRow() {
-			br.Msg = "终端不存在"
+	terminalName := ""
+	terminalDir := ""
+	if terminalCode != "" {
+		terminalInfo, err := data_manage.GetEdbTerminalByTerminalCode(terminalCode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "终端不存在"
+				return
+			}
+			br.Msg = "查询终端信息出错"
+			br.ErrMsg = "查询终端信息出错 Err:" + err.Error()
 			return
 		}
-		br.Msg = "查询终端信息出错"
-		br.ErrMsg = "查询终端信息出错 Err:" + err.Error()
-		return
+		terminalName = terminalInfo.Name
+		terminalDir = terminalInfo.DirPath
 	}
-	condition := " and source = ? and terminal_code = ?"
-	var pars []interface{}
-	pars = append(pars, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, terminalCode)
 
 	if createTime != "" {
 		startT, err := time.ParseInLocation(utils.FormatDate, createTime, time.Local)
@@ -856,9 +858,9 @@ func (this *EdbSourceStatController) EdbUpdateFailedList() {
 	}
 	resp := data_stat.GetEdbUpdateFailedResp{
 		List:             list,
-		Name:             terminalInfo.Name,
+		Name:             terminalName,
 		TerminalCode:     terminalCode,
-		DirPath:          terminalInfo.DirPath,
+		DirPath:          terminalDir,
 		UpdateSuccessNum: successNum,
 		UpdateFailedNum:  failedNum,
 	}
@@ -909,16 +911,12 @@ func (this *EdbSourceStatController) EdbUpdateFailedDetailList() {
 		return
 	}
 
-	if terminalCode == "" {
-		br.Msg = "请选择对应的终端信息"
-		return
-	}
 	if frequency == "" {
 		br.Msg = "请选择对应的频度"
 		return
 	}
 
-	condition := " and source = ? and terminal_code = ? and frequency=? and data_update_failed_reason=? and data_update_result = 2"
+	condition := " and source = ? AND terminal_code = ? and frequency=? and data_update_failed_reason=? and data_update_result = 2"
 	var pars []interface{}
 	pars = append(pars, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, terminalCode, frequency, sourceUpdateFailedReason)
 

+ 31 - 0
controllers/english_report/report.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
+	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/report_approve"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
@@ -140,6 +141,32 @@ func (this *EnglishReportController) Add() {
 	{
 		go models.ModifyEnglishReportCode(newReportId, reportCode)
 	}
+
+	excelReferences := make([]*excel.ReferencedExcelConfig, 0)
+	for _, r := range req.ExcelReferences {
+		refItem := excel.ReferencedExcelConfig{
+			UniqueCode:   r.UniqueCode,
+			ReferencedId: int(newReportId),
+			FromScene:    utils.TableReferencedByEnReport,
+			Uuid:         r.Uuid,
+			WidthList:    r.WidthList,
+			HeightList:   r.HeightList,
+			OpUserId:     this.SysUser.AdminId,
+			OpUserName:   this.SysUser.RealName,
+			CreateTime:   time.Now(),
+			ModifyTime:   time.Now(),
+		}
+		excelReferences = append(excelReferences, &refItem)
+	}
+	if len(excelReferences) > 0 {
+		err = excel.AddReferencedExcelConfig(excelReferences)
+		if err != nil {
+			br.Msg = "新增引用失败"
+			br.ErrMsg = "新增引用失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	resp := new(models.AddEnglishReportResp)
 	resp.ReportId = newReportId
 	resp.ReportCode = reportCode
@@ -198,6 +225,7 @@ func (this *EnglishReportController) Edit() {
 	}
 	var contentSub string
 	if req.Content != "" {
+		req.Content = services.HandleReportContentTable(int(req.ReportId), req.Content)
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
 			br.Msg = "存在非法标签"
@@ -320,6 +348,8 @@ func (this *EnglishReportController) Detail() {
 
 	item.Content = html.UnescapeString(item.Content)
 	item.ContentSub = html.UnescapeString(item.ContentSub)
+	// 处理关联excel的表格id
+	item.Content = services.HandleReportContentTable(item.Id, item.Content)
 
 	classifyNameMap := make(map[int]*models.EnglishClassifyFullName)
 	if item.ClassifyIdSecond > 0 {
@@ -1068,6 +1098,7 @@ func (this *EnglishReportController) ClassifyIdDetail() {
 			}
 		}
 	}
+	item.Content = services.HandleReportContentTable(0, item.Content)
 
 	br.Ret = 200
 	br.Success = true

+ 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"
+}

+ 32 - 0
controllers/ppt_english.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
+	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/ppt_english"
 	"eta/eta_api/services"
 	"eta/eta_api/services/ppt"
@@ -173,6 +174,37 @@ func (this *PptEnglishController) AddPpt() {
 			return
 		}
 		msg = "新增成功"
+
+		excelReferences := make([]*excel.ReferencedExcelConfig, 0)
+		for _, r := range req.ExcelReferences {
+			refItem := excel.ReferencedExcelConfig{
+				UniqueCode:   r.UniqueCode,
+				ReferencedId: int(newId),
+				FromScene:    utils.TableReferencedByEnPPT,
+				Uuid:         r.Uuid,
+				WidthList:    r.WidthList,
+				HeightList:   r.HeightList,
+				OpUserId:     this.SysUser.AdminId,
+				OpUserName:   this.SysUser.RealName,
+				CreateTime:   time.Now(),
+				ModifyTime:   time.Now(),
+			}
+			excelReferences = append(excelReferences, &refItem)
+		}
+		if len(excelReferences) > 0 {
+			err = excel.AddReferencedExcelConfig(excelReferences)
+			if err != nil {
+				br.Msg = "新增引用失败"
+				br.ErrMsg = "新增引用失败,Err:" + err.Error()
+				return
+			}
+		}
+
+		// 合并ppt后,将ppt的表格关系做处理
+		if len(req.MergePptIdList) > 0 {
+			services.HandlerMergePptTableReferenced(req.MergePptIdList, int(newId), this.SysUser.AdminId, this.SysUser.RealName, utils.TableReferencedByEnPPT)
+		}
+
 	} else {
 		newId = req.PptId
 

+ 37 - 0
controllers/ppt_v2.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
+	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/ppt_english"
 	"eta/eta_api/services"
 	"eta/eta_api/services/ppt"
@@ -174,6 +175,36 @@ func (this *PptV2Controller) AddPpt() {
 			return
 		}
 		msg = "新增成功"
+
+		excelReferences := make([]*excel.ReferencedExcelConfig, 0)
+		for _, r := range req.ExcelReferences {
+			refItem := excel.ReferencedExcelConfig{
+				UniqueCode:   r.UniqueCode,
+				ReferencedId: int(newId),
+				FromScene:    utils.TableReferencedByPPT,
+				Uuid:         r.Uuid,
+				WidthList:    r.WidthList,
+				HeightList:   r.HeightList,
+				OpUserId:     this.SysUser.AdminId,
+				OpUserName:   this.SysUser.RealName,
+				CreateTime:   time.Now(),
+				ModifyTime:   time.Now(),
+			}
+			excelReferences = append(excelReferences, &refItem)
+		}
+		if len(excelReferences) > 0 {
+			err = excel.AddReferencedExcelConfig(excelReferences)
+			if err != nil {
+				br.Msg = "新增引用失败"
+				br.ErrMsg = "新增引用失败,Err:" + err.Error()
+				return
+			}
+		}
+
+		// 合并ppt后,将ppt的表格关系做处理
+		if len(req.MergePptIdList) > 0 {
+			services.HandlerMergePptTableReferenced(req.MergePptIdList, int(newId), this.SysUser.AdminId, this.SysUser.RealName, utils.TableReferencedByPPT)
+		}
 	} else {
 		newId = req.PptId
 
@@ -1097,6 +1128,9 @@ func (this *PptV2Controller) ToEn() {
 		return
 	}
 
+	// ppt转英文ppt后,将ppt的表格关系做处理
+	services.HandlerPptToEnPptTableReferenced(origin.PptId, int(newId), this.SysUser.AdminId, this.SysUser.RealName)
+
 	resp := ppt_english.AddPptEnglishResp{
 		PptId: newId,
 	}
@@ -1195,6 +1229,9 @@ func (this *PptV2Controller) BatchToEn() {
 			br.ErrMsg = "新增英文PPT和目录映射失败, Err: " + e.Error()
 			return
 		}
+
+		// ppt转英文ppt后,将ppt的表格关系做处理
+		services.HandlerPptToEnPptTableReferenced(origin.PptId, int(newId), this.SysUser.AdminId, this.SysUser.RealName)
 	}
 
 	br.Ret = 200

+ 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)

+ 108 - 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 {
@@ -389,6 +364,8 @@ func (this *ReportController) EditDayWeekChapter() {
 	// 更新章节及指标
 	contentSub := ""
 	if req.Content != "" {
+		// 处理关联excel的表格id
+		req.Content = services.HandleReportContentTable(reportInfo.Id, req.Content)
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
 			br.Msg = "存在非法标签"
@@ -429,6 +406,9 @@ func (this *ReportController) EditDayWeekChapter() {
 	reportChapterInfo.LastModifyAdminId = sysUser.AdminId
 	reportChapterInfo.LastModifyAdminName = sysUser.RealName
 	reportChapterInfo.ContentModifyTime = time.Now()
+	if req.ContentStruct != `` {
+		req.ContentStruct = services.HandleReportContentStructTable(reportChapterInfo.ReportId, req.ContentStruct)
+	}
 	reportChapterInfo.ContentStruct = html.EscapeString(req.ContentStruct)
 
 	updateCols = append(updateCols, "Author", "Content", "ContentSub", "IsEdit", "ModifyTime")
@@ -530,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 {
@@ -572,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 != "" {
@@ -629,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()
@@ -757,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)
 		}
@@ -811,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()
@@ -826,6 +780,8 @@ func (this *ReportController) GetDayWeekChapter() {
 	chapterItem.Content = html.UnescapeString(chapterItem.Content)
 	chapterItem.ContentSub = html.UnescapeString(chapterItem.ContentSub)
 	chapterItem.ContentStruct = html.UnescapeString(chapterItem.ContentStruct)
+	chapterItem.Content = services.HandleReportContentTable(chapterItem.ReportId, chapterItem.Content)
+	chapterItem.ContentStruct = services.HandleReportContentStructTable(chapterItem.ReportId, chapterItem.ContentStruct)
 
 	// 授权用户列表map
 	chapterGrantIdList := make([]int, 0)
@@ -986,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
 	}
 
 	// 更新章节标签
@@ -1129,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()
@@ -1557,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 {
@@ -1726,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
+}
+

+ 1 - 0
controllers/report_chapter_type.go

@@ -161,6 +161,7 @@ func (this *ReportChapterTypeController) Add() {
 	item.IsSet = 0
 	item.ReportChapterTypeKey = req.ReportChapterTypeName
 	item.TickerTitle = req.ReportChapterTypeName
+	item.IsShow = 1
 
 	if e = item.Create(); e != nil {
 		br.Msg = "操作失败"

+ 45 - 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 != "" {
@@ -573,6 +590,9 @@ func (this *ReportController) Edit() {
 		return
 	}
 
+	req.Content = services.HandleReportContentTable(int(req.ReportId), req.Content)
+	req.ContentStruct = services.HandleReportContentStructTable(int(req.ReportId), req.ContentStruct)
+
 	// 编辑报告信息
 	err, errMsg := services.EditReport(reportInfo, req, sysUser)
 	if err != nil {
@@ -684,6 +704,13 @@ func (this *ReportController) Detail() {
 		item.EndStyle = endResource.Style
 	}
 
+	item.Content = services.HandleReportContentTable(item.Id, item.Content)
+	item.ContentStruct = services.HandleReportContentStructTable(item.Id, item.ContentStruct)
+	for _, v := range chapterList {
+		v.Content = services.HandleReportContentTable(item.Id, v.Content)
+		v.ContentStruct = services.HandleReportContentStructTable(item.Id, v.ContentStruct)
+	}
+
 	resp := &models.ReportDetailView{
 		ReportDetail: item,
 		ChapterList:  chapterList,
@@ -897,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)
 		}
 	}

+ 14 - 11
controllers/voice.go

@@ -6,7 +6,6 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/kgiannakakis/mp3duration/src/mp3duration"
 	"github.com/rdlucklib/rdluck_tools/http"
 	"os"
 	"path"
@@ -116,15 +115,19 @@ func (this *VoiceController) Upload() {
 		return
 	}
 
-	var playSeconds float64
-	playSeconds, err = mp3duration.Calculate(fpath)
-	if playSeconds <= 0 {
-		playSeconds, err = utils.GetVideoPlaySeconds(fpath)
-		if err != nil {
-			br.Msg = "获取音频时间失败"
-			br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
-			return
-		}
+	//var playSeconds float64
+	//playSeconds, err = mp3duration.Calculate(fpath)
+	//if playSeconds <= 0 {
+	//	playSeconds, err = utils.GetVideoPlaySeconds(fpath)
+	//	if err != nil {
+	//		br.Msg = "获取音频时间失败"
+	//		br.ErrMsg = "获取音频时间失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
+	playSecondsStr, err := utils.GetDuration(fpath) //mp3duration.Calculate(fPath)
+	if err != nil {
+		utils.FileLog.Info("获取音频时间失败,Err:" + err.Error())
 	}
 
 	fileBody, err := os.ReadFile(fpath)
@@ -146,7 +149,7 @@ func (this *VoiceController) Upload() {
 
 		reportInfo.VideoUrl = resourceUrl
 		reportInfo.VideoName = videoName
-		reportInfo.VideoPlaySeconds = fmt.Sprint(playSeconds)
+		reportInfo.VideoPlaySeconds = playSecondsStr
 		reportInfo.VideoSize = sizeStr
 		reportInfo.LastModifyAdminId = this.SysUser.AdminId
 		reportInfo.LastModifyAdminName = this.SysUser.RealName

+ 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
+}

+ 15 - 3
models/data_manage/chart_info.go

@@ -73,8 +73,9 @@ type AreaExtraConf 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) {
@@ -1733,7 +1734,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")
 
@@ -2934,3 +2935,14 @@ func UpdateChartClassifyIdByChartInfoId(chartInfoIds []int, classifyId int) (err
 	_, err = o.Raw(sql, classifyId, chartInfoIds).Exec()
 	return
 }
+
+func GetNextChartByClassifyIdAndSource(classifyId, source int) (item *ChartInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT b.* FROM chart_classify AS a
+		INNER JOIN chart_info AS b ON a.chart_classify_id=b.chart_classify_id
+		WHERE a.chart_classify_id > ? AND a.source = ?
+		ORDER BY a.chart_classify_id ASC
+		LIMIT 1`
+	err = o.Raw(sql, classifyId, source).QueryRow(&item)
+	return
+}

+ 26 - 4
models/data_manage/data_manage_permission/edb.go

@@ -107,11 +107,33 @@ func SetPermissionByEdbIdList(edbIdList []string, userIdList []int, edbInfoType
 
 	// 获取已经配置的指标权限用户
 	edbInfoPermissionList := make([]*EdbInfoPermission, 0)
-	sql := `SELECT * FROM edb_info_permission WHERE edb_info_type = ? AND edb_info_id in (` + utils.GetOrmInReplace(edbNum) + `) `
-	_, err = o.Raw(sql, edbInfoType, edbIdList).QueryRows(&edbInfoPermissionList)
-	if err != nil {
-		return
+	// 定义批次大小
+	batchSize := 500
+	var sql string
+	for i := 0; i < edbNum; i += batchSize {
+		// 确定当前批次的结束索引
+		end := i + batchSize
+		if end > edbNum {
+			end = edbNum
+		}
+
+		// 获取当前批次的 ID 列表
+		batch := edbIdList[i:end]
+
+		// 生成批次查询 SQL
+		sql = `SELECT * FROM edb_info_permission WHERE edb_info_type = ? AND edb_info_id in (` + utils.GetOrmInReplace(len(batch)) + `)`
+
+		// 执行查询
+		var batchResult []*EdbInfoPermission
+		_, err = o.Raw(sql, edbInfoType, batch).QueryRows(&batchResult)
+		if err != nil {
+			return
+		}
+
+		// 将批次结果追加到总列表中
+		edbInfoPermissionList = append(edbInfoPermissionList, batchResult...)
 	}
+
 	edbInfoPermissionMap := make(map[string]*EdbInfoPermission)
 	for _, v := range edbInfoPermissionList {
 		edbInfoPermissionMap[fmt.Sprint(v.EdbInfoId, "_", v.SysUserId)] = v

+ 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
+}

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

@@ -0,0 +1,177 @@
+package excel
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ReferencedExcelConfig struct {
+	ReferencedExcelConfigId int       `orm:"column(referenced_excel_config_id);pk;auto" ` // excel表格配置id
+	UniqueCode              string    // 表格唯一编码
+	ReferencedId            int       // 被引用的id,报告就是报告id,pptId
+	FromScene               int       // 引用类型 1智能研报 2研报列表 3英文研报 4PPT 5英文ppt
+	Uuid                    string    // 引用唯一标识
+	WidthList               string    // 宽度数组
+	HeightList              string    // 高度数组
+	OpUserId                int       // 当前编辑操作的用户id
+	OpUserName              string    // 当前编辑的用户名称(冗余字段,避免查表)
+	CreateTime              time.Time // 创建时间
+	Content                 string    // 内容
+	ModifyTime              time.Time // 修改时间
+}
+
+type ExcelReferencesReq struct {
+	UniqueCode   string `description:"表格唯一编码"`
+	ReferencedId int    `description:"被引用的ID"`
+	FromScene    int    `description:"引用类型 1智能研报 2研报列表 3英文研报 4PPT 5英文PPT"`
+	Uuid         string `description:"引用唯一标识"`
+	WidthList    string `description:"宽度数组"`
+	HeightList   string `description:"高度数组"`
+}
+
+// add
+func AddReferencedExcelConfig(items []*ReferencedExcelConfig) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	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")
+	sql := ` SELECT * FROM referenced_excel_config WHERE referenced_id = ? AND from_scene = ? AND unique_code = ? AND  uuid= ? `
+	err = o.Raw(sql, referencedId, fromScene, uniqueCode, uuid).QueryRow(&item)
+	return
+}
+
+// update
+func UpdateReferencedExcelConfig(item *ReferencedExcelConfig) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(item, "WidthList", "HeightList", "OpUserId", "OpUserName", "ModifyTime")
+	return
+}
+
+// delete
+func DeleteReferencedExcelConfig(uniqueCode string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `DELETE FROM referenced_excel_config WHERE unique_code=? `
+	_, err = o.Raw(sql, uniqueCode).Exec()
+	return
+}
+
+// GetReferencedExcelConfigList
+// @Description: 根据来源类型和来源id获取所有配置列表
+// @author: Roc
+// @datetime 2025-01-09 14:31:20
+// @param referencedId int
+// @param fromScene int
+// @return items []ReferencedExcelConfig
+// @return err error
+func GetReferencedExcelConfigList(referencedId, fromScene int) (items []ReferencedExcelConfig, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM referenced_excel_config WHERE referenced_id = ? AND from_scene = ? `
+	_, err = o.Raw(sql, referencedId, fromScene).QueryRows(&items)
+
+	return
+}
+
+// CopyReferencedExcelConfigByReferencedIdAndFromScene
+// @Description: 根据原引用Id和引用类型创建新的表格关系
+// @author: Roc
+// @datetime 2025-01-09 15:02:43
+// @param oldReferencedId int
+// @param oldFromScene int
+// @param newReferencedId int
+// @param newFromScene int
+// @param sysUserId int
+// @param sysUserName string
+// @return err error
+func CopyReferencedExcelConfigByReferencedIdAndFromScene(oldReferencedId, oldFromScene, newReferencedId, newFromScene, sysUserId int, sysUserName string) (addList []*ReferencedExcelConfig, err error) {
+	o := orm.NewOrmUsingDB("data")
+	var items []ReferencedExcelConfig
+	sql := ` SELECT * FROM referenced_excel_config WHERE referenced_id = ? AND from_scene = ? `
+	_, err = o.Raw(sql, oldReferencedId, oldFromScene).QueryRows(&items)
+	if err != nil {
+		return
+	}
+
+	addList = make([]*ReferencedExcelConfig, 0)
+	for _, v := range items {
+		addList = append(addList, &ReferencedExcelConfig{
+			ReferencedExcelConfigId: 0,
+			UniqueCode:              v.UniqueCode,
+			ReferencedId:            newReferencedId,
+			FromScene:               newFromScene,
+			Uuid:                    v.Uuid,
+			WidthList:               v.WidthList,
+			HeightList:              v.HeightList,
+			OpUserId:                sysUserId,
+			OpUserName:              sysUserName,
+			CreateTime:              time.Now(),
+			Content:                 v.Content,
+			ModifyTime:              time.Now(),
+		})
+	}
+	// 批量复制表格关系
+	_, err = o.InsertMulti(utils.MultiAddNum, addList)
+
+	return
+}
+
+// CopyReferencedExcelConfigByReferencedIdListAndFromScene
+// @Description: 根据原引用Id列表和引用类型创建新的表格关系
+// @author: Roc
+// @datetime 2025-01-09 17:37:47
+// @param oldReferencedIdList []int
+// @param oldFromScene int
+// @param newReferencedId int
+// @param newFromScene int
+// @param sysUserId int
+// @param sysUserName string
+// @return addList []*ReferencedExcelConfig
+// @return err error
+func CopyReferencedExcelConfigByReferencedIdListAndFromScene(oldReferencedIdList []int, oldFromScene, newReferencedId, newFromScene, sysUserId int, sysUserName string) (addList []*ReferencedExcelConfig, err error) {
+	num := len(oldReferencedIdList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrmUsingDB("data")
+	var items []ReferencedExcelConfig
+	sql := ` SELECT * FROM referenced_excel_config WHERE referenced_id in (` + utils.GetOrmInReplace(num) + `) AND from_scene = ? group by unique_code,uuid `
+	_, err = o.Raw(sql, oldReferencedIdList, oldFromScene).QueryRows(&items)
+	if err != nil {
+		return
+	}
+
+	addList = make([]*ReferencedExcelConfig, 0)
+	for _, v := range items {
+		addList = append(addList, &ReferencedExcelConfig{
+			ReferencedExcelConfigId: 0,
+			UniqueCode:              v.UniqueCode,
+			ReferencedId:            newReferencedId,
+			FromScene:               newFromScene,
+			Uuid:                    v.Uuid,
+			WidthList:               v.WidthList,
+			HeightList:              v.HeightList,
+			OpUserId:                sysUserId,
+			OpUserName:              sysUserName,
+			CreateTime:              time.Now(),
+			Content:                 v.Content,
+			ModifyTime:              time.Now(),
+		})
+	}
+	// 批量复制表格关系
+	_, err = o.InsertMulti(utils.MultiAddNum, addList)
+
+	return
+}

+ 1 - 0
models/data_manage/excel/request/excel_info.go

@@ -39,6 +39,7 @@ type EditExcelInfoReq struct {
 	Content         string      `description:"Excel表格内容"`
 	TableData       interface{} `description:"自定义表格的数据内容"`
 	SourcesFrom     string      `description:"图表来源"`
+	IsColChange     bool        `description:"是否修改过行列,true时清空引用"`
 }
 
 // SetExcelInfoImageReq 设置excel表格图片请求

+ 1 - 0
models/data_manage/excel/request/mixed_table.go

@@ -142,6 +142,7 @@ type MixCellShowStyle struct {
 	Color           string      `description:"颜色值,#RRG" json:"color"`
 	BackgroundColor string      `description:"背景颜色值,#RRG" json:"background-color"`
 	Align           string      `description:"对齐方式:left|center|right" json:"align"`
+	FontSize        string      `description:"字号大小" json:"font-size"`
 }
 
 type DateDataBeforeAfterReq struct {

+ 17 - 10
models/data_manage/excel/response/excel_info.go

@@ -23,16 +23,17 @@ type ExcelListResp struct {
 
 // ExcelTableDetailResp  excel表格详情
 type ExcelTableDetailResp struct {
-	UniqueCode    string `description:"表格唯一code"`
-	ExcelImage    string `description:"表格截图"`
-	ExcelName     string `description:"表格名称"`
-	TableInfo     excel.TableData
-	Config        ExcelTableDetailConfigResp
-	SourcesFrom   string `description:"图表来源"`
-	ExcelSource   string `description:"表格来源str"`
-	ExcelSourceEn string `description:"表格来源(英文)"`
-	ExcelInfoId   int    `description:"表id"`
-	Source        int    `description:"表格来源"`
+	UniqueCode            string `description:"表格唯一code"`
+	ExcelImage            string `description:"表格截图"`
+	ExcelName             string `description:"表格名称"`
+	TableInfo             excel.TableData
+	Config                ExcelTableDetailConfigResp
+	SourcesFrom           string                       `description:"图表来源"`
+	ExcelSource           string                       `description:"表格来源str"`
+	ExcelSourceEn         string                       `description:"表格来源(英文)"`
+	ExcelInfoId           int                          `description:"表id"`
+	Source                int                          `description:"表格来源"`
+	ReferencedExcelConfig excel2.ReferencedExcelConfig `description:"表格引用信息"`
 }
 
 // ExcelTableDetailConfigResp
@@ -126,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:"表格的按钮权限"`
+}

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