Browse Source

Merge remote-tracking branch 'origin/master' into eta/2.4.5

# Conflicts:
#	controllers/english_report/report.go
#	controllers/report_chapter.go
#	controllers/report_v2.go
#	models/business_conf.go
#	services/report_v2.go
#	utils/constants.go
Roc 1 month ago
parent
commit
eb7d0eb265
100 changed files with 9511 additions and 1307 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. 65 25
      controllers/data_manage/ai_predict_model/index.go
  5. 101 71
      controllers/data_manage/base_from_ths_hf.go
  6. 78 15
      controllers/data_manage/bloomberg_data.go
  7. 43 17
      controllers/data_manage/business_data.go
  8. 52 25
      controllers/data_manage/chart_classify.go
  9. 25 3
      controllers/data_manage/chart_framework.go
  10. 88 3
      controllers/data_manage/chart_info.go
  11. 3 0
      controllers/data_manage/chart_theme.go
  12. 18 20
      controllers/data_manage/correlation/correlation_chart_classify.go
  13. 6 2
      controllers/data_manage/correlation/correlation_chart_info.go
  14. 8 3
      controllers/data_manage/cross_variety/chart_info.go
  15. 50 3
      controllers/data_manage/edb_info.go
  16. 10 1
      controllers/data_manage/edb_info_refresh.go
  17. 1 0
      controllers/data_manage/eia_steo.go
  18. 5 0
      controllers/data_manage/excel/balance_table.go
  19. 11 0
      controllers/data_manage/excel/custom_analysis.go
  20. 431 10
      controllers/data_manage/excel/excel_info.go
  21. 8 3
      controllers/data_manage/future_good/future_good_chart_info.go
  22. 59 10
      controllers/data_manage/gl_data.go
  23. 1045 0
      controllers/data_manage/gpr_risk_data.go
  24. 22 0
      controllers/data_manage/jiayue_edb_source.go
  25. 6 2
      controllers/data_manage/line_equation/line_chart_info.go
  26. 6 2
      controllers/data_manage/line_feature/chart_info.go
  27. 45 11
      controllers/data_manage/manual_edb.go
  28. 9 0
      controllers/data_manage/predict_edb_info.go
  29. 6 2
      controllers/data_manage/range_analysis/chart_info.go
  30. 50 25
      controllers/data_manage/smm_api.go
  31. 3 0
      controllers/data_manage/smm_data.go
  32. 6 1
      controllers/data_manage/stl/stl.go
  33. 272 0
      controllers/data_source/data_source.go
  34. 19 21
      controllers/data_stat/edb_source_stat.go
  35. 31 0
      controllers/english_report/report.go
  36. 256 0
      controllers/eta_forum/eta_forum.go
  37. 15 6
      controllers/material/material.go
  38. 32 0
      controllers/ppt_english.go
  39. 37 0
      controllers/ppt_v2.go
  40. 133 110
      controllers/report.go
  41. 5 1
      controllers/report_chapter.go
  42. 18 2
      controllers/report_v2.go
  43. 5 3
      controllers/residual_analysis/residual_analysis_controller.go
  44. 280 227
      controllers/resource.go
  45. 4 1
      controllers/sys_department.go
  46. 9 2
      controllers/sys_group.go
  47. 5 2
      controllers/sys_role.go
  48. 93 3
      controllers/trade_analysis/trade_analysis.go
  49. 719 0
      controllers/trade_analysis/trade_analysis_correlation.go
  50. 842 0
      controllers/trade_analysis/trade_analysis_table.go
  51. 8 4
      controllers/trade_analysis/warehouse.go
  52. 14 11
      controllers/voice.go
  53. 2 0
      models/ai_predict_model/ai_predict_model_index.go
  54. 10 1
      models/business_conf.go
  55. 80 0
      models/common/common_classify.go
  56. 1 0
      models/data_manage/base_from_eia_stero.go
  57. 280 0
      models/data_manage/base_from_gpr_risk.go
  58. 149 0
      models/data_manage/base_from_gpr_risk_classify.go
  59. 21 14
      models/data_manage/base_from_mtjh.go
  60. 1 1
      models/data_manage/base_from_smm_classify.go
  61. 1 0
      models/data_manage/chart_edb_mapping.go
  62. 7 0
      models/data_manage/chart_framework.go
  63. 25 3
      models/data_manage/chart_info.go
  64. 9 1
      models/data_manage/data_manage_permission/excel.go
  65. 1 0
      models/data_manage/edb_info.go
  66. 47 3
      models/data_manage/excel/excel_info.go
  67. 135 3
      models/data_manage/excel/referenced_excel_config.go
  68. 25 19
      models/data_manage/excel/request/excel_info.go
  69. 1 0
      models/data_manage/excel/request/mixed_table.go
  70. 47 39
      models/data_manage/excel/response/excel_info.go
  71. 33 7
      models/data_manage/gl_data.go
  72. 1 0
      models/data_manage/my_chart.go
  73. 1 0
      models/data_manage/stl/response/stl.go
  74. 10 0
      models/data_manage/trade_analysis/base_from_trade_exchange.go
  75. 35 0
      models/data_manage/trade_analysis/request/trade_analysis_correlation.go
  76. 37 0
      models/data_manage/trade_analysis/request/trade_analysis_table.go
  77. 17 0
      models/data_manage/trade_analysis/response/trade_analysis_correlation.go
  78. 18 0
      models/data_manage/trade_analysis/response/trade_analysis_table.go
  79. 246 359
      models/data_manage/trade_analysis/trade_analysis.go
  80. 43 0
      models/data_manage/trade_analysis/trade_analysis_correlation.go
  81. 41 0
      models/data_manage/trade_analysis/trade_analysis_table.go
  82. 188 0
      models/data_manage/trade_analysis/trade_analysis_table_column.go
  83. 13 13
      models/data_manage/trade_analysis/warehouse_process_classify.go
  84. 18 16
      models/data_source/base_from_sci99.go
  85. 2119 0
      models/data_source/data_source.go
  86. 2 0
      models/data_source/icpi.go
  87. 14 12
      models/db.go
  88. 15 13
      models/english_report.go
  89. 25 21
      models/manual_edb.go
  90. 7 4
      models/ppt_english/ppt_english.go
  91. 7 4
      models/ppt_v2.go
  92. 10 0
      models/residual_analysis_model/calculate_residual_analysis_config.go
  93. 17 0
      models/residual_analysis_model/edb_data_residual_analysis.go
  94. 9 0
      models/system/sys_group.go
  95. 8 0
      models/system/sys_role.go
  96. 294 33
      routers/commentsRouter.go
  97. 9 0
      routers/router.go
  98. 118 36
      services/binlog/binlog.go
  99. 35 0
      services/binlog/data_source.go
  100. 183 50
      services/binlog/handler.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

+ 65 - 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"
@@ -50,6 +52,9 @@ func (this *AiPredictModelIndexController) List() {
 	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)
 
@@ -79,6 +84,7 @@ func (this *AiPredictModelIndexController) List() {
 	}
 
 	// 筛选条件
+	highlightMap := make(map[int]string)
 	indexOb := new(aiPredictModel.AiPredictModelIndex)
 	var cond string
 	var pars []interface{}
@@ -91,9 +97,34 @@ func (this *AiPredictModelIndexController) List() {
 			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)
 		}
 	}
 
@@ -114,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)
 	}
 
@@ -261,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())
@@ -350,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
@@ -493,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
@@ -570,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 = "操作成功"

+ 78 - 15
controllers/data_manage/bloomberg_data.go

@@ -57,16 +57,70 @@ 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)
+			//}
+			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 +135,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 +189,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 +524,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 != "" {

+ 52 - 25
controllers/data_manage/chart_classify.go

@@ -13,6 +13,12 @@ import (
 	"time"
 )
 
+const (
+	CHART_CLASSIFY_ADD_OR_EDIT = "chartLib:classifyOpt:add"
+	CHART_CLASSIFY_DELETE      = "chartLib:classifyOpt:delete"
+	CHART_CLASSIFY_MOVE        = "chartLib:classifyOpt:move"
+)
+
 // 数据管理-分类模块
 type ChartClassifyController struct {
 	controllers.BaseAuthController
@@ -1104,7 +1110,7 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 			noPermissionChartIdMap[v.ChartInfoId] = true
 		}
 	}
-
+	var allNodes []*data_manage.ChartClassifyItems
 	isShowMe, _ := this.GetBool("IsShowMe")
 	if isShowMe {
 		allChartInfo, err := data_manage.GetChartClassifyAndInfoByParentIdForMe(chartClassifyId, sysUser.AdminId)
@@ -1114,41 +1120,62 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 			return
 		}
 		// 移除没有权限的图表
-		allNodes := data.HandleNoPermissionChart(allChartInfo, noPermissionChartIdMap, this.SysUser.AdminId)
+		allNodes = data.HandleNoPermissionChart(allChartInfo, noPermissionChartIdMap, this.SysUser.AdminId)
 		allNodes, err = data.GetChartClassifyByIsMe(sysUser.AdminId, chartClassifyId, utils.CHART_SOURCE_DEFAULT, allNodes)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取数据失败,Err:" + err.Error()
 			return
 		}
-		resp.AllNodes = allNodes
-
-		br.Ret = 200
-		br.Success = true
-		br.Msg = "获取成功"
-		br.Data = resp
-		fmt.Println("source my classify")
-		return
+		//resp.AllNodes = allNodes
+		//
+		//br.Ret = 200
+		//br.Success = true
+		//br.Msg = "获取成功"
+		//br.Data = resp
+		//fmt.Println("source my classify")
+		//return
+	} else {
+		allChartInfo, err := data_manage.GetChartClassifyAndInfoByParentId(chartClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		//allChartInfo, err := data_manage.GetChartInfoAllByClassifyId(utils.CHART_SOURCE_DEFAULT, chartClassifyId)
+		//if err != nil && err.Error() != utils.ErrNoRow() {
+		//	br.Msg = "获取失败"
+		//	br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		//	return
+		//}
+		// 移除没有权限的图表
+		allNodes = data.HandleNoPermissionChart(allChartInfo, noPermissionChartIdMap, this.SysUser.AdminId)
 	}
-
-	allChartInfo, err := data_manage.GetChartClassifyAndInfoByParentId(chartClassifyId)
-	if err != nil && err.Error() != utils.ErrNoRow() {
+	menulist, e := system.GetMenuButtonsByRoleId(this.SysUser.RoleId)
+	if e != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		br.ErrMsg = "获取角色按钮权限失败, Err: " + e.Error()
 		return
 	}
-
-	//allChartInfo, err := data_manage.GetChartInfoAllByClassifyId(utils.CHART_SOURCE_DEFAULT, chartClassifyId)
-	//if err != nil && err.Error() != utils.ErrNoRow() {
-	//	br.Msg = "获取失败"
-	//	br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
-	//	return
-	//}
-	// 移除没有权限的图表
-	allNodes := data.HandleNoPermissionChart(allChartInfo, noPermissionChartIdMap, this.SysUser.AdminId)
-
+	buttonList := make(map[string]int, 0)
+	for _, item := range menulist {
+		if item.MenuType == 1 {
+			buttonList[item.ButtonCode] = 1
+		}
+	}
+	var addOrEditRight, deleteRight, moveRight bool
+	if _, ok := buttonList[CHART_CLASSIFY_ADD_OR_EDIT]; ok {
+		addOrEditRight = true
+	}
+	if _, ok := buttonList[CHART_CLASSIFY_DELETE]; ok {
+		deleteRight = true
+	}
+	if _, ok := buttonList[CHART_CLASSIFY_MOVE]; ok {
+		moveRight = true
+	}
 	for k, item := range allNodes {
-		item.Button = data.GetChartOpButton(this.SysUser, item.SysUserId, item.HaveOperaAuth)
+		//item.Button = data.GetChartOpButton(this.SysUser, item.SysUserId, item.HaveOperaAuth)
+		item.Button = data.GetChartOpButtonV2(item.HaveOperaAuth, addOrEditRight, deleteRight, moveRight)
 		if item.ChartInfoId > 0 {
 			item.Button.AddButton = false
 			item.Button.OpButton = false

+ 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

+ 88 - 3
controllers/data_manage/chart_info.go

@@ -2,6 +2,7 @@ package data_manage
 
 import (
 	"encoding/json"
+	"errors"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
@@ -10,6 +11,7 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data"
+	"eta/eta_api/services/data/area_graph"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/data/excel"
 	"eta/eta_api/services/eta_forum"
@@ -1374,6 +1376,8 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		extraConfigStr = chartInfo.BarConfig
 	} else if chartInfo != nil && chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
 		extraConfigStr = req.ExtraConfig
+	} else if chartInfo != nil && chartInfo.ChartType == utils.CHART_TYPE_AREA {
+		extraConfigStr = req.ExtraConfig
 	}
 
 	// 获取图表中的指标数据
@@ -1401,6 +1405,16 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
 	}
 
+	// 面积图 面积堆积 数据处理
+	if req.ChartType == utils.CHART_TYPE_AREA {
+		err, errMsg = fillAreaGraphData(extraConfigStr, edbList)
+		if err != nil {
+			br.Msg = "面积图处理失败"
+			br.ErrMsg = errMsg
+			return
+		}
+	}
+
 	//图表操作权限
 	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, true)
 	//判断是否需要展示英文标识
@@ -1493,6 +1507,60 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 	br.Data = resp
 }
 
+func fillAreaGraphData(extraConfigStr string, edbDataList []*data_manage.ChartEdbInfoMapping) (err error, errMsg string) {
+
+	var tmpConfig data_manage.AreaExtraConf
+	if extraConfigStr != `` {
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpConfig)
+		if err != nil {
+			errMsg = "面积图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		if tmpConfig.StandardEdbInfoId <= 0 {
+			utils.FileLog.Info("面积图未开启面积堆积")
+			return
+		}
+	}
+	if tmpConfig.IsHeap == 1 {
+		standardIndexMap := make(map[string]*data_manage.EdbDataList)
+		var startDate, endDate string
+		for _, v := range edbDataList {
+			// 判断是否为基准指标
+			if v.EdbInfoId == tmpConfig.StandardEdbInfoId {
+				if dataList, ok := v.DataList.([]*data_manage.EdbDataList); ok {
+					startDate = dataList[0].DataTime
+					endDate = dataList[len(dataList)-1].DataTime
+					for _, dataObject := range dataList {
+						standardIndexMap[dataObject.DataTime] = dataObject
+					}
+				}
+				break
+			}
+		}
+		strategy, err := area_graph.CreateStrategy(tmpConfig.NullDealWay)
+		if err != nil {
+			return err, "创建空值处理器失败"
+		}
+		err = strategy.Deal(tmpConfig, edbDataList, standardIndexMap, startDate, endDate)
+		if err != nil {
+			return err, err.Error()
+		}
+
+		// 时间戳处理
+		for _, mapping := range edbDataList {
+			if dataList, ok := mapping.DataList.([]*data_manage.EdbDataList); ok {
+				for _, dataInfo := range dataList {
+					toFormatTime := utils.StringToFormatTime(dataInfo.DataTime, utils.FormatDate)
+					dataInfo.DataTimestamp = toFormatTime.UnixMilli()
+				}
+			}
+		}
+	}
+
+	return nil, ""
+}
+
 // ChartInfoDetailV2
 // @Title 获取图表详情
 // @Description 获取图表详情接口
@@ -2227,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
 
@@ -2336,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)
@@ -2346,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)
 		}
 	}
@@ -2822,6 +2893,17 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		chartInfo.ChartClassify = append(chartInfo.ChartClassify, chartClassifyParent)
 	}
 	chartInfo.ChartClassify = append(chartInfo.ChartClassify, chartViewClassify)
+
+	// 面积图 面积堆积 数据处理
+	if chartType == utils.CHART_TYPE_AREA {
+		err, errMsg = fillAreaGraphData(extraConfigStr, edbList)
+		if err != nil {
+			msg = "获取失败"
+			errMsg = "获取面积图数据失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	resp.EdbInfoList = edbList
 	//判断是否需要展示英文标识
 	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList, chartInfo.Source, chartInfo.ChartType)
@@ -3214,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,

+ 3 - 0
controllers/data_manage/chart_theme.go

@@ -207,6 +207,9 @@ func (c *ChartThemeController) GetThemePreviewData() {
 		chartInfo.LeftMax = "4000"
 		extraConfigStr = `{"DateConfList":[],"IsHeap":0,"XDataList":[{"Name":"内销"},{"Name":"出口"},{"Name":"销量"},{"Name":"产量"}],"UnitList":{"LeftName":"万台","RightName":"%","RightTwoName":""},"BaseChartSeriesName":"增量","SortType":0,"SeriesList":[{"ChartSeriesId":0,"SeriesName":"增量","ChartStyle":"column","ChartColor":"rgba(0, 0, 255, 1)","ChartWidth":1,"IsPoint":0,"IsNumber":0,"IsAxis":1,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":19,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":20,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":21,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":22,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}}]},{"ChartSeriesId":0,"SeriesName":"增速","ChartStyle":"spline","ChartColor":"rgba(255, 0, 200, 1)","ChartWidth":1,"IsPoint":0,"IsNumber":0,"IsAxis":0,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":23,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":24,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":25,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}},{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":26,"DateConfName":"","DateConfType":0,"DateConf":{"MoveForward":0,"DateChange":[]}}]}]}`
 		chartInfo.ChartName = "图表标题"
+	case utils.CHART_TYPE_AREA: // 曲线图
+		edbInfoIdList = []int{25, 26}
+		chartInfo.ChartName = "面积图"
 	default:
 		br.Msg = "暂不支持该类型"
 		br.IsSendEmail = false

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

+ 50 - 3
controllers/data_manage/edb_info.go

@@ -3464,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 != "" {
@@ -3475,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 {
@@ -3524,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 {
@@ -3538,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
@@ -4897,6 +4922,9 @@ func (this *EdbInfoController) AllEdbInfoByEs() {
 		}
 	}
 
+	// 只看我的
+	mine, _ := this.GetBool("IsShowMe")
+
 	// 是否走ES
 	isEs := false
 	if keyWord != "" {
@@ -4907,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
@@ -4948,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 {
@@ -4962,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

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

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

+ 431 - 10
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"
@@ -121,6 +123,18 @@ func (c *ExcelInfoController) Add() {
 		req.ExcelClassifyId = parentExcelInfo.ExcelClassifyId
 	}
 
+	// 额外配置(表格冻结行列等)
+	var extraConfig string
+	if req.ExtraConfig != nil {
+		b, e := json.Marshal(req.ExtraConfig)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("操作失败, %v", e)
+			return
+		}
+		extraConfig = string(b)
+	}
+
 	var condition string
 	var pars []interface{}
 	condition += " AND excel_classify_id=? AND parent_id=?"
@@ -257,6 +271,7 @@ func (c *ExcelInfoController) Add() {
 		UpdateUserId:       sysUser.AdminId,
 		UpdateUserRealName: sysUser.RealName,
 		SourcesFrom:        req.SourcesFrom,
+		ExtraConfig:        extraConfig,
 	}
 
 	excelEdbMappingList := make([]*excel3.ExcelEdbMapping, 0)
@@ -293,6 +308,7 @@ func (c *ExcelInfoController) Add() {
 			//ParentId:           req.ParentId,
 			UpdateUserId:       sysUser.AdminId,
 			UpdateUserRealName: sysUser.RealName,
+			ExtraConfig:        extraConfig,
 		}
 	}
 	err = excel3.AddExcelInfo(excelInfo, excelEdbMappingList, childExcel)
@@ -336,6 +352,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 +638,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)
+			}
 		}
 
 	}
@@ -1042,6 +1071,18 @@ func (c *ExcelInfoController) Edit() {
 		updateExcelInfoParams = []string{"ModifyTime", "ExcelName", "ExcelType", "ExcelClassifyId", "Content", "SourcesFrom"}
 	}
 
+	// 额外配置(表格冻结行列等)
+	if req.ExtraConfig != nil {
+		b, e := json.Marshal(req.ExtraConfig)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("操作失败, %v", e)
+			return
+		}
+		excelInfo.ExtraConfig = string(b)
+		updateExcelInfoParams = append(updateExcelInfoParams, "ExtraConfig")
+	}
+
 	excelEdbMappingList := make([]*excel3.ExcelEdbMapping, 0)
 	if len(edbInfoIdList) > 0 {
 		for _, edbInfoId := range edbInfoIdList {
@@ -1091,6 +1132,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 +1151,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 +1477,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 +1617,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 == `` {
@@ -1687,17 +1750,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 +3442,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
+}

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

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

+ 59 - 10
controllers/data_manage/gl_data.go

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

+ 1045 - 0
controllers/data_manage/gpr_risk_data.go

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

+ 22 - 0
controllers/data_manage/jiayue_edb_source.go

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

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

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

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

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

+ 45 - 11
controllers/data_manage/manual_edb.go

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

+ 9 - 0
controllers/data_manage/predict_edb_info.go

@@ -1253,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

+ 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"
@@ -142,6 +143,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
@@ -200,6 +227,7 @@ func (this *EnglishReportController) Edit() {
 	}
 	var contentSub string
 	if req.Content != "" {
+		req.Content = services.HandleReportContentTable(int(req.ReportId), req.Content)
 		req.Content = services.HandleReportContent(req.Content, "del", nil)
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
@@ -323,6 +351,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)
 
 	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
 	if err != nil {
@@ -1095,6 +1125,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
+}

+ 15 - 6
controllers/material/material.go

@@ -10,7 +10,6 @@ import (
 	materialService "eta/eta_api/services/material"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/h2non/filetype"
 	"github.com/rdlucklib/rdluck_tools/http"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"io/ioutil"
@@ -1368,13 +1367,23 @@ func (this *MaterialController) 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)
 	randStr := utils.GetRandStringNoSpecialChar(28)
 	fileName := randStr + ext
 

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

+ 5 - 1
controllers/report_chapter.go

@@ -364,6 +364,8 @@ func (this *ReportController) EditDayWeekChapter() {
 	// 更新章节及指标
 	contentSub := ""
 	if req.Content != "" {
+		// 处理关联excel的表格id
+		req.Content = services.HandleReportContentTable(reportInfo.Id, req.Content)
 		req.Content = services.HandleReportContent(req.Content, "del", nil)
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
@@ -405,8 +407,8 @@ 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)
 		req.ContentStruct = services.HandleReportContentStruct(req.ContentStruct, "del", nil)
 	}
 	reportChapterInfo.ContentStruct = html.EscapeString(req.ContentStruct)
@@ -780,6 +782,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)
 
 	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
 	if err != nil {

+ 18 - 2
controllers/report_v2.go

@@ -284,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]
@@ -589,8 +595,11 @@ func (this *ReportController) Edit() {
 		return
 	}
 
-	req.Content = services.HandleReportContent(req.Content, "del", nil)
+	req.Content = services.HandleReportContentTable(int(req.ReportId), req.Content)
+	req.ContentStruct = services.HandleReportContentStructTable(int(req.ReportId), req.ContentStruct)
+    req.Content = services.HandleReportContent(req.Content, "del", nil)
 	req.ContentStruct = services.HandleReportContentStruct(req.ContentStruct, "del", nil)
+	
 	// 编辑报告信息
 	err, errMsg := services.EditReport(reportInfo, req, sysUser)
 	if err != nil {
@@ -702,7 +711,14 @@ func (this *ReportController) Detail() {
 		item.EndStyle = endResource.Style
 	}
 
-	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
+	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)
+	}
+
+    businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取配置失败,Err:" + err.Error()

+ 5 - 3
controllers/residual_analysis/residual_analysis_controller.go

@@ -81,15 +81,17 @@ func (this *ResidualAnalysisController) ContrastPreview() {
 		return
 	}
 
-	IndexCode := this.GetString("IndexCode")
-	if IndexCode == "" {
+	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)
+	resp, err := residual_analysis_service.ContrastPreview(indexCode, startTime, endTime)
 	if err != nil {
 		return
 	}

+ 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

+ 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

+ 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

+ 10 - 1
models/business_conf.go

@@ -55,7 +55,9 @@ const (
 
 	BusinessConfEdbStopRefreshRule = "EdbStopRefreshRule" // 是否停止指标刷新规则
 	BusinessConfReport2ImgUrl      = "Report2ImgUrl"      // 报告转长图地址(用于兼容内外网环境的)
-	BusinessConfReportViewUrl      = "ReportViewUrl"      // 报告详情地址
+	BusinessConfReportViewUrl      = "ReportViewUrl"      // 报告详情地址     // 报告详情地址
+	BusinessConfEsIndexNameExcel             = "EsIndexNameExcel"             // ES索引名称-表格
+	BusinessConfEsIndexNameDataSource        = "EsIndexNameDataSource"   
 	BusinessConfIsOpenChartExpired = "IsOpenChartExpired" // 图表是否鉴权
 )
 
@@ -267,4 +269,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
+//}

+ 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:"子级分类列表"`
 }
 

+ 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:"下级"`

+ 1 - 0
models/data_manage/chart_edb_mapping.go

@@ -28,6 +28,7 @@ type ChartEdbMapping struct {
 	ChartColor        string    `description:"颜色"`
 	PredictChartColor string    `description:"预测数据的颜色"`
 	ChartWidth        float64   `description:"线条大小"`
+	ChartScale        float64   `description:"参考刻度线"`
 	Source            int       `description:"1:ETA图库;2:商品价格曲线"`
 	EdbAliasName      string    `description:"中文别名"`
 	IsConvert         int       `description:"是否数据转换 0不转 1转"`

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

+ 25 - 3
models/data_manage/chart_info.go

@@ -63,10 +63,19 @@ type ChartInfo struct {
 	DateTypeNum       int    `description:"date_type=25(N月前)时的N值,其他N值可复用此字段"`
 }
 
+// AreaExtraConf 面积图配置
+type AreaExtraConf struct {
+	IsHeap            int `description:"是否堆积 1-堆积 2-不堆积"`
+	HeapWay           int `description:"堆积方式 1-普通 2-百分比"`
+	StandardEdbInfoId int `description:"基准指标id"`
+	NullDealWay       int `description:"空值处理方式,1-插值填充 2-前值填充 3-后值填充 4-等于0 5-删除日期"`
+}
+
 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) {
@@ -196,6 +205,7 @@ type ChartSaveItem struct {
 	ChartColor        string  `description:"颜色"`
 	PredictChartColor string  `description:"预测数据的颜色"`
 	ChartWidth        float64 `description:"线条大小"`
+	ChartScale        float64 `description:"参考刻度线"`
 	Source            int     `description:"1:ETA图库;2:商品价格曲线"`
 	EdbAliasName      string  `description:"中文别名"`
 	IsConvert         int     `description:"是否数据转换 0不转 1转"`
@@ -716,6 +726,7 @@ type ChartEdbInfoMapping struct {
 	ChartColor          string  `description:"颜色"`
 	PredictChartColor   string  `description:"预测数据的颜色"`
 	ChartWidth          float64 `description:"线条大小"`
+	ChartScale          float64 `description:"参考刻度线"`
 	ChartType           int     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图"`
 	LatestDate          string  `description:"数据最新日期"`
 	LatestValue         float64 `description:"数据最新值"`
@@ -1723,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")
 
@@ -2924,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
+}

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

+ 1 - 0
models/data_manage/edb_info.go

@@ -473,6 +473,7 @@ type EdbInfoList struct {
 	MoveFrequency    string                  `description:"移动频度"`
 	MinValue         float64                 `description:"最小值"`
 	MaxValue         float64                 `description:"最大值"`
+	SearchText       string                  `description:"搜索结果(含高亮)"`
 }
 
 type EdbDataInsertConfigItem struct {

+ 47 - 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,45 @@ 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
+}
+
+// ExcelCommonExtraConfig 表格额外配置
+type ExcelCommonExtraConfig struct {
+	TableFreeze ExcelInfoFreeze `description:"表格冻结"`
+}
+
+// ExcelInfoFreeze 表格冻结
+type ExcelInfoFreeze struct {
+	FreezeFirstRow bool `description:"冻结首行"`
+	FreezeFirstCol bool `description:"冻结首列"`
+	FreezeStartRow int  `description:"冻结开始行"`
+	FreezeEndRow   int  `description:"冻结结束行"`
+	FreezeStartCol int  `description:"冻结开始列"`
+	FreezeEndCol   int  `description:"冻结结束列"`
+}

+ 135 - 3
models/data_manage/excel/referenced_excel_config.go

@@ -1,6 +1,7 @@
 package excel
 
 import (
+	"eta/eta_api/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"time"
 )
@@ -8,12 +9,12 @@ import (
 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
+	ReferencedId            int       // 被引用的id,报告就是报告id,pptId
+	FromScene               int       // 引用类型 1智能研报 2研报列表 3英文研报 4PPT 5英文ppt
 	Uuid                    string    // 引用唯一标识
 	WidthList               string    // 宽度数组
 	HeightList              string    // 高度数组
-	OpUserID                int       // 当前编辑操作的用户id
+	OpUserId                int       // 当前编辑操作的用户id
 	OpUserName              string    // 当前编辑的用户名称(冗余字段,避免查表)
 	CreateTime              time.Time // 创建时间
 	Content                 string    // 内容
@@ -43,3 +44,134 @@ func GetReferencedExcelConfigByUniqueCode(uniqueCode string) (item ReferencedExc
 	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
+}

+ 25 - 19
models/data_manage/excel/request/excel_info.go

@@ -1,6 +1,9 @@
 package request
 
-import "encoding/json"
+import (
+	"encoding/json"
+	"eta/eta_api/models/data_manage/excel"
+)
 
 // MoveExcelInfoReq 移动excel表格请求
 type MoveExcelInfoReq struct {
@@ -17,28 +20,31 @@ type DeleteExcelInfoReq struct {
 
 // AddExcelInfoReq 新增表格请求
 type AddExcelInfoReq struct {
-	ExcelInfoId     int         `description:"表格ID"`
-	ExcelName       string      `description:"表格名称"`
-	Source          int         `description:"表格来源,1:excel插件的表格,2:自定义表格,5平衡表,默认:1"`
-	ExcelType       int         `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelImage      string      `description:"表格截图"`
-	ExcelClassifyId int         `description:"分类id"`
-	Content         string      `description:"Excel表格内容"`
-	TableData       interface{} `description:"自定义表格的数据内容"`
-	ParentId        int         `description:"表格的父级id"`
-	SourcesFrom     string      `description:"图表来源"`
+	ExcelInfoId     int                           `description:"表格ID"`
+	ExcelName       string                        `description:"表格名称"`
+	Source          int                           `description:"表格来源,1:excel插件的表格,2:自定义表格,5平衡表,默认:1"`
+	ExcelType       int                           `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelImage      string                        `description:"表格截图"`
+	ExcelClassifyId int                           `description:"分类id"`
+	Content         string                        `description:"Excel表格内容"`
+	TableData       interface{}                   `description:"自定义表格的数据内容"`
+	ParentId        int                           `description:"表格的父级id"`
+	SourcesFrom     string                        `description:"图表来源"`
+	ExtraConfig     *excel.ExcelCommonExtraConfig `description:"表格额外配置"`
 }
 
 // EditExcelInfoReq 编辑表格请求
 type EditExcelInfoReq struct {
-	ExcelInfoId     int         `description:"ETA表格ID"`
-	ExcelType       int         `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelName       string      `description:"表格名称"`
-	ExcelImage      string      `description:"表格截图"`
-	ExcelClassifyId int         `description:"分类id"`
-	Content         string      `description:"Excel表格内容"`
-	TableData       interface{} `description:"自定义表格的数据内容"`
-	SourcesFrom     string      `description:"图表来源"`
+	ExcelInfoId     int                           `description:"ETA表格ID"`
+	ExcelType       int                           `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName       string                        `description:"表格名称"`
+	ExcelImage      string                        `description:"表格截图"`
+	ExcelClassifyId int                           `description:"分类id"`
+	Content         string                        `description:"Excel表格内容"`
+	TableData       interface{}                   `description:"自定义表格的数据内容"`
+	SourcesFrom     string                        `description:"图表来源"`
+	IsColChange     bool                          `description:"是否修改过行列,true时清空引用"`
+	ExtraConfig     *excel.ExcelCommonExtraConfig `description:"表格额外配置"`
 }
 
 // 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 {

+ 47 - 39
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
@@ -65,35 +66,36 @@ type TableDetailResp struct {
 
 // ExcelInfoDetail excel表格详情(前端使用)
 type ExcelInfoDetail struct {
-	ExcelInfoId        int                          `orm:"column(excel_info_id);pk"`
-	Source             int                          `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
-	ExcelType          int                          `description:"表格类型,1:指标列,2:日期列,默认:1"`
-	ExcelName          string                       `description:"表格名称"`
-	UniqueCode         string                       `description:"表格唯一编码"`
-	ExcelClassifyId    int                          `description:"表格分类id"`
-	SysUserId          int                          `description:"操作人id"`
-	SysUserRealName    string                       `description:"操作人真实姓名"`
-	Content            string                       `description:"表格内容"`
-	ExcelImage         string                       `description:"表格图片"`
-	FileUrl            string                       `description:"表格下载地址"`
-	Sort               int                          `description:"排序字段,数字越小越排前面"`
-	IsDelete           int                          `description:"是否删除,0:未删除,1:已删除"`
-	ModifyTime         time.Time                    `description:"最近修改日期"`
-	CreateTime         time.Time                    `description:"创建日期"`
-	TableData          interface{}                  `description:"表格内容"`
-	Button             excel2.ExcelInfoDetailButton `description:"操作权限"`
-	CanEdit            bool                         `description:"是否可编辑"`
-	Editor             string                       `description:"编辑人"`
-	IsJoinPermission   int                          `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
-	HaveOperaAuth      bool                         `description:"是否有数据权限"`
-	ParentId           int                          `description:"表格的父级id"`
-	BalanceType        int                          `description:"平衡表类型:0 动态表,1静态表"`
-	UpdateUserId       int                          `description:"更新人id"`
-	UpdateUserRealName string                       `description:"更新人真实姓名"`
-	RelExcelInfoId     int                          `description:"平衡表里静态表关联的动态表excel id"`
-	SourcesFrom        string                       `description:"图表来源"`
-	ExcelSource        string                       `description:"表格来源str"`
-	ExcelSourceEn      string                       `description:"表格来源(英文)"`
+	ExcelInfoId        int                           `orm:"column(excel_info_id);pk"`
+	Source             int                           `description:"表格来源,1:excel插件的表格,2:自定义表格,默认:1"`
+	ExcelType          int                           `description:"表格类型,1:指标列,2:日期列,默认:1"`
+	ExcelName          string                        `description:"表格名称"`
+	UniqueCode         string                        `description:"表格唯一编码"`
+	ExcelClassifyId    int                           `description:"表格分类id"`
+	SysUserId          int                           `description:"操作人id"`
+	SysUserRealName    string                        `description:"操作人真实姓名"`
+	Content            string                        `description:"表格内容"`
+	ExcelImage         string                        `description:"表格图片"`
+	FileUrl            string                        `description:"表格下载地址"`
+	Sort               int                           `description:"排序字段,数字越小越排前面"`
+	IsDelete           int                           `description:"是否删除,0:未删除,1:已删除"`
+	ModifyTime         time.Time                     `description:"最近修改日期"`
+	CreateTime         time.Time                     `description:"创建日期"`
+	TableData          interface{}                   `description:"表格内容"`
+	Button             excel2.ExcelInfoDetailButton  `description:"操作权限"`
+	CanEdit            bool                          `description:"是否可编辑"`
+	Editor             string                        `description:"编辑人"`
+	IsJoinPermission   int                           `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth      bool                          `description:"是否有数据权限"`
+	ParentId           int                           `description:"表格的父级id"`
+	BalanceType        int                           `description:"平衡表类型:0 动态表,1静态表"`
+	UpdateUserId       int                           `description:"更新人id"`
+	UpdateUserRealName string                        `description:"更新人真实姓名"`
+	RelExcelInfoId     int                           `description:"平衡表里静态表关联的动态表excel id"`
+	SourcesFrom        string                        `description:"图表来源"`
+	ExcelSource        string                        `description:"表格来源str"`
+	ExcelSourceEn      string                        `description:"表格来源(英文)"`
+	ExtraConfig        excel2.ExcelCommonExtraConfig `description:"表格额外配置"`
 }
 
 type BalanceChildTableResp struct {
@@ -126,3 +128,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 {

+ 1 - 0
models/data_manage/stl/response/stl.go

@@ -5,6 +5,7 @@ type StlPreviewResp struct {
 	TrendChartInfo    ChartEdbInfo
 	SeasonalChartInfo ChartEdbInfo
 	ResidualChartInfo ChartEdbInfo
+	NonTrendChartInfo ChartEdbInfo
 	EvaluationResult  EvaluationResult
 }
 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 18 - 16
models/data_source/base_from_sci99.go

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

+ 2119 - 0
models/data_source/data_source.go

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

+ 2 - 0
models/data_source/icpi.go

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

+ 14 - 12
models/db.go

@@ -16,6 +16,7 @@ import (
 	future_good2 "eta/eta_api/models/data_manage/future_good"
 	"eta/eta_api/models/data_manage/stl"
 	"eta/eta_api/models/data_manage/supply_analysis"
+	tradeAnalysisModel "eta/eta_api/models/data_manage/trade_analysis"
 	"eta/eta_api/models/data_stat"
 	edbmonitor "eta/eta_api/models/edb_monitor"
 	"eta/eta_api/models/eta_trial"
@@ -30,7 +31,6 @@ import (
 	"eta/eta_api/models/speech_recognition"
 	"eta/eta_api/models/system"
 	"eta/eta_api/models/yb"
-	binlogSvr "eta/eta_api/services/binlog"
 	"eta/eta_api/utils"
 	"time"
 
@@ -223,7 +223,7 @@ func init() {
 	// 开启mysql binlog监听
 	if utils.MYSQL_DATA_BINLOG_URL != "" {
 		initBinlog()
-		go binlogSvr.ListenMysql()
+		//go binlogSvr.ListenMysql()
 	}
 
 	// 初始化部分数据表变量(直接init会有顺序问题=_=!)
@@ -537,16 +537,18 @@ func initChartFramework() {
 // initExcel 初始化EXCEL
 func initExcel() {
 	orm.RegisterModel(
-		new(excel.ExcelClassify),        //ETA excel表格分类
-		new(excel.ExcelInfo),            //ETA excel表格
-		new(excel.ExcelDraft),           //ETA excel表格草稿
-		new(excel.ExcelSheet),           //ETA excel sheet
-		new(excel.ExcelSheetData),       //ETA excel sheet data
-		new(excel.ExcelEdbMapping),      //ETA excel 与 指标 的关系表
-		new(excel.ExcelWorker),          // 平衡表协作人表格
-		new(excel.ExcelChartEdb),        // 平衡表做图指标
-		new(excel.ExcelChartData),       // 平衡表作图数据
-		new(excel.ExcelInfoRuleMapping), //表格的管理规则
+		new(excel.ExcelClassify),         //ETA excel表格分类
+		new(excel.ExcelInfo),             //ETA excel表格
+		new(excel.ExcelDraft),            //ETA excel表格草稿
+		new(excel.ExcelSheet),            //ETA excel sheet
+		new(excel.ExcelSheetData),        //ETA excel sheet data
+		new(excel.ExcelEdbMapping),       //ETA excel 与 指标 的关系表
+		new(excel.ExcelWorker),           // 平衡表协作人表格
+		new(excel.ExcelChartEdb),         // 平衡表做图指标
+		new(excel.ExcelChartData),        // 平衡表作图数据
+		new(excel.ExcelInfoRuleMapping),  //表格的管理规则
+		new(tradeAnalysisModel.TradeAnalysisTableColumn), // 持仓分析表格-自定义列
+		new(excel.ReferencedExcelConfig), //表格的样式引用
 	)
 }
 

+ 15 - 13
models/english_report.go

@@ -2,6 +2,7 @@ package models
 
 import (
 	"errors"
+	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/utils"
 	"fmt"
 	"strings"
@@ -91,19 +92,20 @@ func ModifyEnglishReportCode(reportId int64, reportCode string) (err error) {
 }
 
 type AddEnglishReportReq struct {
-	AddType            int    `description:"新增方式:1:新增报告,2:继承报告"`
-	ClassifyIdFirst    int    `description:"一级分类id"`
-	ClassifyNameFirst  string `description:"一级分类名称"`
-	ClassifyIdSecond   int    `description:"二级分类id"`
-	ClassifyNameSecond string `description:"二级分类名称"`
-	Title              string `description:"标题"`
-	Abstract           string `description:"摘要"`
-	Author             string `description:"作者"`
-	Frequency          string `description:"频度"`
-	State              int    `description:"状态:1:未发布,2:已发布"`
-	Content            string `description:"内容"`
-	CreateTime         string `description:"创建时间"`
-	Overview           string `description:"英文概述部分"`
+	AddType            int                        `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int                        `description:"一级分类id"`
+	ClassifyNameFirst  string                     `description:"一级分类名称"`
+	ClassifyIdSecond   int                        `description:"二级分类id"`
+	ClassifyNameSecond string                     `description:"二级分类名称"`
+	Title              string                     `description:"标题"`
+	Abstract           string                     `description:"摘要"`
+	Author             string                     `description:"作者"`
+	Frequency          string                     `description:"频度"`
+	State              int                        `description:"状态:1:未发布,2:已发布"`
+	Content            string                     `description:"内容"`
+	CreateTime         string                     `description:"创建时间"`
+	Overview           string                     `description:"英文概述部分"`
+	ExcelReferences    []excel.ExcelReferencesReq `description:"引用的Excel信息"`
 }
 
 type AddEnglishReportResp struct {

+ 25 - 21
models/manual_edb.go

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

+ 7 - 4
models/ppt_english/ppt_english.go

@@ -1,6 +1,7 @@
 package ppt_english
 
 import (
+	"eta/eta_api/models/data_manage/excel"
 	"time"
 
 	"github.com/beego/beego/v2/client/orm"
@@ -119,10 +120,12 @@ type AddPptEnglishReq struct {
 		BackIndex    int    `description:"背景图片下标"`
 		TemplateType int    `description:"模版id"`
 	} `description:"首页"`
-	Content      string `description:"ppt的json数据"`
-	GroupId      int64  `description:"目录id"`
-	CoverContent string `description:"PPT内容-JSON"`
-	TitleSetting string `description:"PPT标题设置"`
+	Content         string                     `description:"ppt的json数据"`
+	GroupId         int64                      `description:"目录id"`
+	CoverContent    string                     `description:"PPT内容-JSON"`
+	TitleSetting    string                     `description:"PPT标题设置"`
+	ExcelReferences []excel.ExcelReferencesReq `description:"引用的Excel信息"`
+	MergePptIdList  []int                      `description:"合并的ppt的Id列表"`
 }
 
 type AddPptEnglishResp struct {

+ 7 - 4
models/ppt_v2.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/ppt_english"
 	"time"
 
@@ -124,10 +125,12 @@ type AddPptV2Req struct {
 		BackIndex    int    `description:"背景图片下标"`
 		TemplateType int    `description:"模版id"`
 	} `description:"首页"`
-	Content      string `description:"ppt的json数据"`
-	GroupId      int64  `description:"目录id"`
-	CoverContent string `description:"封面图内容-JSON数据"`
-	TitleSetting string `description:"PPT标题设置"`
+	Content         string                     `description:"ppt的json数据"`
+	GroupId         int64                      `description:"目录id"`
+	CoverContent    string                     `description:"封面图内容-JSON数据"`
+	TitleSetting    string                     `description:"PPT标题设置"`
+	ExcelReferences []excel.ExcelReferencesReq `description:"引用的Excel信息"`
+	MergePptIdList  []int                      `description:"合并的ppt的Id列表"`
 }
 
 type AddPptResp struct {

+ 10 - 0
models/residual_analysis_model/calculate_residual_analysis_config.go

@@ -112,6 +112,15 @@ type ResidualAnalysisChartEdbInfoMapping struct {
 	MinValue            float64 `description:"最小值"`
 	MaxValue            float64 `description:"最大值"`
 	DataList            interface{}
+
+	LeftIndexMin     float64 `description:"指标A左侧下限"`
+	LeftIndexMax     float64 `description:"指标A左侧上限"`
+	RightIndexMin    float64 `description:"指标B右侧下限"`
+	RightIndexMax    float64 `description:"指标B右侧上限"`
+	ResidualIndexMin float64 `description:"残差指标下限"`
+	ResidualIndexMax float64 `description:"残差指标上限"`
+	ContrastIndexMin float64 `description:"对比指标下限"`
+	ContrastIndexMax float64 `description:"对比指标上限"`
 }
 
 type ResidualAnalysisIndexSaveReq struct {
@@ -136,6 +145,7 @@ type ResidualAnalysisIndexSaveReq struct {
 
 // ResidualAnalysisConfigVo 残差分析配置vo
 type ResidualAnalysisConfigVo struct {
+	ResidualType     int     `description:"残差类型: 1-映射残差 2-拟合残差"`
 	DateType         int     `description:"时间类型 -1-自定义时间 0-至今 n-枚举时间(近n年)"`
 	StartDate        string  `description:"自定义开始日期"`
 	EndDate          string  `description:"自定义结束日期"`

+ 17 - 0
models/residual_analysis_model/edb_data_residual_analysis.go

@@ -38,3 +38,20 @@ func AddResidualAnalysisData(dataList []edbDataResidualAnalysis) (num int64, err
 
 	return num, nil
 }
+
+// GetDataListByCondition 根据条件查询所有数据
+func GetDataListByCondition(condition string, pars []interface{}) (items []edbDataResidualAnalysis, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `select * from edb_data_residual_analysis where 1=1` + condition
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return items, err
+}
+
+// RefreshDataTimestamp 刷新残差指标时间戳
+func (m *edbDataResidualAnalysis) RefreshDataTimestamp(cols []string) {
+	o := orm.NewOrmUsingDB("data")
+	_, err := o.Update(m, cols...)
+	if err != nil {
+		return
+	}
+}

+ 9 - 0
models/system/sys_group.go

@@ -1,6 +1,7 @@
 package system
 
 import (
+	"eta/eta_api/utils"
 	"github.com/beego/beego/v2/client/orm"
 	"time"
 )
@@ -130,6 +131,14 @@ func GetSysGroupByGroupId(groupId int) (items []*SysGroupList, err error) {
 	return
 }
 
+// GetSysGroupByGroupIds 销售主管用,查找销售主管所在大组的名称
+func GetSysGroupByGroupIds(groupIds []int) (items []*SysGroup, err error) {
+	sql := `SELECT * FROM sys_group WHERE group_id in (` + utils.GetOrmInReplace(len(groupIds)) + `) ORDER BY sort ASC, create_time ASC`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, groupIds).QueryRows(&items)
+	return
+}
+
 // GetChildSysGroupByGroupId 通过上级分组id获取下级的分组id
 func GetChildSysGroupByGroupId(groupId int) (items []*SysGroup, err error) {
 	sql := `SELECT * FROM sys_group WHERE parent_id=? ORDER BY sort ASC, create_time ASC`

+ 8 - 0
models/system/sys_role.go

@@ -18,6 +18,7 @@ type SysRole struct {
 	RoleName     string    `description:"角色名称"`
 	RoleType     string    `description:"角色类型"`
 	RoleTypeCode string    `description:"角色类型编码"`
+	RoleLevel    int       `description:"角色等级:0-表示一级角色,每个账号只能绑定一个一级角色 ,1-表示二级角色,每个账号可以绑定多个二级 角色"`
 	CreateTime   time.Time `description:"创建时间"`
 	ModifyTime   time.Time
 }
@@ -57,6 +58,13 @@ func GetSysRoleById(roleId int) (item *SysRoleItem, err error) {
 	return
 }
 
+func GetRoleInfoById(roleId int) (item *SysRole, err error) {
+	sql := `SELECT * FROM sys_role WHERE role_id=? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, roleId).QueryRow(&item)
+	return
+}
+
 func GetSysRoleByName(roleName string) (item *SysRoleItem, err error) {
 	sql := `SELECT * FROM sys_role WHERE role_name=? `
 	o := orm.NewOrm()

+ 294 - 33
routers/commentsRouter.go

@@ -1438,6 +1438,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "SaveExcelReference",
+            Router: `/excel_info/reference/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "Rename",
@@ -1492,6 +1501,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
+        beego.ControllerComments{
+            Method: "SearchByEs",
+            Router: `/excel_info/search_by_es`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:ExcelInfoController"],
         beego.ControllerComments{
             Method: "GetBatchEdbData",
@@ -2716,6 +2734,96 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskClassify",
+            Router: `/gpr_risk/classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskIndexList",
+            Router: `/gpr_risk/classify/index/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskEdbInfoAdd",
+            Router: `/gpr_risk/edb_info/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskAddCheck",
+            Router: `/gpr_risk/edb_info/add_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskNameCheck",
+            Router: `/gpr_risk/edb_info/name_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "ExportGprRiskList",
+            Router: `/gpr_risk/export`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GetFrequency",
+            Router: `/gpr_risk/frequency`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskIndexData",
+            Router: `/gpr_risk/index/data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskSearchList",
+            Router: `/gpr_risk/search_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromGprRiskController"],
+        beego.ControllerComments{
+            Method: "GprRiskSingleData",
+            Router: `/gpr_risk/single_data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromLyClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromLyClassifyController"],
         beego.ControllerComments{
             Method: "LyClassifyList",
@@ -6973,6 +7081,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
+        beego.ControllerComments{
+            Method: "SearchByEs",
+            Router: `/common/search_by_es`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
         beego.ControllerComments{
             Method: "GfexClassifyList",
@@ -8134,6 +8251,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "CommonChartInfoDetailFromUniqueCode",
+            Router: `/chart/from_unique_code`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "UserChartList",
+            Router: `/chart_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "UserCollectChartList",
+            Router: `/collect/chart`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_forum:EtaForumController"],
+        beego.ControllerComments{
+            Method: "UserCollectChartClassifyList",
+            Router: `/collect/chart_classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"],
         beego.ControllerComments{
             Method: "QuestionnaireCommit",
@@ -9484,6 +9637,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisController"],
+        beego.ControllerComments{
+            Method: "GetTradeExchangeClassifyTree",
+            Router: `/exchange_classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisController"],
         beego.ControllerComments{
             Method: "GetTradeExchangeList",
@@ -9502,6 +9664,132 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/correlation/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"],
+        beego.ControllerComments{
+            Method: "Download",
+            Router: `/correlation/download`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"],
+        beego.ControllerComments{
+            Method: "Preview",
+            Router: `/correlation/preview`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/correlation/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/correlation/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"],
+        beego.ControllerComments{
+            Method: "Save",
+            Router: `/correlation/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisCorrelationController"],
+        beego.ControllerComments{
+            Method: "SaveAs",
+            Router: `/correlation/save_as`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/table/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"],
+        beego.ControllerComments{
+            Method: "Download",
+            Router: `/table/download`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"],
+        beego.ControllerComments{
+            Method: "Preview",
+            Router: `/table/preview`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/table/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"],
+        beego.ControllerComments{
+            Method: "Remove",
+            Router: `/table/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"],
+        beego.ControllerComments{
+            Method: "Save",
+            Router: `/table/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisTableController"],
+        beego.ControllerComments{
+            Method: "SaveAs",
+            Router: `/table/save_as`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:WarehouseClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:WarehouseClassifyController"],
         beego.ControllerComments{
             Method: "AddChartClassify",
@@ -11419,15 +11707,6 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
-        beego.ControllerComments{
-            Method: "Upload",
-            Router: `/upload`,
-            AllowHTTPMethods: []string{"post"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
     beego.GlobalControllerRouter["eta/eta_api/controllers:ReportUploadCommonController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportUploadCommonController"],
         beego.ControllerComments{
             Method: "UploadImg",
@@ -11464,42 +11743,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"],
-        beego.ControllerComments{
-            Method: "FileUpload",
-            Router: `/file/upload`,
-            AllowHTTPMethods: []string{"post"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
-    beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"],
+    beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceAuthController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceAuthController"],
         beego.ControllerComments{
-            Method: "Upload",
-            Router: `/image/upload`,
-            AllowHTTPMethods: []string{"post"},
+            Method: "OssSTSToken",
+            Router: `/oss/get_sts_token`,
+            AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
     beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"],
         beego.ControllerComments{
-            Method: "UploadV2",
-            Router: `/image/uploadV2`,
+            Method: "FileUpload",
+            Router: `/file/upload`,
             AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"],
-        beego.ControllerComments{
-            Method: "OssSTSToken",
-            Router: `/oss/get_sts_token`,
-            AllowHTTPMethods: []string{"get"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
     beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"],
         beego.ControllerComments{
             Method: "UploadImageBase64",

+ 9 - 0
routers/router.go

@@ -27,6 +27,7 @@ import (
 	"eta/eta_api/controllers/document_manage"
 	"eta/eta_api/controllers/edb_monitor"
 	"eta/eta_api/controllers/english_report"
+	"eta/eta_api/controllers/eta_forum"
 	"eta/eta_api/controllers/eta_trial"
 	"eta/eta_api/controllers/fe_calendar"
 	"eta/eta_api/controllers/material"
@@ -187,6 +188,7 @@ func init() {
 				&data_manage.BaseFromUsdaFasController{},
 				&data_manage.BaseFromRzdIndexController{},
 				&data_manage.ClarksonsDataController{},
+				&data_manage.BaseFromGprRiskController{},
 			),
 		),
 		web.NSNamespace("/my_chart",
@@ -299,6 +301,8 @@ func init() {
 				&trade_analysis.TradeAnalysisController{},
 				&trade_analysis.WarehouseClassifyController{},
 				&trade_analysis.WarehouseController{},
+				&trade_analysis.TradeAnalysisTableController{},
+				&trade_analysis.TradeAnalysisCorrelationController{},
 			),
 		),
 		web.NSNamespace("/custom_analysis",
@@ -438,6 +442,11 @@ func init() {
 				&residual_analysis.ResidualAnalysisController{},
 			),
 		),
+		web.NSNamespace("/eta_forum",
+			web.NSInclude(
+				&eta_forum.EtaForumController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 118 - 36
services/binlog/binlog.go

@@ -17,29 +17,62 @@ func ListenMysql() {
 	var err error
 	defer func() {
 		if err != nil {
-			fmt.Println("数据库监听服务异常,err:", err)
+			tips := fmt.Sprintf("ListenMysql-数据库监听服务异常, %v", err)
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
 		}
+		utils.FileLog.Info("ListenMysql end")
 	}()
 	if utils.MYSQL_DATA_BINLOG_URL == "" {
-		panic("mysql url is empty")
+		err = fmt.Errorf("mysql url is empty")
+		return
 	}
-
 	if utils.MYSQL_DATA_BINLOG_USER == "" {
-		panic("mysql user is empty")
+		err = fmt.Errorf("mysql user is empty")
+		return
 	}
 	if utils.MYSQL_DATA_BINLOG_PWD == "" {
-		panic("mysql password is empty")
+		err = fmt.Errorf("mysql password is empty")
+		return
 	}
 	if utils.MYSQL_DATA_BINLOG_DB == "" {
-		panic("mysql db is empty")
+		err = fmt.Errorf("mysql db is empty")
+		return
 	}
 
 	includeTableRegex := []string{
 		utils.MYSQL_DATA_BINLOG_DB + ".edb_info$",
-		// utils.MYSQL_DATA_BINLOG_DB + ".edb_classify$",
-		// utils.MYSQL_DATA_BINLOG_DB + ".base_from_mysteel_chemical_index$",
-		// utils.MYSQL_DATA_BINLOG_DB + ".base_from_smm_index$",
-		// utils.MYSQL_DATA_BINLOG_DB + ".edb_data*",
+
+		// 数据源
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_rzd_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_hisugar_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_ly_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_sci_hq_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_ths_hf_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_oilchem_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_ccf_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_mysteel_chemical_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_smm_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_baiinfo_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_sci_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_coalmine_mapping$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_eia_steo_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_icpi_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_yongyi_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_fenwei_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_sci99_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_bloomberg_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_mtjh_mapping$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_usda_fas_index$",
+		utils.MYSQL_DATA_BINLOG_DB + ".base_from_business_index$",
+	}
+
+	// 监听手工指标库和钢联库
+	if utils.MYSQL_DATA_BINLOG_DB_EDB != "" {
+		includeTableRegex = append(includeTableRegex, utils.MYSQL_DATA_BINLOG_DB_EDB+".edbinfo$")
+	}
+	if utils.MYSQL_DATA_BINLOG_DB_GL != "" {
+		includeTableRegex = append(includeTableRegex, utils.MYSQL_DATA_BINLOG_DB_GL+".mb_index_main_info$")
 	}
 
 	// 主从复制的身份id配置,必须全局唯一,如果没有配置的话,那么会随机生成一个
@@ -73,47 +106,77 @@ func ListenMysql() {
 	}
 
 	// 校验mysql binlog format,目前仅支持row格式
-	{
-		binlogFormat, tmpErr := binlog.GetBinlogFormat()
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-
-		if binlogFormat.Value != "ROW" {
-			panic("mysql binlog format is not ROW")
-		}
+	binlogFormat, e := binlog.GetBinlogFormat()
+	if e != nil {
+		err = fmt.Errorf("get binlog format err: %v", e)
+		return
+	}
+	if binlogFormat.Value != "ROW" {
+		err = fmt.Errorf("mysql binlog format is not ROW")
+		return
 	}
 
-	//  获取上一次启动时的binlog文件名称和位置
-	fileName, position, err := getBinlogNamePosition()
-	if err != nil {
+	// 获取上一次启动时的binlog文件名称和位置
+	fileName, position, e := getBinlogNamePosition()
+	if e != nil {
+		err = fmt.Errorf("获取binlog文件名称和位置失败, %v", e)
 		return
 	}
+
 	// 修改记录本次启动时的binlog文件名称和位置
 	modifyBinlogNamePosition(fileName, position)
-	// 定时修改binlog文件名称和位置
-	go timingModifyBinlogNamePosition()
 
-	c, err := canal.NewCanal(cfg)
-	if err != nil {
-		fmt.Println("err:", err)
+	c, e := canal.NewCanal(cfg)
+	if e != nil {
+		err = fmt.Errorf("new canal err: %v", e)
 		return
 	}
-	utils.FileLog.Debug("记录上一次启动时的fileName:", fileName, ";position:", position)
+	utils.FileLog.Debug(fmt.Sprintf("记录上一次启动时的fileName: %s, position: %d", fileName, position))
 
 	binlogHandler := &EdbEventHandler{}
 	binlogHandler.SetBinlogFileName(fileName, position)
 	c.SetEventHandler(binlogHandler)
 	//c.Run()
-	// 同步到redis
-	go binlogHandler.SyncToRedis()
 
 	pos := mysql.Position{
 		Name: fileName,
 		Pos:  position,
 	}
-	err = c.RunFrom(pos)
+	if e = c.RunFrom(pos); e != nil {
+		fmt.Printf("启动监听异常: %v, 重新定位binlog\n", e)
+		if c != nil {
+			c.Close()
+		}
+
+		// 重新获取binlog位置
+		rename, reposition, e := initBinlogNamePosition()
+		if e != nil {
+			err = fmt.Errorf("重新定位binlog失败, %v", e)
+			return
+		}
+		pos.Name = rename
+		pos.Pos = reposition
+
+		// 重新尝试监听, 再起不来就退出报异常
+		canalNew, e := canal.NewCanal(cfg)
+		if e != nil {
+			err = fmt.Errorf("renew canal err: %v", e)
+			return
+		}
+		binlogHandler.SetBinlogFileName(rename, reposition)
+		canalNew.SetEventHandler(binlogHandler)
+		c = canalNew
+		if e = c.RunFrom(pos); e != nil {
+			err = fmt.Errorf("重新监听binlog失败, %v", e)
+			return
+		}
+	}
+
+	// 定时修改binlog文件名称和位置
+	go timingModifyBinlogNamePosition()
+
+	// 同步到redis
+	go binlogHandler.SyncToRedis()
 }
 
 // getBinlogNamePosition
@@ -139,19 +202,18 @@ func getBinlogNamePosition() (fileName string, position uint32, err error) {
 
 	// 如果没有从redis中获取到上次监听到的binlog的文件名称,或者位置为0,则从mysql中获取,则从 MySQL 中获取最新的文件名和位置。
 	if fileName == `` || position == 0 {
-
 		// binlog文件名
 		fileNameKey := binlog.BinlogFileNameKey
 		fileNameLog, tmpErr := binlog.GetBusinessSysInteractionLogByKey(fileNameKey)
 		if tmpErr == nil {
-			fileName = fileNameLog.InteractionKey
+			fileName = fileNameLog.InteractionVal
 		}
 
 		// binlog位置
 		positionKey := binlog.BinlogPositionKey
 		positionLog, tmpErr := binlog.GetBusinessSysInteractionLogByKey(positionKey)
 		if tmpErr == nil {
-			positionStr := positionLog.InteractionKey
+			positionStr := positionLog.InteractionVal
 			positionInt, tmpErr := strconv.Atoi(positionStr)
 			if tmpErr == nil {
 				position = uint32(positionInt)
@@ -183,7 +245,7 @@ func modifyBinlogNamePosition(fileName string, position uint32) {
 	var err error
 	defer func() {
 		if err != nil {
-			utils.FileLog.Error("修改binlog文件名称和位置异常,fileName", fileName, ",position:", position, ",err:", err)
+			utils.FileLog.Error(fmt.Sprintf("修改binlog文件名称和位置异常, fileName: %s, position: %d, err: %v", fileName, position, err))
 		}
 	}()
 
@@ -269,3 +331,23 @@ func timingModifyBinlogNamePosition() {
 		}
 	}
 }
+
+// initBinlogNamePosition 初始化/重新定位binlog文件名称和位置
+func initBinlogNamePosition() (fileName string, position uint32, err error) {
+	// 获取mysql当前binlog文件名称和位置
+	status, e := binlog.GetShowMaster()
+	if e != nil {
+		err = fmt.Errorf("get mysql master status err: %v", e)
+		return
+	}
+	fileName = status.File
+	position = status.Position
+
+	// 写入redis
+	_ = utils.Rc.Put(utils.CACHE_MYSQL_DATA_FILENAME, fileName, 31*24*time.Hour)
+	_ = utils.Rc.Put(utils.CACHE_MYSQL_DATA_POSITION, position, 31*24*time.Hour)
+
+	// 更新MySQL
+	modifyBinlogNamePosition(fileName, position)
+	return
+}

+ 35 - 0
services/binlog/data_source.go

@@ -0,0 +1,35 @@
+package binlog
+
+import (
+	"encoding/json"
+	dataSourceModel "eta/eta_api/models/data_source"
+	"eta/eta_api/services/elastic"
+	"eta/eta_api/utils"
+	"fmt"
+)
+
+// HandleDataSourceChange2Es 数据源变动写入ES
+func HandleDataSourceChange2Es() {
+	for {
+		utils.Rc.Brpop(utils.CACHE_DATA_SOURCE_ES_HANDLE, func(b []byte) {
+			indexItem := new(dataSourceModel.SearchDataSource)
+			if e := json.Unmarshal(b, &indexItem); e != nil {
+				utils.FileLog.Info(fmt.Sprintf("HandleDataSourceChange2Es, json unmarshal err: %v", e))
+				return
+			}
+			fmt.Printf("ES开始写入, source: %d, sourceName: %s, indexCode: %s\n", indexItem.Source, indexItem.SourceName, indexItem.IndexCode)
+			// 手工指标的文档ID用source和指标编码组合
+			var docId string
+			if indexItem.Source == utils.DATA_SOURCE_MANUAL {
+				docId = fmt.Sprintf("%d-%s", indexItem.Source, indexItem.IndexCode)
+			} else {
+				docId = fmt.Sprintf("%d-%d", indexItem.Source, indexItem.PrimaryId)
+			}
+			if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+				utils.FileLog.Info(fmt.Sprintf("HandleDataSourceChange2Es, write2es byte: %s, err: %v", string(b), e))
+				return
+			}
+			fmt.Println("ES写入成功")
+		})
+	}
+}

+ 183 - 50
services/binlog/handler.go

@@ -1,10 +1,12 @@
 package binlog
 
 import (
+	dataSourceModel "eta/eta_api/models/data_source"
 	edbmonitorSvr "eta/eta_api/services/edb_monitor"
 	"eta/eta_api/utils"
 	"fmt"
 	"reflect"
+	"runtime/debug"
 	"time"
 
 	"github.com/go-mysql-org/go-mysql/canal"
@@ -20,6 +22,15 @@ type EdbEventHandler struct {
 }
 
 func (h *EdbEventHandler) OnRow(e *canal.RowsEvent) (err error) {
+	// 下面有可能出现panic导致监听挂掉
+	defer func() {
+		if r := recover(); r != nil {
+			utils.FileLog.Error("binlog OnRow panic: ", r)
+
+			stackTrace := debug.Stack()
+			utils.FileLog.Error("binlog OnRow panic stack: ", string(stackTrace))
+		}
+	}()
 
 	// 监听逻辑
 	switch e.Action {
@@ -69,25 +80,95 @@ func (h *EdbEventHandler) String() string {
 func (h *EdbEventHandler) Insert(e *canal.RowsEvent) error {
 	// 批量插入的时候,e.Rows的长度会大于0
 	fmt.Println(e.Header.ServerID, ";", e.Table.Schema, ".", e.Table.Name)
-	for _, row := range e.Rows { // 遍历当前插入的数据列表(存在批量插入的情况,所以是list)
-		newEdbInfo := h.MapRowToStruct(e.Table.Columns, row)
-		if ok := edbmonitorSvr.EdbLocalSet.IsExist(newEdbInfo.EdbInfoId); ok {
-			err := utils.Rc.LPush(edbmonitorSvr.EDB_MONITOR_HANDLE_LIST_CACHE, newEdbInfo)
-			if err != nil {
-				return err
-			}
-		} else {
-			ok, err := utils.Rc.SIsMember(edbmonitorSvr.EDB_MONITOR_ID_SET_CACHE, newEdbInfo.EdbInfoId)
-			if err != nil {
-				return err
-			}
-			if ok {
+
+	// 指标库
+	tableName := e.Table.Name
+	if tableName == "edb_info" {
+		for _, row := range e.Rows { // 遍历当前插入的数据列表(存在批量插入的情况,所以是list)
+			newEdbInfo := h.MapRowToStruct(e.Table.Columns, row)
+			if ok := edbmonitorSvr.EdbLocalSet.IsExist(newEdbInfo.EdbInfoId); ok {
 				err := utils.Rc.LPush(edbmonitorSvr.EDB_MONITOR_HANDLE_LIST_CACHE, newEdbInfo)
 				if err != nil {
 					return err
 				}
+			} else {
+				ok, err := utils.Rc.SIsMember(edbmonitorSvr.EDB_MONITOR_ID_SET_CACHE, newEdbInfo.EdbInfoId)
+				if err != nil {
+					return err
+				}
+				if ok {
+					err := utils.Rc.LPush(edbmonitorSvr.EDB_MONITOR_HANDLE_LIST_CACHE, newEdbInfo)
+					if err != nil {
+						return err
+					}
+				}
 			}
+			// if monitors, ok := edbMonitorMap[newEdbInfo.EdbInfoId]; ok {
+			// 	for _, monitor := range monitors {
+			// 		err = edbmonitorSvr.ModifyEdbMonitorState(monitor, newEdbInfo.EdbCode, newEdbInfo.Source, newEdbInfo.SubSource)
+			// 		if err != nil {
+			// 			continue
+			// 		}
+			// 	}
+			// }
 		}
+		return nil
+	}
+
+	// 数据源
+	indexOb := dataSourceModel.GetEsBaseFromIndexByTableName(tableName)
+	if indexOb == nil {
+		return fmt.Errorf("数据表无对应数据源: %s", tableName)
+	}
+	for _, row := range e.Rows {
+		indexItem := DataSourceMapRowToStruct(e.Table.Columns, row, indexOb)
+		// 写入队列(此处无需做去重处理)
+		if e := utils.Rc.LPush(utils.CACHE_DATA_SOURCE_ES_HANDLE, indexItem); e != nil {
+			return fmt.Errorf("写入redis队列失败, %v", e)
+		}
+	}
+	return nil
+}
+
+func (h *EdbEventHandler) Update(e *canal.RowsEvent) error {
+	tableName := e.Table.Name
+
+	// 指标库
+	lenRows := len(e.Rows)
+	if tableName == "edb_info" {
+		//if len(e.Rows) != 2 {
+		//	fmt.Println("更新数据异常,没有原始数据和新数据:", e.Rows)
+		//	return nil
+		//}
+
+		// 由于UPDATE语句影响行数超过1时e.Rows长度会大于2,所以此处遍历处理
+		for i := 0; i < lenRows; i += 2 {
+			if i+1 >= lenRows {
+				continue
+			}
+			oldEdbInfo := h.MapRowToStruct(e.Table.Columns, e.Rows[i])
+			newEdbInfo := h.MapRowToStruct(e.Table.Columns, e.Rows[i+1])
+			if oldEdbInfo.EndValue != newEdbInfo.EndValue {
+				if ok := edbmonitorSvr.EdbLocalSet.IsExist(newEdbInfo.EdbInfoId); ok {
+					err := utils.Rc.LPush(edbmonitorSvr.EDB_MONITOR_HANDLE_LIST_CACHE, newEdbInfo)
+					if err != nil {
+						return err
+					}
+				} else {
+					ok, err := utils.Rc.SIsMember(edbmonitorSvr.EDB_MONITOR_ID_SET_CACHE, newEdbInfo.EdbInfoId)
+					if err != nil {
+						return err
+					}
+					if ok {
+						err := utils.Rc.LPush(edbmonitorSvr.EDB_MONITOR_HANDLE_LIST_CACHE, newEdbInfo)
+						if err != nil {
+							return err
+						}
+					}
+				}
+			}
+		}
+
 		// if monitors, ok := edbMonitorMap[newEdbInfo.EdbInfoId]; ok {
 		// 	for _, monitor := range monitors {
 		// 		err = edbmonitorSvr.ModifyEdbMonitorState(monitor, newEdbInfo.EdbCode, newEdbInfo.Source, newEdbInfo.SubSource)
@@ -96,52 +177,41 @@ func (h *EdbEventHandler) Insert(e *canal.RowsEvent) error {
 		// 		}
 		// 	}
 		// }
+		return nil
 	}
-	return nil
-}
 
-func (h *EdbEventHandler) Update(e *canal.RowsEvent) error {
-	if len(e.Rows) != 2 {
-		fmt.Println("更新数据异常,没有原始数据和新数据:", e.Rows)
-		return nil
+	// 数据源
+	indexOb := dataSourceModel.GetEsBaseFromIndexByTableName(tableName)
+	if indexOb == nil {
+		return fmt.Errorf("数据表无对应数据源: %s", tableName)
 	}
-	oldEdbInfo := h.MapRowToStruct(e.Table.Columns, e.Rows[0])
-	newEdbInfo := h.MapRowToStruct(e.Table.Columns, e.Rows[1])
-	if oldEdbInfo.EndValue != newEdbInfo.EndValue {
-		if ok := edbmonitorSvr.EdbLocalSet.IsExist(newEdbInfo.EdbInfoId); ok {
-			err := utils.Rc.LPush(edbmonitorSvr.EDB_MONITOR_HANDLE_LIST_CACHE, newEdbInfo)
-			if err != nil {
-				return err
-			}
-		} else {
-			ok, err := utils.Rc.SIsMember(edbmonitorSvr.EDB_MONITOR_ID_SET_CACHE, newEdbInfo.EdbInfoId)
-			if err != nil {
-				return err
-			}
-			if ok {
-				err := utils.Rc.LPush(edbmonitorSvr.EDB_MONITOR_HANDLE_LIST_CACHE, newEdbInfo)
-				if err != nil {
-					return err
-				}
-			}
+	for i := 0; i < lenRows; i += 2 {
+		if i+1 >= lenRows {
+			continue
+		}
+		// 这里只取[i+1]即UPDATE后的数据
+		indexItem := DataSourceMapRowToStruct(e.Table.Columns, e.Rows[i+1], indexOb)
+		if e := utils.Rc.LPush(utils.CACHE_DATA_SOURCE_ES_HANDLE, indexItem); e != nil {
+			utils.FileLog.Info(fmt.Sprintf("binlog update data source lpush err: %v", e))
+			continue
 		}
 	}
-
-	// if monitors, ok := edbMonitorMap[newEdbInfo.EdbInfoId]; ok {
-	// 	for _, monitor := range monitors {
-	// 		err = edbmonitorSvr.ModifyEdbMonitorState(monitor, newEdbInfo.EdbCode, newEdbInfo.Source, newEdbInfo.SubSource)
-	// 		if err != nil {
-	// 			continue
-	// 		}
-	// 	}
-	// }
 	return nil
 }
 
 func (h *EdbEventHandler) MapRowToStruct(columns []schema.TableColumn, row []interface{}) edbmonitorSvr.EdbInfoBingLog {
+	defer func() {
+		if r := recover(); r != nil {
+			utils.FileLog.Info(fmt.Sprintf("binlog update data source panic;data:%v; err: %v", columns, r))
+		}
+	}()
 	newEdbInfo := edbmonitorSvr.EdbInfoBingLog{}
 	for i, column := range columns {
 		value := reflect.ValueOf(row[i])
+		// 数据无效的话,那么就过滤掉
+		if !value.IsValid() {
+			continue
+		}
 		switch column.Name {
 		case "edb_info_id":
 			newEdbInfo.EdbInfoId = int(value.Int())
@@ -162,9 +232,7 @@ func (h *EdbEventHandler) MapRowToStruct(columns []schema.TableColumn, row []int
 		case "modify_time":
 			newEdbInfo.ModifyTime = value.String()
 		case "base_modify_time":
-			if value.IsValid() {
-				newEdbInfo.BaseModifyTime = value.String()
-			}
+			newEdbInfo.BaseModifyTime = value.String()
 		case "min_value":
 			newEdbInfo.MinValue = value.Float()
 		case "max_value":
@@ -201,3 +269,68 @@ func (h *EdbEventHandler) SetBinlogFileName(fileName string, position uint32) {
 
 	fmt.Println("init fileName:", h.fileName, ";position:", h.position)
 }
+
+// DataSourceMapRowToStruct 数据源-binlog转为es结构体
+func DataSourceMapRowToStruct(columns []schema.TableColumn, row []interface{}, indexOb dataSourceModel.EsBaseFromIndex) dataSourceModel.SearchDataSource {
+	item := dataSourceModel.SearchDataSource{}
+	source, subSource, sourceName := indexOb.SourceInfo()
+	item.Source = source
+	item.SubSource = subSource
+	item.SourceName = sourceName
+
+	// 根据不同数据源匹配对应的字段名
+	indexCols := indexOb.EsCols()
+	for i, column := range columns {
+		// 数据无效的话,那么就过滤掉
+		value := reflect.ValueOf(row[i])
+		if !value.IsValid() {
+			continue
+		}
+
+		switch column.Name {
+		case indexCols.PrimaryId:
+			if value.Kind() == reflect.Int || value.Kind() == reflect.Int32 || value.Kind() == reflect.Int64 {
+				item.PrimaryId = int(value.Int())
+			}
+			if value.Kind() == reflect.Uint || value.Kind() == reflect.Uint32 || value.Kind() == reflect.Uint64 {
+				item.PrimaryId = int(value.Uint())
+			}
+		case indexCols.IndexCode:
+			item.IndexCode = value.String()
+		case indexCols.IndexName:
+			item.IndexName = value.String()
+		case indexCols.ClassifyId:
+			if value.Kind() == reflect.Int || value.Kind() == reflect.Int32 || value.Kind() == reflect.Int64 {
+				item.ClassifyId = int(value.Int())
+			}
+			if value.Kind() == reflect.Uint || value.Kind() == reflect.Uint32 || value.Kind() == reflect.Uint64 {
+				item.ClassifyId = int(value.Uint())
+			}
+		case indexCols.Unit:
+			item.Unit = value.String()
+		case indexCols.Frequency:
+			item.Frequency = value.String()
+		case indexCols.StartDate:
+			item.StartDate = value.String()
+		case indexCols.EndDate:
+			item.EndDate = value.String()
+		case indexCols.LatestValue:
+			if value.Kind() == reflect.String {
+				item.LatestValue = value.String()
+			}
+			if value.Kind() == reflect.Int || value.Kind() == reflect.Int32 || value.Kind() == reflect.Int64 {
+				item.LatestValue = fmt.Sprint(value.Int())
+			}
+			if value.Kind() == reflect.Float64 {
+				item.LatestValue = fmt.Sprint(value.Float())
+			}
+		case indexCols.CreateTime:
+			item.CreateTime = value.String()
+		case indexCols.ModifyTime:
+			item.ModifyTime = value.String()
+		default:
+			continue
+		}
+	}
+	return item
+}

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