Browse Source

Merge branch 'debug' of http://8.136.199.33:3000/eta_server/eta_api into bzq1/execl-edit_cf

zqbao 8 months ago
parent
commit
07378226c5
100 changed files with 12265 additions and 1608 deletions
  1. 1 2
      .gitignore
  2. 10 1
      controllers/ai/ai_file.go
  3. 0 1
      controllers/base_auth.go
  4. 8 0
      controllers/data_manage/chart_classify.go
  5. 24 0
      controllers/data_manage/chart_info.go
  6. 158 0
      controllers/data_manage/chart_info_section.go
  7. 31 3
      controllers/data_manage/chart_theme.go
  8. 2 1
      controllers/data_manage/correlation/correlation_chart_info.go
  9. 2 1
      controllers/data_manage/cross_variety/chart_info.go
  10. 33 2
      controllers/data_manage/edb_info.go
  11. 10 2
      controllers/data_manage/edb_info_calculate.go
  12. 814 5
      controllers/data_manage/excel/custom_analysis.go
  13. 44 34
      controllers/data_manage/excel/excel_info.go
  14. 2 1
      controllers/data_manage/future_good/future_good_chart_info.go
  15. 2 1
      controllers/data_manage/line_equation/line_chart_info.go
  16. 2 1
      controllers/data_manage/line_feature/chart_info.go
  17. 1907 0
      controllers/data_manage/manual_edb.go
  18. 6 0
      controllers/data_manage/multiple_graph_config.go
  19. 13 2
      controllers/data_manage/predict_edb_info.go
  20. 817 0
      controllers/data_manage/range_analysis/chart_classify.go
  21. 1302 0
      controllers/data_manage/range_analysis/chart_info.go
  22. 0 1
      controllers/data_source/guagnzhouqihuo.go
  23. 6 2
      controllers/data_source/icpi.go
  24. 0 1
      controllers/english_report/report.go
  25. 55 4
      controllers/ppt_english.go
  26. 11 1
      controllers/ppt_english_group.go
  27. 58 4
      controllers/ppt_v2.go
  28. 11 1
      controllers/ppt_v2_group.go
  29. 1 0
      controllers/report.go
  30. 2 2
      controllers/sandbox/sandbox.go
  31. 55 27
      controllers/sys_admin.go
  32. 21 1
      controllers/sys_role.go
  33. 3 3
      controllers/sys_team.go
  34. 42 458
      controllers/target.go
  35. 56 52
      go.mod
  36. 127 266
      go.sum
  37. 1 1
      main.go
  38. 34 35
      models/business_conf.go
  39. 8 0
      models/chart_permission.go
  40. 3 2
      models/data_manage/base_from_trade_index.go
  41. 236 71
      models/data_manage/chart_info.go
  42. 268 0
      models/data_manage/chart_info_range_analysis.go
  43. 374 0
      models/data_manage/chart_series.go
  44. 29 0
      models/data_manage/chart_series_edb_mapping.go
  45. 15 2
      models/data_manage/chart_theme/chart_theme_type.go
  46. 38 0
      models/data_manage/chart_type.go
  47. 78 5
      models/data_manage/data_manage_permission/excel.go
  48. 15 5
      models/data_manage/edb_data_base.go
  49. 71 17
      models/data_manage/edb_info.go
  50. 5 0
      models/data_manage/edb_info_calculate.go
  51. 28 0
      models/data_manage/edb_info_record.go
  52. 21 0
      models/data_manage/excel/excel_classify.go
  53. 0 1
      models/data_manage/excel/excel_info.go
  54. 17 0
      models/data_manage/excel/request/excel_info.go
  55. 7 0
      models/data_manage/excel/response/excel_info.go
  56. 14 0
      models/data_manage/excel_style.go
  57. 62 0
      models/data_manage/factor_edb_series.go
  58. 210 0
      models/data_manage/factor_edb_series_calculate_data_qjjs.go
  59. 8 0
      models/data_manage/factor_edb_series_chart_mapping.go
  60. 7 0
      models/data_manage/factor_edb_series_mapping.go
  61. 8 8
      models/data_manage/my_chart.go
  62. 1 0
      models/data_manage/predict_edb_info_calculate.go
  63. 1 1
      models/data_manage/trade_analysis/trade_analysis.go
  64. 15 9
      models/db.go
  65. 304 0
      models/manual_edb.go
  66. 1 1
      models/ppt_english/ppt_english_grant.go
  67. 11 11
      models/ppt_english/ppt_english_group_mapping.go
  68. 2 2
      models/ppt_v2_group_mapping.go
  69. 1 1
      models/report.go
  70. 26 26
      models/sandbox/sandbox.go
  71. 15 2
      models/smart_report/smart_report.go
  72. 63 3
      models/target.go
  73. 347 23
      routers/commentsRouter.go
  74. 28 11
      routers/router.go
  75. 684 2
      services/data/chart_extra_config.go
  76. 494 3
      services/data/chart_info.go
  77. 2 2
      services/data/chart_info_excel_balance.go
  78. 37 0
      services/data/chart_theme.go
  79. 1 1
      services/data/data_manage_permission/data_move.go
  80. 2 8
      services/data/edb_classify.go
  81. 41 5
      services/data/edb_info.go
  82. 0 287
      services/data/edb_info_calculate.go
  83. 30 0
      services/data/edb_info_record.go
  84. 4 3
      services/data/excel/balance_table.go
  85. 30 11
      services/data/excel/custom_analysis_edb.go
  86. 33 2
      services/data/excel/excel_info.go
  87. 60 45
      services/data/excel/mixed_table.go
  88. 29 18
      services/data/excel/time_table.go
  89. 4 3
      services/data/future_good/chart_info.go
  90. 33 22
      services/data/future_good/profit_chart_info.go
  91. 10 6
      services/data/line_feature/chart_info.go
  92. 983 0
      services/data/manual.go
  93. 1651 0
      services/data/range_analysis/chart_info.go
  94. 1 0
      services/elastic.go
  95. 41 0
      services/eta_forum/eta_forum_hub.go
  96. 31 35
      services/excel/lucky_sheet.go
  97. 15 0
      services/report.go
  98. 20 20
      services/sandbox/sandbox.go
  99. 19 18
      services/user_login.go
  100. 2 0
      services/video.go

+ 1 - 2
.gitignore

@@ -3,7 +3,6 @@
 /.idea
 /routers/.DS_Store
 /rdlucklog
-/etalogs
 /conf/*.conf
 /binlog/*
 /*.pdf
@@ -19,4 +18,4 @@ eta_api.exe
 eta_api.exe~
 /static/tmpFile/*
 etalogs/
-/.vscode
+/.vscode

+ 10 - 1
controllers/ai/ai_file.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/models/aimod"
 	"eta/eta_api/services"
 	"eta/eta_api/services/aiser"
+	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
 	"os"
@@ -122,7 +123,6 @@ func (this *AiFileController) FileUpload() {
 		assistantId = topic.AssistantId
 		threadId = topic.ThreadId
 	}
-
 	if aiChatTopicId <= 0 { //新增
 		topic := new(aimod.AiChatTopic)
 		var filenameWithSuffix string
@@ -159,6 +159,15 @@ func (this *AiFileController) FileUpload() {
 		chatItem.OpenaiFilePath = resourceUrl
 		chatItem.CreateTime = time.Now()
 		chatItem.ModifyTime = time.Now()
+
+		bchat, err := json.Marshal(chatItem)
+		if err != nil {
+			br.Msg = "获取数据失败!"
+			br.ErrMsg = "生成话题失败,Err:" + err.Error()
+			return
+		}
+		alarm_msg.SendAlarmMsg(string(bchat), 1)
+		alarm_msg.SendAlarmMsg(string("cur model:"+model), 1)
 		_, err = aimod.AddAiChat(chatItem)
 		if err != nil {
 			br.Msg = "获取数据失败!"

+ 0 - 1
controllers/base_auth.go

@@ -290,7 +290,6 @@ func (c *BaseAuthController) ServeJSON(encoding ...bool) {
 		}
 	}
 
-
 	//新增uuid记录
 	{
 		if _, ok := AdminOperateRecordMap[urlPath]; !ok && !strings.Contains(urlPath, "cygx") {

+ 8 - 0
controllers/data_manage/chart_classify.go

@@ -654,6 +654,14 @@ func (this *ChartClassifyController) DeleteChartClassify() {
 			br.ErrMsg = "删除失败,Err:" + err.Error()
 			return
 		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+			err = data_manage.DeleteChartSeriesAndEdbMapping(req.ChartInfoId)
+			if err != nil {
+				br.Msg = "删除失败"
+				br.ErrMsg = "删除失败,Err:" + err.Error()
+				return
+			}
+		}
 		//删除ES
 		{
 			go data.EsDeleteChartInfo(req.ChartInfoId)

+ 24 - 0
controllers/data_manage/chart_info.go

@@ -1379,6 +1379,8 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 			return
 		}
 		extraConfigStr = chartInfo.BarConfig
+	} else if chartInfo != nil && chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+		extraConfigStr = req.ExtraConfig
 	}
 
 	// 获取图表中的指标数据
@@ -3219,6 +3221,28 @@ func (this *ChartInfoController) CopyChartInfo() {
 			return
 		}
 	}
+	if chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+		// 查询系列信息并复制
+		chartSeriesList, e := data_manage.GetChartSeriesByChartInfoId(oldChartInfo.ChartInfoId)
+		if e != nil {
+			br.Msg = "获取图表关联的系列信息失败"
+			br.ErrMsg = "获取图表关联的系列信息失败,Err:" + e.Error()
+			return
+		}
+
+		chartSeriesEdbList, e := data_manage.GetChartSeriesEdbByChartInfoId(oldChartInfo.ChartInfoId)
+		if err != nil {
+			br.Msg = "获取图表关联的系列指标信息失败"
+			br.ErrMsg = "获取图表关联的系列指标信息失败,Err:" + err.Error()
+			return
+		}
+		err = data_manage.CopyChartSeriesAndEdbMapping(chartSeriesList, chartSeriesEdbList, chartInfo.ChartInfoId)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存图表系列失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	//添加es数据
 	go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId)

+ 158 - 0
controllers/data_manage/chart_info_section.go

@@ -0,0 +1,158 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/data"
+	"eta/eta_api/utils"
+	"strings"
+)
+
+// GetChartTypeList
+// @Title 获取所有图表类型
+// @Description 获取所有图表类型
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /chart_info/type_list [get]
+func (this *ChartInfoController) GetChartTypeList() {
+	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
+	}
+
+	listTmp, err := data_manage.GetChartTypeList()
+	if err != nil {
+		br.Msg = "获取图表类型失败"
+		br.ErrMsg = "获取图表类型失败,Err:" + err.Error()
+		return
+	}
+	//遍历list,将id和name组成map
+	chartTypeMap := make(map[int][]data_manage.ChartType)
+	for _, v := range listTmp {
+		if v.ParentId > 0 {
+			chartTypeMap[v.ParentId] = append(chartTypeMap[v.ParentId], v)
+		}
+	}
+	list := make([]data_manage.ChartTypeList, 0)
+	for _, v := range listTmp {
+		if v.ParentId == 0 {
+			tmp := data_manage.ChartTypeList{
+				ChartTypeId:     v.ChartTypeId,
+				ChartTypeName:   v.ChartTypeName,
+				ChartTypeNameEn: v.ChartTypeNameEn,
+				ParentId:        v.ParentId,
+			}
+			child, ok := chartTypeMap[v.ChartTypeId]
+			if ok {
+				tmp.Child = child
+			}
+			list = append(list, tmp)
+		}
+	}
+
+	resp := data_manage.ChartTypeListResp{List: list}
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取图表类型成功"
+
+}
+
+// PreviewSectionCombineChartInfo
+// @Title 图表-获取预览的截面组合图数据
+// @Description 图表-获取预览的截面组合图数据
+// @Param	request	body data_manage.PreviewSectionCombineChartReq true "type json string"
+// @Success 200 {object} data_manage.ChartEdbInfoDetailResp
+// @router /chart_info/preview/section_combine [post]
+func (this *ChartInfoController) PreviewSectionCombineChartInfo() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req data_manage.PreviewSectionCombineChartReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	chartInfo := new(data_manage.ChartInfoView)
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	chartType := utils.CHART_TYPE_SECTION_COMBINE
+	edbInfoIdArr, err, errMsg := data.CheckChartExtraConfig(chartType, req.ExtraConfig)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = "添加失败:" + err.Error()
+		return
+	}
+	chartInfo.ChartType = chartType
+	mappingList, err := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdArr)
+
+	// 获取图表中的指标数据
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, chartType, "", "", "", mappingList, req.ExtraConfig, "")
+	if err != nil {
+		br.Msg = "获取失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	warnEdbList := make([]string, 0)
+	for _, v := range edbList {
+		if v.IsNullData {
+			warnEdbList = append(warnEdbList, v.EdbName+"("+v.EdbCode+")")
+		}
+	}
+	if len(warnEdbList) > 0 {
+		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
+	}
+
+	// 图表的指标来源
+	sourceNameList, sourceNameEnList := data.GetEdbSourceByEdbInfoIdList(edbList)
+	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+	//图表操作权限
+	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, true)
+	//判断是否需要展示英文标识
+	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList, utils.CHART_SOURCE_DEFAULT, chartType)
+	chartInfo.Button = data_manage.ChartViewButton{
+		IsEdit:    chartInfo.IsEdit,
+		IsEnChart: chartInfo.IsEnChart,
+		IsAdd:     chartInfo.IsAdd,
+		IsCopy:    true,
+		IsSetName: chartInfo.IsSetName,
+	}
+
+	resp := new(data_manage.ChartInfoDetailResp)
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.YDataList = yDataList
+	resp.DataResp = dataResp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 31 - 3
controllers/data_manage/chart_theme.go

@@ -87,13 +87,32 @@ func (c *ChartThemeController) TypeList() {
 		return
 	}
 
-	list, err := chart_theme.GetAllChartThemeTypeList()
+	listTmp, err := chart_theme.GetAllChartThemeTypeList()
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表样式类型信息失败,Err:" + err.Error()
 		return
 	}
 
+	//遍历list,将id和name组成map
+	chartTypeMap := make(map[int][]*chart_theme.ChartThemeTypeList)
+	for _, v := range listTmp {
+		if v.ParentId > 0 {
+			chartTypeMap[v.ParentId] = append(chartTypeMap[v.ParentId], v)
+		}
+	}
+	list := make([]*chart_theme.ChartThemeTypeList, 0)
+	for _, v := range listTmp {
+		if v.ParentId == 0 {
+			tmp := v
+			child, ok := chartTypeMap[v.ChartType]
+			if ok {
+				tmp.Child = child
+			}
+			list = append(list, tmp)
+		}
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
@@ -142,6 +161,7 @@ func (c *ChartThemeController) GetThemePreviewData() {
 	chartInfo := new(data_manage.ChartInfoView)
 	// 图表额外数据参数
 	extraConfigStr := ``
+	var barConfig data_manage.BarChartInfoReq
 
 	// 开始时间,结束时间
 	var tmpStartDate, tmpEndDate string
@@ -170,6 +190,8 @@ func (c *ChartThemeController) GetThemePreviewData() {
 		chartInfo.ChartName = "散点图"
 	case 7: // 柱形图
 		edbInfoIdList = []int{1, 2, 3, 4, 5}
+		chartInfo.LeftMin = "260"
+		chartInfo.LeftMax = "430"
 		extraConfigStr = `{"EdbInfoIdList":[{"EdbInfoId":1,"Name":"指标1","NameEn":"","Source":0},{"EdbInfoId":2,"Name":"指标2","NameEn":"","Source":0},{"EdbInfoId":3,"Name":"指标3","NameEn":"","Source":0},{"EdbInfoId":4,"Name":"指标4","NameEn":"","Source":0},{"EdbInfoId":5,"Name":"指标5","NameEn":"","Source":0}],"DateList":[{"Type":2,"Date":"","Value":100,"Color":"","Name":""},{"Type":1,"Date":"","Value":0,"Color":"","Name":""}],"Sort":{"Sort":0,"DateIndex":0},"XEdbList":null,"YEdbList":null,"Unit":"千桶","UnitEn":""}`
 		chartInfo.ChartName = "柱形图"
 	case 10: // 截面散点图
@@ -182,6 +204,12 @@ func (c *ChartThemeController) GetThemePreviewData() {
 		chartInfo.LeftMax = "60000"
 		extraConfigStr = `{"DateList":[{"Type":3,"Date":"2023-11-01","Value":0,"Color":"","Name":""},{"Type":1,"Date":"","Value":0,"Color":"","Name":""}]}`
 		chartInfo.ChartName = "雷达图"
+	case utils.CHART_TYPE_SECTION_COMBINE:
+		edbInfoIdList = []int{19, 20, 21, 22, 23, 24}
+		chartInfo.LeftMin = "0"
+		chartInfo.LeftMax = "4000"
+		extraConfigStr = `{"DateConfList":[],"IsHeap":1,"XDataList":[{"Name":"内销","NameEn":""},{"Name":"出口","NameEn":""},{"Name":"销量","NameEn":""},{"Name":"产量","NameEn":""}],"UnitList":{"LeftName":"万台","LeftNameEn":"","RightName":"%","RightNameEn":"%","RightTwoName":"","RightTwoNameEn":""},"BaseChartSeriesName":"增量","SortType":0,"SeriesList":[{"ChartSeriesId":1,"SeriesName":"增量","SeriesNameEn":"","ChartStyle":"column","ChartColor":"#FF0000","ChartWidth":2,"IsPoint":1,"IsNumber":1,"IsAxis":0,"MaxData":4000,"MinData":0,"IsOrder":true,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":0,"DateConf":{},"DateConfName":""}],"DataList":[1455,1076,2532,3729],"NoDataEdbIdList":[]},{"ChartSeriesId":2,"SeriesName":"增速","SeriesNameEn":"","ChartStyle":"spline","ChartColor":"#FF0000","ChartWidth":2,"IsPoint":1,"IsNumber":1,"IsAxis":1,"MaxData":15,"MinData":10,"IsOrder":true,"EdbInfoList":[{"ChartSeriesEdbMappingId":0,"ChartSeriesId":0,"EdbInfoId":0,"DateConf":{},"DateConfName":""}],"DataList":[13,12,12,14],"NoDataEdbIdList":[]}]}`
+		chartInfo.ChartName = "图表标题"
 	default:
 		br.Msg = "暂不支持该类型"
 		br.IsSendEmail = false
@@ -255,6 +283,7 @@ func (c *ChartThemeController) GetThemePreviewData() {
 	resp.XEdbIdValue = xEdbIdValue
 	resp.YDataList = yDataList
 	resp.DataResp = dataResp
+	resp.BarChartInfo = barConfig
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
@@ -338,7 +367,7 @@ func (c *ChartThemeController) Add() {
 }
 
 // Edit
-// @Title 新增主题
+// @Title 编辑主题
 // @Description
 // @Param	request	body request.DeleteThemeConfReq true "type json string"
 // @Success 200 Ret=200 修改成功
@@ -679,7 +708,6 @@ func (c *ChartThemeController) ListBySource() {
 		list[i].Config = newConfig
 	}
 
-
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"

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

@@ -12,11 +12,12 @@ import (
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // CorrelationChartInfoController 相关性图表管理

+ 2 - 1
controllers/data_manage/cross_variety/chart_info.go

@@ -14,10 +14,11 @@ import (
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // ChartInfoController

+ 33 - 2
controllers/data_manage/edb_info.go

@@ -21,11 +21,12 @@ import (
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"strings"
 	"sync"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // EdbInfoController 数据管理
@@ -2171,6 +2172,8 @@ func (this *EdbInfoController) EdbInfoList() {
 	button.AddButton = false
 	edbInfoItem.Button = button
 	edbInfoItem.DataList = make([]*data_manage.EdbData, 0)
+	edbInfoItem.EndValue = utils.FormatFloatPoint(edbInfoItem.EndValue, 4)
+	edbInfoItem.LatestValue = utils.FormatFloatPoint(edbInfoItem.LatestValue, 4)
 
 	// 如果有数据权限,那么就去获取指标数据
 	if edbInfoItem.HaveOperaAuth {
@@ -2180,6 +2183,9 @@ func (this *EdbInfoController) EdbInfoList() {
 			br.ErrMsg = "获取指标数据总数失败,Err:" + err.Error()
 			return
 		}
+		for _, v := range dataList {
+			v.Value = utils.FormatFloatPoint(v.Value, 4)
+		}
 		page = paging.GetPaging(currentIndex, pageSize, dataCount)
 		edbInfoItem.DataList = dataList
 	}
@@ -2433,6 +2439,11 @@ func (this *EdbInfoController) EdbInfoEdit() {
 		return
 	}
 
+	// 记录旧的指标基本信息
+	oldEdbName := edbInfo.EdbName
+	oldFrequency := edbInfo.Frequency
+	oldUnit := edbInfo.Unit
+
 	var haveOperaAuth bool
 	// 权限校验
 	{
@@ -2526,6 +2537,20 @@ func (this *EdbInfoController) EdbInfoEdit() {
 	//	}
 	//}
 
+	// 新增保存记录日志
+	oldEdbInfo := new(data_manage.EdbInfo)
+	oldEdbInfo.EdbInfoId = edbInfo.EdbInfoId
+	oldEdbInfo.EdbName = oldEdbName
+	oldEdbInfo.Frequency = oldFrequency
+	oldEdbInfo.Unit = oldUnit
+	newEdbInfoRecord := new(data_manage.EdbInfoEditRecord)
+	newEdbInfoRecord.EdbName = req.EdbName
+	newEdbInfoRecord.Frequency = req.Frequency
+	newEdbInfoRecord.Unit = req.Unit
+	newEdbInfoRecord.OperateUserId = sysUser.AdminId
+	newEdbInfoRecord.OperateUserRealName = sysUser.RealName
+	go data.AddEditEdbInfoRcord(oldEdbInfo, newEdbInfoRecord)
+
 	//新增操作日志
 	{
 		edbLog := new(data_manage.EdbInfoLog)
@@ -3754,6 +3779,10 @@ func (this *ChartInfoController) EdbInfoData() {
 		}
 		fullEdb.HaveOperaAuth = haveOperaAuth
 	}
+	fullEdb.MinValue = utils.FormatFloatPoint(fullEdb.MinValue, 4)
+	fullEdb.MaxValue = utils.FormatFloatPoint(fullEdb.MaxValue, 4)
+	fullEdb.EndValue = utils.FormatFloatPoint(fullEdb.EndValue, 4)
+	fullEdb.LatestValue = utils.FormatFloatPoint(fullEdb.LatestValue, 4)
 	resp.EdbInfo = fullEdb
 
 	resp.DataList = make([]*data_manage.EdbDataList, 0)
@@ -3771,7 +3800,9 @@ func (this *ChartInfoController) EdbInfoData() {
 			br.ErrMsg = "获取失败,Err:" + err.Error()
 			return
 		}
-
+		for _, v := range dataList {
+			v.Value = utils.FormatFloatPoint(v.Value, 4)
+		}
 		resp.DataList = dataList
 	}
 	br.Ret = 200

+ 10 - 2
controllers/data_manage/edb_info_calculate.go

@@ -9,11 +9,12 @@ import (
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"net/url"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // 计算指标
@@ -230,7 +231,6 @@ func (this *ChartInfoController) CalculateDetail() {
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-
 	fullEdb := new(data_manage.EdbInfoFullClassify)
 	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfo.ClassifyId)
 	if err != nil {
@@ -649,6 +649,7 @@ func (this *ChartInfoController) CalculateBatchSave() {
 		EdbInfoIdArr: req.EdbInfoIdArr,
 		Calendar:     req.Calendar,
 		Extra:        req.Extra,
+		EmptyType:    req.EmptyType,
 	}
 
 	// 调用指标库去更新
@@ -848,6 +849,8 @@ func (this *ChartInfoController) CalculateBatchEdit() {
 		Frequency:     req.Frequency,
 		Unit:          req.Unit,
 		ClassifyId:    req.ClassifyId,
+		AdminId:       this.SysUser.AdminId,
+		AdminName:     this.SysUser.RealName,
 		Formula:       req.Formula, //N数值移动平均计算、环比值、环差值
 		FromEdbInfoId: req.FromEdbInfoId,
 		Source:        req.Source,
@@ -856,6 +859,7 @@ func (this *ChartInfoController) CalculateBatchEdit() {
 		EdbInfoIdArr:  req.EdbInfoIdArr,
 		Calendar:      req.Calendar,
 		Extra:         req.Extra,
+		EmptyType:     req.EmptyType,
 	}
 
 	// 调用指标库去更新
@@ -1948,6 +1952,10 @@ func (this *ChartInfoController) BatchCalculateBatchEdit() {
 		}
 		return
 	}
+
+	req.AdminId = sysUser.AdminId
+	req.AdminName = sysUser.RealName
+
 	req.EdbList = reqEdbList
 	// 调用指标库去更新
 	reqJson, err := json.Marshal(req)

+ 814 - 5
controllers/data_manage/excel/custom_analysis.go

@@ -4,13 +4,19 @@ import (
 	"encoding/json"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
+	excelPermissionModel "eta/eta_api/models/data_manage/data_manage_permission"
 	excelModel "eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/data_manage/excel/request"
 	"eta/eta_api/models/data_manage/excel/response"
+	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/data/excel"
+	excel2 "eta/eta_api/services/data/excel"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"sort"
 	"strconv"
 	"strings"
 	"time"
@@ -413,12 +419,25 @@ func (c *CustomAnalysisController) BaseExcelDetail() {
 	}
 
 	// 数据权限
-	haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelDetail.ExcelInfoId, excelDetail.ExcelClassifyId, excelDetail.IsJoinPermission, c.SysUser.AdminId)
-	if err != nil {
-		br.Msg = "获取ETA表格失败"
-		br.ErrMsg = "获取ETA表格权限失败,Err:" + err.Error()
+	//haveOperaAuth, err := data_manage_permission.CheckExcelPermissionByExcelInfoId(excelDetail.ExcelInfoId, excelDetail.ExcelClassifyId, excelDetail.IsJoinPermission, c.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "获取ETA表格失败"
+	//	br.ErrMsg = "获取ETA表格权限失败,Err:" + err.Error()
+	//	return
+	//}
+
+	// 自定义分析权限与其他表格略有差别...
+	permissions, e := excelPermissionModel.GetExcelPermissionByExcelIdAndUserId(excelDetail.ExcelInfoId, sysUser.AdminId)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取表格权限失败, %v", e)
 		return
 	}
+	var permissionType []int
+	for _, v := range permissions {
+		permissionType = append(permissionType, v.PermissionType)
+	}
+	opButton := excel.GetCustomAnalysisOpButton(sysUser, excelDetail.SysUserId, permissionType)
 
 	resp.IsFind = true
 	resp.ExcelInfo = response.FindExcelInfo{
@@ -435,7 +454,7 @@ func (c *CustomAnalysisController) BaseExcelDetail() {
 		Sort:            excelDetail.Sort,
 		ModifyTime:      excelDetail.ModifyTime,
 		CreateTime:      excelDetail.CreateTime,
-		Button:          excel.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, haveOperaAuth),
+		Button:          opButton,
 		HaveOperaAuth:   true,
 	}
 	if markStatus.Status == 0 {
@@ -443,6 +462,7 @@ func (c *CustomAnalysisController) BaseExcelDetail() {
 	} else {
 		resp.ExcelInfo.Editor = markStatus.Editor
 	}
+
 	if excelDetail != nil {
 		sheetList, err := excelModel.GetAllSheetItemList(excelDetail.ExcelInfoId)
 		if err != nil {
@@ -607,3 +627,792 @@ func (c *CustomAnalysisController) ExcelDataList() {
 	br.Msg = "获取成功"
 	br.Data = sheetList
 }
+
+// ClassifyList
+// @Title 分类列表
+// @Description 分类列表
+// @Param   IsShare  query  bool  false  "是否共享表格: true-是; false-否(默认)"
+// @Success 200 {object} response.ExcelClassifyListResp
+// @router /excel_classify/list [get]
+func (c *CustomAnalysisController) ClassifyList() {
+	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 := utils.CUSTOM_ANALYSIS_TABLE
+	isShare, _ := c.GetBool("IsShare")
+
+	resp := new(response.ExcelClassifyListResp)
+
+	// 查询我分享的/分享给我的表格
+	excels, e := excelPermissionModel.GetAdminAuthExcelInfoPermission(source, sysUser.AdminId, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取我分享的/分享给我的表格失败, %v", e)
+		return
+	}
+	hasShareMap := make(map[int]bool) // 是否已分享标记
+	for _, v := range excels {
+		if v.CreateUserId == sysUser.AdminId {
+			hasShareMap[int(v.ExcelInfoId)] = true
+		}
+	}
+
+	// 共享
+	if isShare {
+		shareExcel := new(excelModel.ExcelClassifyItems)
+		shareExcel.ExcelClassifyId = excelModel.CustomAnalysisMenuShareId
+		shareExcel.ExcelClassifyName = "我共享的"
+		shareExcel.Level = 1
+		shareExcel.Sort = 1
+		shareExcel.UniqueCode = fmt.Sprintf("SHARE%d", shareExcel.ExcelClassifyId)
+		shareExcel.HaveOperaAuth = true
+		shareExcel.Children = make([]*excelModel.ExcelClassifyItems, 0)
+
+		sharedExcel := new(excelModel.ExcelClassifyItems)
+		sharedExcel.ExcelClassifyId = excelModel.CustomAnalysisMenuSharedId
+		sharedExcel.ExcelClassifyName = "收到共享"
+		sharedExcel.Level = 1
+		sharedExcel.Sort = 2
+		sharedExcel.UniqueCode = fmt.Sprintf("SHARED%d", sharedExcel.ExcelClassifyId)
+		sharedExcel.HaveOperaAuth = true
+		sharedExcel.Children = make([]*excelModel.ExcelClassifyItems, 0)
+
+		// 收到共享细分目录
+		sharedUserClassify := make(map[int]*excelModel.ExcelClassifyItems)
+		adminIdName := make(map[int]string)
+		{
+			adminOb := new(system.Admin)
+			adminList, e := adminOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取用户列表失败, %v", e)
+				return
+			}
+			for _, v := range adminList {
+				adminIdName[v.AdminId] = v.RealName
+			}
+		}
+
+		sortUser := make([]int, 0)
+		shareExists := make(map[int]bool)
+		for _, v := range excels {
+			item := new(excelModel.ExcelClassifyItems)
+			item.ExcelInfoId = int(v.ExcelInfoId)
+			item.ExcelClassifyName = v.ExcelName
+			item.UniqueCode = v.UniqueCode
+			item.HaveOperaAuth = true
+			item.HasShare = hasShareMap[int(v.ExcelInfoId)]
+			// 我共享的
+			if v.CreateUserId == sysUser.AdminId && !shareExists[int(v.ExcelInfoId)] {
+				shareExists[int(v.ExcelInfoId)] = true
+				item.Level = 2
+				item.ParentId = shareExcel.ExcelClassifyId
+				item.ShowShareBtn = true
+				shareExcel.Children = append(shareExcel.Children, item)
+				continue
+			}
+
+			// 收到共享
+			if int(v.SysUserId) == sysUser.AdminId {
+				if adminIdName[v.CreateUserId] == "" {
+					// 分享人用户不存在, 那么不显示该分类
+					continue
+				}
+				if !utils.InArrayByInt(sortUser, v.CreateUserId) {
+					sortUser = append(sortUser, v.CreateUserId)
+				}
+				if sharedUserClassify[v.CreateUserId] == nil {
+					us := new(excelModel.ExcelClassifyItems)
+					us.ExcelClassifyId = v.CreateUserId // 用户ID作为分类ID
+					us.ExcelClassifyName = fmt.Sprintf("%s的表格", adminIdName[v.CreateUserId])
+					us.Level = 2
+					us.UniqueCode = fmt.Sprintf("%d-%d", sharedExcel.ExcelClassifyId, v.CreateUserId)
+					us.HaveOperaAuth = true
+					us.ShowShareBtn = false
+					us.Children = make([]*excelModel.ExcelClassifyItems, 0)
+					sharedUserClassify[v.CreateUserId] = us
+				}
+				item.Level = 3
+				item.ParentId = sharedUserClassify[v.CreateUserId].ExcelClassifyId
+				sharedUserClassify[v.CreateUserId].Children = append(sharedUserClassify[v.CreateUserId].Children, item)
+			}
+		}
+		for _, v := range sortUser {
+			if sharedUserClassify[v] != nil {
+				sharedExcel.Children = append(sharedExcel.Children, sharedUserClassify[v])
+			}
+		}
+
+		resp.AllNodes = make([]*excelModel.ExcelClassifyItems, 0)
+		resp.AllNodes = append(resp.AllNodes, shareExcel, sharedExcel)
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	// 我的表格
+	classifyList, err := excelModel.GetAdminExcelClassifyBySource(source, sysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	classifyMap := make(map[int]*excelModel.ExcelClassifyItems)
+	for _, v := range classifyList {
+		v.HaveOperaAuth = true
+		classifyMap[v.ExcelClassifyId] = v
+	}
+	allExcelInfo, err := excelModel.GetNoContentExcelInfoAll(source, sysUser.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
+		return
+	}
+
+	excelInfoMap := make(map[int][]*excelModel.ExcelClassifyItems)
+	for _, v := range allExcelInfo {
+		v.HaveOperaAuth = true
+		v.ShowShareBtn = true
+		v.HasShare = hasShareMap[v.ExcelInfoId]
+		excelInfoMap[v.ExcelClassifyId] = append(excelInfoMap[v.ExcelClassifyId], v)
+	}
+
+	classifyListMap := make(map[int][]*excelModel.ExcelClassifyItems)
+	for _, v := range classifyList {
+		if v.ParentId > 0 {
+			classifyListMap[v.ParentId] = append(classifyListMap[v.ParentId], v)
+		}
+		if existItems, ok := excelInfoMap[v.ExcelClassifyId]; ok {
+			v.Children = existItems
+		}
+	}
+	for key, classify := range classifyList {
+		subList, ok := classifyListMap[classify.ExcelClassifyId]
+		if ok {
+			classifyList[key].Children = append(classifyList[key].Children, subList...)
+			sort.Slice(classifyList[key].Children, func(i, j int) bool {
+				return excelModel.ExcelClassifyItemBySort(classifyList[key].Children[i], classifyList[key].Children[j])
+			})
+		}
+	}
+
+	nodeAll := make([]*excelModel.ExcelClassifyItems, 0)
+	for _, v := range classifyList {
+		if v.ParentId == 0 {
+			sort.Slice(v.Children, func(i, j int) bool {
+				return excelModel.ExcelClassifyItemBySort(v.Children[i], v.Children[j])
+			})
+			nodeAll = append(nodeAll, v)
+		}
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// List
+// @Title 自定义分析列表
+// @Description ETA表格列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ExcelClassifyId   query   int  true       "分类id"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   IsShare  query  bool  false  "是否共享表格: true-是; false-否(默认)"
+// @Success 200 {object} response.ExcelListResp
+// @router /excel/list [get]
+func (c *CustomAnalysisController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	excelClassifyId, _ := c.GetInt("ExcelClassifyId")
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyword := c.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
+	adminId := sysUser.AdminId
+	isShare, _ := c.GetBool("IsShare")
+	source := utils.CUSTOM_ANALYSIS_TABLE
+
+	var condition string
+	var pars []interface{}
+	condition += " AND source = ?"
+	pars = append(pars, source)
+	// 共享的
+	//hasShareMap := make(map[int]bool)
+	if isShare {
+		if keyword != "" {
+			keyword = fmt.Sprint("%", keyword, "%")
+		}
+		// 查询我分享的/分享给我的表格
+		excels, e := excelPermissionModel.GetAdminAuthExcelInfoPermission(source, adminId, keyword)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取我分享的/分享给我的表格失败, %v", e)
+			return
+		}
+
+		var excelIds, shareIds, sharedIds, userIds []int
+		for _, v := range excels {
+			id := int(v.ExcelInfoId)
+			// 无分类ID
+			if excelClassifyId <= 0 && !utils.InArrayByInt(excelIds, id) {
+				excelIds = append(excelIds, id)
+				continue
+			}
+			// 用户分类
+			if excelClassifyId == v.CreateUserId {
+				userIds = append(userIds, id)
+				continue
+			}
+			// 我共享的
+			if excelClassifyId == excelModel.CustomAnalysisMenuShareId && v.CreateUserId == sysUser.AdminId {
+				shareIds = append(shareIds, id)
+				//hasShareMap[id] = true
+				continue
+			}
+			// 收到共享
+			if excelClassifyId == excelModel.CustomAnalysisMenuSharedId && int(v.SysUserId) == sysUser.AdminId {
+				sharedIds = append(sharedIds, id)
+				continue
+			}
+		}
+		if excelClassifyId > 0 {
+			switch excelClassifyId {
+			case excelModel.CustomAnalysisMenuShareId: // 我共享的
+				excelIds = shareIds
+			case excelModel.CustomAnalysisMenuSharedId: // 收到共享
+				excelIds = sharedIds
+			default: // 用户目录
+				excelIds = userIds
+			}
+		}
+		if len(excelIds) == 0 {
+			page := paging.GetPaging(currentIndex, pageSize, 0)
+			resp := response.ExcelListResp{
+				Paging: page,
+				List:   make([]*excelModel.MyExcelInfoList, 0),
+			}
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			br.Data = resp
+			return
+		}
+
+		condition += fmt.Sprintf(` AND excel_info_id IN (%s)`, utils.GetOrmInReplace(len(excelIds)))
+		pars = append(pars, excelIds)
+
+	} else {
+		condition += ` AND sys_user_id = ?`
+		pars = append(pars, adminId)
+	}
+
+	// 筛选分类
+	if !isShare && excelClassifyId > 0 {
+		_, err := excelModel.GetExcelClassifyById(excelClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取表格信息失败"
+			br.ErrMsg = "获取信息失败,GetExcelClassify,Err:" + err.Error()
+			return
+		}
+
+		childClassify, e, _ := excel2.GetChildClassifyByClassifyId(excelClassifyId, source)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
+			return
+		}
+		if len(childClassify) == 0 {
+			condition += " AND excel_classify_id = ? "
+			pars = append(pars, excelClassifyId)
+		} else {
+			classifyIds := []int{excelClassifyId}
+			for _, v := range childClassify {
+				classifyIds = append(classifyIds, v.ExcelClassifyId)
+			}
+			condition += fmt.Sprintf(` AND excel_classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+			pars = append(pars, classifyIds)
+		}
+	}
+	if keyword != "" {
+		condition += ` AND (excel_name LIKE ?)`
+		pars = utils.GetLikeKeywordPars(pars, keyword, 1)
+	}
+
+	// 获取表格信息
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+	list, err := excelModel.GetNoContentExcelListByCondition(condition, pars, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Success = true
+		br.Msg = "获取表格信息失败"
+		br.ErrMsg = "获取表格信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range list {
+		v.HaveOperaAuth = true
+		//v.Button.CancelShareButton = hasShareMap[v.ExcelInfoId]
+	}
+
+	// 总数据量
+	dataCount, err := excelModel.GetExcelListCountByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取表格列表信息失败"
+		br.ErrMsg = "获取表格列表数据总数失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, dataCount)
+
+	resp := response.ExcelListResp{
+		Paging: page,
+		List:   list,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Share
+// @Title 分享表格
+// @Description 分享表格
+// @Param	request	body request.ShareExcelInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /excel/share [post]
+func (c *CustomAnalysisController) Share() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg != "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.ShareExcelInfoReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if req.ExcelInfoId <= 0 {
+		br.Msg = "请选择表格"
+		return
+	}
+
+	excelInfo, e := excelModel.GetExcelInfoById(req.ExcelInfoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "表格不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取表格失败, %v", e)
+		return
+	}
+	if excelInfo.SysUserId != sysUser.AdminId {
+		br.Msg = "无权操作"
+		br.ErrMsg = fmt.Sprintf("无权设置表格权限, ExcelInfoId: %d, SysAdminId: %d", req.ExcelInfoId, sysUser.AdminId)
+		return
+	}
+
+	source := utils.CUSTOM_ANALYSIS_TABLE
+	newPermissions := make([]*excelPermissionModel.ExcelInfoPermission, 0)
+	for _, v := range req.ViewUserIds {
+		newPermissions = append(newPermissions, &excelPermissionModel.ExcelInfoPermission{
+			ExcelInfoId:    int32(req.ExcelInfoId),
+			Source:         int32(source),
+			SysUserId:      int32(v),
+			CreateTime:     time.Now().Local(),
+			ModifyTime:     time.Now().Local(),
+			PermissionType: 1,
+		})
+	}
+	for _, v := range req.EditUserIds {
+		newPermissions = append(newPermissions, &excelPermissionModel.ExcelInfoPermission{
+			ExcelInfoId:    int32(req.ExcelInfoId),
+			Source:         int32(source),
+			SysUserId:      int32(v),
+			CreateTime:     time.Now().Local(),
+			ModifyTime:     time.Now().Local(),
+			PermissionType: 2,
+		})
+	}
+	if e := excelPermissionModel.ClearAndSetExcelInfoPermission(source, req.ExcelInfoId, newPermissions); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("设置表格权限失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ShareDetail
+// @Title 分享表格-详情
+// @Description 分享表格-详情
+// @Param   ExcelInfoId  query  int  true  "表格ID"
+// @Success 200 {object} response.ShareExcelInfoDetail
+// @router /excel/share_detail [get]
+func (c *CustomAnalysisController) ShareDetail() {
+	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
+	}
+	excelInfoId, _ := c.GetInt("ExcelInfoId")
+	if excelInfoId <= 0 {
+		br.Msg = "请选择表格"
+		return
+	}
+
+	permissions, e := excelPermissionModel.GetExcelPermissionBySourceAndId(excelInfoId, utils.CUSTOM_ANALYSIS_TABLE)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取表格权限失败, %v", e)
+		return
+	}
+	resp := new(response.ShareExcelInfoDetail)
+	resp.ExcelInfoId = excelInfoId
+	for _, v := range permissions {
+		if v.PermissionType == 1 {
+			resp.ViewUserIds = append(resp.ViewUserIds, int(v.SysUserId))
+		}
+		if v.PermissionType == 2 {
+			resp.EditUserIds = append(resp.EditUserIds, int(v.SysUserId))
+		}
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// CustomAnalysisCommonController 修复数据用的
+type CustomAnalysisCommonController struct {
+	controllers.BaseCommonController
+}
+
+//var OnceCustomAnalysisFixed = false
+
+// FixClassify
+// @Title 修复表格分类(一次性-后续版本可删掉)
+// @Description 修复表格分类
+// @Param   ExcelInfoId  query  int  true  "表格ID"
+// @Success 200 string "操作成功"
+// @router /excel/fix_classify [get]
+func (c *CustomAnalysisCommonController) FixClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg != "" {
+			br.IsSendEmail = false
+		}
+		if br.Ret == 200 {
+			//OnceCustomAnalysisFixed = true
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	//if OnceCustomAnalysisFixed {
+	//	br.Ret = 200
+	//	br.Success = true
+	//	br.Msg = "请勿重复修复"
+	//	return
+	//}
+	source := utils.CUSTOM_ANALYSIS_TABLE
+
+	// 获取所有分类
+	classifies, e := excelModel.GetExcelClassifyModelBySource(source)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类列表失败, %v", e)
+		return
+	}
+	classifyMap := make(map[int]*excelModel.ExcelClassify)
+	for _, v := range classifies {
+		classifyMap[v.ExcelClassifyId] = v
+	}
+
+	// 查询所有excel
+	excels, e := excelModel.GetAllExcelInfoBySource(source)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取excel列表失败, %v", e)
+		return
+	}
+	if len(excels) == 0 {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "无分类需要修复"
+		return
+	}
+
+	// 先按照用户ID分组
+	groupExcels := make(map[int][]*excelModel.ExcelInfo)
+	for _, v := range excels {
+		//if v.SysUserId != 198 {
+		//	continue
+		//}
+		if groupExcels[v.SysUserId] == nil {
+			groupExcels[v.SysUserId] = make([]*excelModel.ExcelInfo, 0)
+		}
+		groupExcels[v.SysUserId] = append(groupExcels[v.SysUserId], v)
+	}
+
+	// 递归函数
+	findAndReplace := func(replaceId map[int]*excelModel.ExcelClassify, userId int, excelItem *excelModel.ExcelInfo, classifyItem *excelModel.ExcelClassify, firstStep bool) (hasParent, updateChild bool, currentClassify *excelModel.ExcelClassify, err error) {
+		// 分类创建人与表格不一致
+		currentClassify = classifyItem
+		if classifyItem.SysUserId != userId {
+			// 是否已存在替换分类
+			_, ok := replaceId[classifyItem.ExcelClassifyId]
+			if !ok {
+				classifyNew := &excelModel.ExcelClassify{
+					ExcelClassifyName: fmt.Sprintf("%d%s", userId, classifyItem.ExcelClassifyName),
+					ParentId:          classifyItem.ParentId, // 根据updateChild递归外更新
+					Source:            source,
+					SysUserId:         excelItem.SysUserId,
+					SysUserRealName:   excelItem.SysUserRealName,
+					Level:             classifyItem.Level,
+					UniqueCode:        utils.MD5(utils.EXCEL_DATA_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10)),
+					Sort:              classifyItem.Sort,
+					CreateTime:        time.Now(),
+					ModifyTime:        time.Now(),
+				}
+				_, e = excelModel.AddExcelClassify(classifyNew)
+				if e != nil {
+					err = fmt.Errorf("AddExcelClassify, %v", e)
+					return
+				}
+				replaceId[classifyItem.ExcelClassifyId] = classifyNew
+				excelItem.ExcelClassifyId = classifyNew.ExcelClassifyId
+				currentClassify = classifyNew
+				classifyMap[classifyNew.ExcelClassifyId] = classifyNew
+			} else {
+				excelItem.ExcelClassifyId = replaceId[classifyItem.ExcelClassifyId].ExcelClassifyId
+				currentClassify = replaceId[classifyItem.ExcelClassifyId]
+			}
+			// 仅第一次递归时修改
+			if firstStep {
+				//fmt.Printf("修改ExcelClassifyId: %d\n", excelItem.ExcelClassifyId)
+				excelItem.ModifyTime = time.Now().Local()
+				if e = excelItem.Update([]string{"ExcelClassifyId", "ModifyTime"}); e != nil {
+					err = fmt.Errorf("UpdateExcelInfo, %v", e)
+					return
+				}
+			}
+		}
+
+		// 查上级
+		if classifyItem.ParentId <= 0 {
+			//fmt.Printf("终止递归, ClassifyId: %d", classifyItem.ExcelClassifyId)
+			return
+		}
+		hasParent = true
+		parent := classifyMap[classifyItem.ParentId]
+		if parent == nil {
+			err = fmt.Errorf("未找到上级分类, ClassifyId: %d, ParentId: %d", classifyItem.ExcelClassifyId, classifyItem.ParentId)
+			return
+		}
+		if parent.SysUserId != userId {
+			updateChild = true
+		}
+		return
+	}
+
+	for uid, gv := range groupExcels {
+
+		replaceId := make(map[int]*excelModel.ExcelClassify)
+		for _, v := range gv {
+			classifyItem, e := excelModel.GetExcelClassifyById(v.ExcelClassifyId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("获取表格分类失败, ExcelClassifyId: %d, %v", v.ExcelClassifyId, e)
+				return
+			}
+			// 首次递归
+			hasParent, updateChild, currentClassify, e := findAndReplace(replaceId, uid, v, classifyItem, true)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("findAndReplace1, %v", e)
+				return
+			}
+			if !hasParent {
+				continue
+			}
+
+			// 二次递归
+			//fmt.Printf("二次递归: %d\n", currentClassify.ParentId)
+			parentClassify, e := excelModel.GetExcelClassifyById(currentClassify.ParentId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("2-parent-GetExcelClassifyById, %v", e)
+				return
+			}
+			hasParent2, updateChild2, currentClassify2, e := findAndReplace(replaceId, uid, v, parentClassify, false)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("findAndReplace2, %v", e)
+				return
+			}
+			// 更新上次递归的parent_id
+			if updateChild {
+				currentClassify.ParentId = currentClassify2.ExcelClassifyId
+				currentClassify.ModifyTime = time.Now().Local()
+				if e = currentClassify.Update([]string{"ParentId", "ModifyTime"}); e != nil {
+					br.Msg = "操作失败"
+					br.ErrMsg = fmt.Sprintf("2-更新父级分类ID失败, %v", e)
+					return
+				}
+			}
+			if !hasParent2 {
+				continue
+			}
+
+			// 三次递归
+			//fmt.Printf("三次递归: %d\n", currentClassify2.ParentId)
+			parentClassify2, e := excelModel.GetExcelClassifyById(currentClassify2.ParentId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("3-parent-GetExcelClassifyById, %v", e)
+				return
+			}
+			hasParent3, updateChild3, currentClassify3, e := findAndReplace(replaceId, uid, v, parentClassify2, false)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("findAndReplace3, %v", e)
+				return
+			}
+			if updateChild2 {
+				currentClassify2.ParentId = currentClassify3.ExcelClassifyId
+				currentClassify2.ModifyTime = time.Now().Local()
+				if e = currentClassify2.Update([]string{"ParentId", "ModifyTime"}); e != nil {
+					br.Msg = "操作失败"
+					br.ErrMsg = fmt.Sprintf("3-更新父级分类ID失败, %v", e)
+					return
+				}
+			}
+			if !hasParent3 {
+				continue
+			}
+
+			// 四次递归
+			//fmt.Printf("四次递归: %d\n", currentClassify3.ParentId)
+			parentClassify3, e := excelModel.GetExcelClassifyById(currentClassify3.ParentId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("4-parent-GetExcelClassifyById, %v", e)
+				return
+			}
+			hasParent4, updateChild4, currentClassify4, e := findAndReplace(replaceId, uid, v, parentClassify3, false)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("findAndReplace4, %v", e)
+				return
+			}
+			if updateChild3 {
+				currentClassify3.ParentId = currentClassify4.ExcelClassifyId
+				currentClassify3.ModifyTime = time.Now().Local()
+				if e = currentClassify3.Update([]string{"ParentId", "ModifyTime"}); e != nil {
+					br.Msg = "操作失败"
+					br.ErrMsg = fmt.Sprintf("4-更新父级分类ID失败, %v", e)
+					return
+				}
+			}
+			if !hasParent4 {
+				continue
+			}
+
+			// 五次递归-理论上没了(自定义分析最多三级目录+最底层的表格)
+			//fmt.Printf("五次递归: %d\n", currentClassify4.ParentId)
+			parentClassify4, e := excelModel.GetExcelClassifyById(currentClassify4.ParentId)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("5-parent-GetExcelClassifyById, %v", e)
+				return
+			}
+			hasParent5, _, currentClassify5, e := findAndReplace(replaceId, uid, v, parentClassify4, false)
+			if e != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("findAndReplace5, %v", e)
+				return
+			}
+			if updateChild4 {
+				currentClassify4.ParentId = currentClassify5.ExcelClassifyId
+				currentClassify4.ModifyTime = time.Now().Local()
+				if e = currentClassify4.Update([]string{"ParentId", "ModifyTime"}); e != nil {
+					br.Msg = "操作失败"
+					br.ErrMsg = fmt.Sprintf("5-更新父级分类ID失败, %v", e)
+					return
+				}
+			}
+			if !hasParent5 {
+				continue
+			}
+		}
+
+		//fmt.Println("replaceId: ", replaceId)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 44 - 34
controllers/data_manage/excel/excel_info.go

@@ -3,6 +3,7 @@ package excel
 import (
 	"archive/zip"
 	"encoding/json"
+	"errors"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
@@ -18,13 +19,14 @@ import (
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"github.com/shopspring/decimal"
-	"github.com/yidane/formula"
 	"io"
 	"os"
 	"path/filepath"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/dengsgo/math-engine/engine"
 )
 
 // ExcelInfoController ETA表格管理
@@ -728,7 +730,6 @@ func (c *ExcelInfoController) Detail() {
 		excelDetail.ExcelSourceEn = strings.Join(sourceNameEnList, ",")
 	}
 
-
 	// excel表格按钮权限
 	if excelDetail.Source != utils.BALANCE_TABLE {
 		excelDetail.Button = excel2.GetExcelInfoOpButton(sysUser, excelDetail.SysUserId, excelDetail.Source, excelDetail.HaveOperaAuth)
@@ -1745,32 +1746,42 @@ func (c *ExcelInfoController) Calculate() {
 		return
 	}
 
-	expression := formula.NewExpression(formulaFormStr)
-	calResult, err := expression.Evaluate()
+	//expression := formula.NewExpression(formulaFormStr)
+	//calResult, err := expression.Evaluate()
+	//if err != nil {
+	//	br.Msg = "计算失败"
+	//	br.ErrMsg = "计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr
+	//	// 分母为0的报错
+	//	if strings.Contains(err.Error(), "divide by zero") {
+	//		br.Msg = "分母不能为0"
+	//		br.ErrMsg = "分母不能为空,计算公式:" + formulaFormStr
+	//		br.IsSendEmail = false
+	//	}
+	//	return
+	//}
+	//// 如果计算结果是NAN,那么就提示报错
+	//if calResult.IsNan() {
+	//	br.Msg = "计算失败"
+	//	br.ErrMsg = "计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr
+	//	return
+	//}
+
+	calFloat, err := engine.ParseAndExec(formulaFormStr)
+	//calVal, err := calResult.Float64()
 	if err != nil {
-		br.Msg = "计算失败"
-		br.ErrMsg = "计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr
-		// 分母为0的报错
-		if strings.Contains(err.Error(), "divide by zero") {
-			br.Msg = "分母不能为0"
-			br.ErrMsg = "分母不能为空,计算公式:" + formulaFormStr
-			br.IsSendEmail = false
-		}
-		return
-	}
-	// 如果计算结果是NAN,那么就提示报错
-	if calResult.IsNan() {
-		br.Msg = "计算失败"
-		br.ErrMsg = "计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr
+		err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+		fmt.Println(err)
 		return
 	}
+	//nanCheck := fmt.Sprintf("%0.f", calVal)
+
 	// 计算结果格式化
-	calFloat, e := calResult.Float64()
-	if e != nil {
-		br.Msg = "计算失败"
-		br.ErrMsg = "计算失败, Err: " + e.Error()
-		return
-	}
+	//calFloat, e := calResult.Float64()
+	//if e != nil {
+	//	br.Msg = "计算失败"
+	//	br.ErrMsg = "计算失败, Err: " + e.Error()
+	//	return
+	//}
 	calVal := utils.FormatTableDataShowValue(calFloat)
 
 	//calVal := calResult.String()
@@ -1857,9 +1868,9 @@ func (c *ExcelInfoController) GetFirstEdbData() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = response.TableDataItem{
-		EdbInfoId: edbInfoId,
-		Data:      dataList,
-		ExcelSource: excelSource,
+		EdbInfoId:     edbInfoId,
+		Data:          dataList,
+		ExcelSource:   excelSource,
 		ExcelSourceEn: excelSourceEn,
 	}
 }
@@ -1936,9 +1947,9 @@ func (c *ExcelInfoController) GetOtherEdbData() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = response.TableDataItem{
-		EdbInfoId: req.EdbInfoId,
-		Data:      dataList,
-		ExcelSource: excelSource,
+		EdbInfoId:     req.EdbInfoId,
+		Data:          dataList,
+		ExcelSource:   excelSource,
 		ExcelSourceEn: excelSourceEn,
 	}
 }
@@ -2914,7 +2925,6 @@ func (c *ExcelInfoController) GetBatchChartRefreshResult() {
 	br.Success = true
 }
 
-
 // GetBatchChartRefreshResult
 // @Title 获取批量刷新表格结果
 // @Description 获取批量刷新表格结果
@@ -2935,7 +2945,7 @@ func (c *ExcelInfoController) GetEdbSource() {
 		return
 	}
 	edbInfoId, _ := c.GetInt("EdbInfoId")
-	if edbInfoId <= 0  {
+	if edbInfoId <= 0 {
 		br.Msg = "请选择指标"
 		br.ErrMsg = "请选择指标"
 		br.IsSendEmail = false
@@ -2951,7 +2961,7 @@ func (c *ExcelInfoController) GetEdbSource() {
 	excelSourceEn := strings.Join(sourceNameEnList, ",")
 
 	var resp struct {
-		ExcelSource string `description:"表格来源"`
+		ExcelSource   string `description:"表格来源"`
 		ExcelSourceEn string `description:"表格来源(英文)"`
 	}
 
@@ -2961,4 +2971,4 @@ func (c *ExcelInfoController) GetEdbSource() {
 	br.Data = resp
 	br.Ret = 200
 	br.Success = true
-}
+}

+ 2 - 1
controllers/data_manage/future_good/future_good_chart_info.go

@@ -15,13 +15,14 @@ import (
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"os"
 	"os/exec"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // FutureGoodChartInfoController 商品价格图表管理

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

@@ -13,10 +13,11 @@ import (
 	"eta/eta_api/services/data/line_equation"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // LineEquationChartInfoController 拟合方程图表管理

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

@@ -14,10 +14,11 @@ import (
 	lineFeatureServ "eta/eta_api/services/data/line_feature"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // LineFeaturesChartInfoController 统计特征图表管理

+ 1907 - 0
controllers/data_manage/manual_edb.go

@@ -0,0 +1,1907 @@
+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/alarm_msg"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/shopspring/decimal"
+	"github.com/tealeg/xlsx"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ManualEdbController 手工指标服务(鉴权)
+type ManualEdbController struct {
+	controllers.BaseAuthController
+}
+
+// TemplateDownload
+// @Title 下载模板
+// @Description 下载模板
+// @Success 200 {object} models.EdbdataClassifyResp
+// @Param   Source   query   int  false       "来源:1:模板1;2:模板2"
+// @router /template [get]
+func (c *ManualEdbController) TemplateDownload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	source, _ := c.GetInt("Source")
+	switch source {
+	case 2:
+		c.Ctx.Output.Download("./static/template/导入模板2.xlsx", "数据导入模板2.xlsx")
+	default:
+		c.Ctx.Output.Download("./static/template/导入模板1.xlsx", "数据导入模板1.xlsx")
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "下载成功"
+}
+
+// EdbDetail
+// @Title 指标列表
+// @Description 指标列表
+// @Param   TradeCode   query   string  true       "指标编码"
+// @Success 200 {object} models.TargetDetailResp
+// @router /target/edb/detail [get]
+func (c *ManualEdbController) EdbDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	// 指标编码
+	tradeCode := c.GetString("TradeCode")
+	if tradeCode == `` {
+		br.Msg = "请输入指标编码"
+		br.IsSendEmail = false
+		return
+	}
+
+	manualEdbInfo, err := models.GetTargetByTradeCode(tradeCode)
+	if err != nil {
+		br.Msg = "获取指标失败"
+		br.ErrMsg = "获取指标失败,err:" + err.Error()
+		return
+	}
+
+	// 如果不是超管账号,那么得校验下当前用户是否有该指标的权限
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		//获取账户所拥有权限的分类id集合
+		count, err := models.GetCountManualUserClassify(sysUser.AdminId, manualEdbInfo.ClassifyId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.ErrMsg = "获取分类数据失败,err:" + err.Error()
+			return
+		}
+
+		if count <= 0 {
+			br.Msg = "无权访问"
+			br.ErrMsg = "无权访问"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	classifyList := make([]*models.EdbdataClassify, 0)
+	{
+		classify, err := models.GetManualClassifyByClassifyId(manualEdbInfo.ClassifyId)
+		if err != nil {
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取分类信息失败,err:" + err.Error()
+			return
+		}
+		parentClassify, err := models.GetManualClassifyByClassifyId(classify.ParentId)
+		if err != nil {
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取父级分类信息失败,err:" + err.Error()
+			return
+		}
+		classifyList = []*models.EdbdataClassify{
+			parentClassify, classify,
+		}
+	}
+
+	// 明细数据
+	{
+		dataList, err := models.GetEdbDataListByCode(manualEdbInfo.TradeCode)
+		if err != nil {
+			br.Msg = "获取明细数据失败"
+			br.ErrMsg = "获取明细数据失败,err:" + err.Error()
+			return
+		}
+
+		manualEdbInfo.DataList = dataList
+	}
+
+	resp := models.TargetDetailResp{
+		Detail:       manualEdbInfo,
+		ClassifyList: classifyList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	//br.Data = resp
+	br.Data = resp
+}
+
+// ClassifyEdbList
+// @Title 分类指标列表
+// @Description 指标列表
+// @Param   ClassifyId   query   string  true       "分类id"
+// @Success 200 {object} models.TargetDetailResp
+// @router /target/classify/edb/list [get]
+func (c *ManualEdbController) ClassifyEdbList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.Ret = 408
+		return
+	}
+	resp := new(models.EdbdataClassifyResp)
+
+	classifyId, _ := c.GetInt("ClassifyId") //分类
+	if classifyId <= 0 {
+		br.Msg = "请传入分类"
+		br.ErrMsg = "请传入分类"
+		br.IsSendEmail = false
+	}
+
+	var condition string
+	var pars []interface{}
+
+	// 如果不是超管账号,那么得校验下当前用户是否有该指标的权限
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		//获取账户所拥有权限的分类id集合
+		count, err := models.GetCountManualUserClassify(sysUser.AdminId, classifyId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.ErrMsg = "获取分类数据失败,err:" + err.Error()
+			return
+		}
+
+		if count <= 0 {
+			br.Msg = "无权访问"
+			br.ErrMsg = "无权访问"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	list := make([]*models.EdbdataClassifyList, 0)
+
+	condition += ` AND a.classify_id = ? `
+	pars = append(pars, classifyId)
+
+	tmpList, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	for _, v := range tmpList {
+		list = append(list, &models.EdbdataClassifyList{
+			ClassifyId:   0,
+			ClassifyName: v.SecName,
+			ParentId:     v.ClassifyId,
+			Child:        nil,
+			TradeCode:    v.TradeCode,
+			UniqueCode:   utils.MD5(v.TradeCode),
+		})
+	}
+
+	resp.List = list
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EdbSearch
+// @Title 指标搜索
+// @Description 指标搜索
+// @Param   Keyword   query   string  false       "关键字搜索"
+// @Success 200 {object} models.TargetItemListResp
+// @router /target/edb/search [get]
+func (c *ManualEdbController) EdbSearch() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	//关键字
+	keyword := c.GetString("Keyword")
+
+	var condition string
+	var pars []interface{}
+
+	userId := sysUser.AdminId
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyList, err := models.GetEdbdataClassify(int64(userId))
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+
+		num := len(classifyList)
+		if num > 0 {
+			classifyIdList := make([]int, 0)
+			for _, v := range classifyList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	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 {
+		br.Msg = "获取失败"
+		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)
+	}
+
+	resp := models.EdbListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EdbList
+// @Title 获取研究员指标
+// @Description 获取研究员指标
+// @Param   Frequency   query   string  false       "频度;枚举值:日度、周度、月度、季度、半年度、年度"
+// @Param   Keyword   query   string  false       "关键字搜索"
+// @Param   TradeCode   query   string  false       "指标唯一编码"
+// @Param   ClassifyId   query   int  false       "分类id"
+// @Param   UserId   query   int  false       "用户id"
+// @Param   EdbShowType   query   string  true       "指标展示类型:0,1,2; 0:不管有没有数据,指标都展示出来, 1:只展示有数据的指标, 2:只展示没有数据的指标"
+// @Success 200 {object} models.TargetItemListResp
+// @router /target/edb/list [get]
+func (c *ManualEdbController) EdbList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 频度
+	frequency := c.GetString("Frequency")
+	//关键字
+	keyword := c.GetString("Keyword")
+	//关键字
+	classifyId, _ := c.GetInt("ClassifyId", 0)
+	//用户ID
+	userId, _ := c.GetInt("UserId", 0)
+
+	var condition string
+	var pars []interface{}
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyList, err := models.GetEdbdataClassify(int64(sysUser.AdminId))
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+
+		num := len(classifyList)
+		if num > 0 {
+			classifyIdList := make([]int, 0)
+			for _, v := range classifyList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	// 关键词
+	if keyword != "" {
+		condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+	}
+	if frequency != "" {
+		condition += ` AND a.frequency = ?`
+		pars = append(pars, frequency)
+	}
+
+	// 所属分类
+	if classifyId > 0 {
+		condition += ` AND a.classify_id = ? `
+		pars = append(pars, classifyId)
+	}
+
+	// 所属用户
+	if userId > 0 {
+		condition += ` AND a.user_id = ? `
+		pars = append(pars, userId)
+	}
+
+	total, err := models.GetCountEdbInfoList(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		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
+	}
+
+	resp := models.EdbListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// EditExcelData
+// @Title 根据excel的样式去编辑指标
+// @Description 根据excel的样式去编辑指标
+// @Param  request	body models.ManualEdbExcelStyleEditReq true "type json string"
+// @Success 200
+// @router /target/edb/excel_style/edit [post]
+func (c *ManualEdbController) EditExcelData() {
+	br := new(models.BaseResponse).Init()
+	var err error
+	errs := make([]string, 0)
+	defer func() {
+		if len(errs) > 0 {
+			utils.FileLog.Info("编辑EXCEL数据 新增或修改数据失败,Err:" + strings.Join(errs, "\n"))
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	var req data_manage.ManualEdbExcelStyleEditReq
+	err = json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.TradeCode == `` {
+		br.Msg = "指标编码异常!"
+		br.ErrMsg = "指标编码异常!"
+		return
+	}
+	if req.Unit == `` {
+		br.Msg = "单位填写异常!"
+		br.ErrMsg = "单位填写异常!"
+		return
+	}
+	if req.Frequency == `` {
+		br.Msg = "频度填写异常!"
+		br.ErrMsg = "频度填写异常!"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "品种填写异常!"
+		br.ErrMsg = "品种填写异常!"
+		return
+	}
+
+	// 获取指标信息
+	manualEdbInfo, err := models.GetEdbinfoByTradeCode(req.TradeCode)
+	if err != nil {
+		br.Msg = `找不到该指标`
+		br.Msg = `找不到该指标,ERR:` + err.Error()
+		return
+	}
+
+	// 如果不是超管账号,那么得校验下当前用户是否有该指标的权限
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		//获取账户所拥有权限的分类id集合
+		count, err := models.GetCountManualUserClassify(sysUser.AdminId, manualEdbInfo.ClassifyId)
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.ErrMsg = "获取分类数据失败,err:" + err.Error()
+			return
+		}
+
+		if count <= 0 {
+			br.Msg = "无权访问"
+			br.ErrMsg = "无权访问"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	dateValueMap := make(map[string]string)
+	//取到所有数据
+	for _, dateValue := range req.Data {
+		dateValueMap[dateValue.Date] = strconv.FormatFloat(dateValue.Value, 'f', -1, 64)
+	}
+
+	//操作指标,新增指标及数据等
+	{
+		var isUpdate bool
+
+		secName := strings.TrimSpace(req.EdbName)
+		frequency := strings.TrimSpace(req.Frequency)
+		unit := strings.TrimSpace(req.Unit)
+
+		// 指标基础信息变更
+		{
+			updateCols := make([]string, 0)
+
+			if manualEdbInfo.SecName != secName {
+				updateCols = append(updateCols, "SecName")
+				manualEdbInfo.SecName = secName
+			}
+
+			if manualEdbInfo.ClassifyId != req.ClassifyId {
+				updateCols = append(updateCols, "ClassifyId")
+				manualEdbInfo.ClassifyId = req.ClassifyId
+			}
+
+			if manualEdbInfo.Frequency != frequency {
+				updateCols = append(updateCols, "Frequency")
+				manualEdbInfo.Frequency = frequency
+			}
+
+			if manualEdbInfo.Unit != unit {
+				updateCols = append(updateCols, "Unit")
+				manualEdbInfo.Unit = unit
+			}
+
+			if len(updateCols) > 0 {
+				isUpdate = true
+				err = manualEdbInfo.Update(updateCols)
+				if err != nil {
+					br.Msg = `指标保存失败`
+					br.Msg = `指标保存失败,ERR:` + err.Error()
+					return
+				}
+			}
+		}
+
+		// 当前已经存在的指标明细数据
+		targetDataList, tmpErr := models.GetTargetsDataList(manualEdbInfo.TradeCode)
+		if tmpErr != nil {
+			err = tmpErr
+		}
+		existDataMap := make(map[string]string)
+		deleteDataMap := make(map[string]string)
+		for _, tmpData := range targetDataList {
+			existDataMap[tmpData.Dt] = tmpData.Close
+			deleteDataMap[tmpData.Dt] = tmpData.Dt
+		}
+
+		addDataList := make([]*models.Edbdata, 0)
+
+		for createDate, closeVal := range dateValueMap {
+			if createDate == "" || closeVal == "" {
+				continue
+			}
+			//判断数据是否已经存在
+			tmpVal, ok3 := existDataMap[createDate]
+			// 不存在,那么后面进行插入
+			if !ok3 {
+				addDataList = append(addDataList, &models.Edbdata{
+					TradeCode:  manualEdbInfo.TradeCode,
+					Dt:         createDate,
+					Close:      closeVal,
+					ModifyTime: time.Now(),
+				})
+				continue
+			}
+
+			delete(deleteDataMap, createDate)
+
+			// 库里面的数据
+			tmpValDecimal, tmpErr := decimal.NewFromString(tmpVal)
+			if tmpErr != nil {
+				fmt.Println("tmpVal Parse err:", tmpErr.Error())
+				err = tmpErr
+				continue
+			}
+			// 用户填写的数据
+			closeValDecimal, tmpErr := decimal.NewFromString(closeVal)
+			if tmpErr != nil {
+				fmt.Println("closeVal Parse err:", tmpErr.Error())
+				err = tmpErr
+				continue
+			}
+			if !tmpValDecimal.Equal(closeValDecimal) {
+				isUpdate = true
+				fmt.Println("更新数值")
+				err = models.ModifyTargetsDataByImport(manualEdbInfo.TradeCode, createDate, closeVal)
+				if err != nil {
+					fmt.Println("ModifyTargetsDataByImport err:", err.Error())
+					errs = append(errs, err.Error())
+					//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+				}
+			}
+		}
+
+		// 新增指标
+		if len(addDataList) > 0 {
+			isUpdate = true
+			err = models.OnlyMultiAddEdbdata(addDataList)
+			if err != nil {
+				fmt.Println("MultiAddEdbdata err:", err.Error())
+				errs = append(errs, err.Error())
+			}
+		}
+
+		// 删除不需要的日期数据
+		num := len(deleteDataMap)
+		if num > 0 {
+			isUpdate = true
+			deleteDateList := make([]string, 0)
+			for date := range deleteDataMap {
+				deleteDateList = append(deleteDateList, date)
+			}
+
+			err = models.DelEdbdataByCodeAndDateList(manualEdbInfo.TradeCode, deleteDateList)
+			if err != nil {
+				fmt.Println("DelEdbdataByCodeAndDateList err:", err.Error())
+				errs = append(errs, err.Error())
+			}
+		}
+
+		// 添加记录
+		if isUpdate {
+			record := &models.EdbinfoOpRecord{
+				TradeCode:  manualEdbInfo.TradeCode,
+				Remark:     "编辑指标",
+				UserId:     sysUser.AdminId,
+				UserName:   sysUser.RealName,
+				CreateTime: time.Now(),
+			}
+			go func() {
+				_ = record.Create()
+			}()
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+}
+
+// BatchEdbList
+// @Title 获取研究员指标
+// @Description 获取研究员指标
+// @Param	request	body models.BatchAddEdbReq true "type json string"
+// @Success 200 {object} models.TargetItemListResp
+// @router /target/edb/batch/list [post]
+func (c *ManualEdbController) BatchEdbList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	var req models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	pageSize := req.PageSize
+	currentIndex := req.CurrentIndex
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	// 是否加到指标库
+	condition += ` AND a.is_join_edb = ? `
+	pars = append(pars, 0)
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyList, err := models.GetEdbdataClassify(int64(sysUser.AdminId))
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+
+		num := len(classifyList)
+		if num > 0 {
+			classifyIdList := make([]int, 0)
+			for _, v := range classifyList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	// 关键词
+	if req.Keyword != "" {
+		condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+		pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+	}
+
+	// 所属分类
+	lenFrequency := len(req.FrequencyList)
+	if lenFrequency > 0 {
+		condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+		pars = append(pars, req.FrequencyList)
+	}
+
+	// 所属分类
+	lenClassify := len(req.ClassifyIdList)
+	if lenClassify > 0 {
+		condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+		pars = append(pars, req.ClassifyIdList)
+	}
+
+	// 所属用户
+	lenUser := len(req.UserIdList)
+	if lenUser > 0 {
+		condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+		pars = append(pars, req.UserIdList)
+	}
+
+	total, err := models.GetCountEdbInfoList(condition, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		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
+	}
+
+	resp := models.EdbListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+	br.IsAddLog = false
+}
+
+// BatchAddEdbCheck
+// @Title 新增校验
+// @Description 新增校验
+// @Param	request	body data_manage.BatchManualEdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/add/check [post]
+func (c *ManualEdbController) BatchAddEdbCheck() {
+	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
+	}
+
+	// 最大批量添加的数量
+	codeMax := 30
+
+	var req models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	//
+	condition += ` AND a.is_join_edb = 0 `
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyList, err := models.GetEdbdataClassify(int64(sysUser.AdminId))
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+
+		num := len(classifyList)
+		if num > 0 {
+			classifyIdList := make([]int, 0)
+			for _, v := range classifyList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	if req.ListAll {
+		// 关键词
+		if req.Keyword != "" {
+			condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+			pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+		}
+
+		// 所属分类
+		lenFrequency := len(req.FrequencyList)
+		if lenFrequency > 0 {
+			condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+			pars = append(pars, req.FrequencyList)
+		}
+
+		// 所属分类
+		lenClassify := len(req.ClassifyIdList)
+		if lenClassify > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+			pars = append(pars, req.ClassifyIdList)
+		}
+
+		// 所属用户
+		lenUser := len(req.UserIdList)
+		if lenUser > 0 {
+			condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+			pars = append(pars, req.UserIdList)
+		}
+
+		lenTradeList := len(req.TradeCodeList)
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE not in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	} else {
+		lenTradeList := len(req.TradeCodeList)
+		if lenTradeList <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if len(list) > codeMax {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMax)
+		return
+	}
+
+	br.Data = list
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// NameCheck
+// @Title 重名校验
+// @Description 批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/add/name_check [post]
+func (c *ManualEdbController) NameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	type NameCheckResult struct {
+		EdbCode string
+		EdbName string
+		Exist   bool
+	}
+	indexNames := make([]string, 0)
+	resp := make([]*NameCheckResult, 0)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &NameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+	}
+
+	// 重名校验
+	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
+}
+
+// BatchAdd2Edb
+// @Title 批量新增到指标库
+// @Description 批量新增到指标库
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/add [post]
+func (c *ManualEdbController) BatchAdd2Edb() {
+	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
+	}
+
+	// 最大批量添加的数量
+	codeMax := 30
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_MANUAL_" + 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(c.Ctx.Input.RequestBody)
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > codeMax {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	indexNames := make([]string, 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)
+		v.Frequency = strings.TrimSpace(v.Frequency)
+		if v.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		v.Unit = strings.TrimSpace(v.Unit)
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+	}
+
+	// 限定同一时间最多批量新增30个指标
+	for _, v := range req {
+		var r data.ManualIndexSource2EdbReq
+		r.EdbCode = v.EdbCode
+		r.EdbName = v.EdbName
+		r.Frequency = v.Frequency
+		r.Unit = v.Unit
+		r.ClassifyId = v.ClassifyId
+		r.AdminId = sysUser.AdminId
+		r.AdminRealName = sysUser.RealName
+
+		edbInfo, e, errMsg, skip := data.ManualIndexSource2Edb(r, c.Lang)
+		if e != nil {
+			br.Msg = "操作失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = e.Error()
+			return
+		}
+		if skip {
+			continue
+		}
+
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(c.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = c.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// BatchDelEdbCheck
+// @Title 新增校验
+// @Description 新增校验
+// @Param	request	body data_manage.BatchManualEdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/del/check [post]
+func (c *ManualEdbController) BatchDelEdbCheck() {
+	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 models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	// 就是为了找出已经加入指标库的指标
+	condition += ` AND a.is_join_edb = ? `
+	pars = append(pars, 1)
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyList, err := models.GetEdbdataClassify(int64(sysUser.AdminId))
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+
+		num := len(classifyList)
+		if num > 0 {
+			classifyIdList := make([]int, 0)
+			for _, v := range classifyList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	if req.ListAll {
+		// 关键词
+		if req.Keyword != "" {
+			condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+			pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+		}
+
+		// 所属分类
+		lenFrequency := len(req.FrequencyList)
+		if lenFrequency > 0 {
+			condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+			pars = append(pars, req.FrequencyList)
+		}
+
+		// 所属分类
+		lenClassify := len(req.ClassifyIdList)
+		if lenClassify > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+			pars = append(pars, req.ClassifyIdList)
+		}
+
+		// 所属用户
+		lenUser := len(req.UserIdList)
+		if lenUser > 0 {
+			condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+			pars = append(pars, req.UserIdList)
+		}
+
+		lenTradeList := len(req.TradeCodeList)
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE not in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	} else {
+		lenTradeList := len(req.TradeCodeList)
+		if lenTradeList <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Data = list
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// BatchDel
+// @Title 批量删除
+// @Description 批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /target/edb/batch/del [post]
+func (c *ManualEdbController) BatchDel() {
+	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
+	}
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_MANUAL_" + 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(c.Ctx.Input.RequestBody)
+		return
+	}
+	var req models.BatchManualEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+
+	req.Keyword = strings.TrimSpace(req.Keyword)
+
+	var condition string
+	var pars []interface{}
+
+	// 就是为了找出已经加入指标库的指标
+	condition += ` AND a.is_join_edb = ? `
+	pars = append(pars, 0)
+
+	//超管账号可以查看分类下的所有频度数据
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN {
+		classifyList, err := models.GetEdbdataClassify(int64(sysUser.AdminId))
+		if err != nil {
+			br.Msg = "获取分类数据失败"
+			br.Msg = "获取拥有的分类数据失败,Err:" + err.Error()
+			return
+		}
+
+		num := len(classifyList)
+		if num > 0 {
+			classifyIdList := make([]int, 0)
+			for _, v := range classifyList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
+			pars = append(pars, classifyIdList)
+		}
+
+	}
+
+	if req.ListAll {
+		// 关键词
+		if req.Keyword != "" {
+			condition += ` AND (a.SEC_NAME like ?  or a.TRADE_CODE like ? )`
+			pars = utils.GetLikeKeywordPars(pars, req.Keyword, 2)
+		}
+
+		// 所属分类
+		lenFrequency := len(req.FrequencyList)
+		if lenFrequency > 0 {
+			condition += ` AND a.frequency in (` + utils.GetOrmInReplace(lenFrequency) + `) `
+			pars = append(pars, req.FrequencyList)
+		}
+
+		// 所属分类
+		lenClassify := len(req.ClassifyIdList)
+		if lenClassify > 0 {
+			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(lenClassify) + `) `
+			pars = append(pars, req.ClassifyIdList)
+		}
+
+		// 所属用户
+		lenUser := len(req.UserIdList)
+		if lenUser > 0 {
+			condition += ` AND a.user_id in (` + utils.GetOrmInReplace(lenUser) + `) `
+			pars = append(pars, req.UserIdList)
+		}
+
+		lenTradeList := len(req.TradeCodeList)
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE not in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	} else {
+		lenTradeList := len(req.TradeCodeList)
+		if lenTradeList <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		if lenTradeList > 0 {
+			condition += ` AND a.TRADE_CODE in (` + utils.GetOrmInReplace(lenTradeList) + `) `
+			pars = append(pars, req.TradeCodeList)
+		}
+	}
+
+	list, err := models.GetEdbInfoList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// TODO 二次校验指标是否已经加到指标库
+	{
+		codeList := make([]string, 0)
+		// 限定同一时间最多批量新增30个指标
+		for _, v := range list {
+			codeList = append(codeList, v.TradeCode)
+		}
+		err = models.DelManualIndexByCodeList(codeList)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	br.Msg = "删除成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// ImportData
+// @Title 导入数据
+// @Description 导入数据
+// @Param   EntryFile   query   file  true       "文件"
+// @Success 200 Ret=200 录入成功
+// @router /import/data [post]
+func (c *ManualEdbController) ImportData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+	file, _, err := c.GetFile("EntryFile")
+	if err != nil {
+		br.Msg = "获取文件失败"
+		br.ErrMsg = "获取文件失败,Err:" + err.Error()
+		return
+	}
+	path := "./static/数据导入_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	defer file.Close()
+	err = c.SaveToFile("EntryFile", path)
+	if err != nil {
+		br.Msg = "文件保存失败"
+		br.ErrMsg = "文件保存失败,Err:" + err.Error()
+		return
+	}
+	if utils.RunMode == "debug" {
+		defer os.Remove(path)
+	}
+	xlFile, err := xlsx.OpenFile(path)
+	if err != nil {
+		fmt.Println(err.Error())
+		return
+	}
+
+	//超管账号可以查看分类下的所有频度数据
+	userId := sysUser.AdminId
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
+		userId = 0
+	}
+	//获取账户所拥有权限的分类id集合
+	classifyNameStrList, edbDataClassifyMap, err := data.GetEdbClassifyNameListByAdminId(int64(userId))
+	if err != nil {
+		br.Msg = "获取分类数据失败"
+		return
+	}
+
+	//导入成功数量
+	successCount := 0
+	//导入失败数据
+	failDatas := make([]*models.EdbdataImportFail, 0)
+
+	//指标map
+	targetMap := make(map[string]*models.Edbinfo)
+	defer func() {
+		for _, target := range targetMap {
+			//结束后,清除掉对应的缓存
+			key := "import:edbinfo:data:" + target.TradeCode
+			utils.Rc.Delete(key)
+
+			//将该指标的code加入到 “手工数据导入后刷新” 缓存
+			if utils.Re == nil {
+				err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, target.TradeCode)
+				if err != nil {
+					fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+				}
+			}
+		}
+	}()
+
+	// 所有的指标数据map
+	edbCodeDataMap := make(map[string]map[string]string)
+
+	// 遍历sheet页读取
+	for _, sheet := range xlFile.Sheets {
+		fmt.Println("sheet name: ", sheet.Name)
+		//遍历行读取
+		maxRow := sheet.MaxRow
+		fmt.Println("maxRow:", maxRow)
+		fmt.Println("maxRow")
+		for i := 0; i < maxRow; i++ {
+			if i == 1 {
+				row := sheet.Row(i)
+				cells := row.Cells
+				if len(cells) < 6 {
+					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
+					br.Msg = "导入文件异常,请下载最新导入模板文件"
+					return
+				}
+				templateFail := false
+				if cells[0].Value != "品种分类" && cells[0].Value != "Species Category" {
+					templateFail = true
+				}
+				if cells[1].Value != "录入日期" && cells[1].Value != "Input Date" {
+					templateFail = true
+				}
+				if cells[2].Value != "指标名称" && cells[2].Value != "Indicator Name Indicator Name" {
+					templateFail = true
+				}
+				if cells[3].Value != "值" && cells[3].Value != "Value" {
+					templateFail = true
+				}
+				if cells[4].Value != "频度" && cells[4].Value != "Frequency" {
+					templateFail = true
+				}
+				if cells[5].Value != "单位" && cells[5].Value != "Unit" {
+					templateFail = true
+				}
+				if templateFail {
+					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
+					br.Msg = "导入文件异常,请下载最新导入模板文件"
+					return
+				}
+			}
+			if i > 1 {
+				row := sheet.Row(i)
+				cells := row.Cells
+				if len(cells) >= 6 {
+					classifyName := cells[0].Value //分类
+					if classifyName == "" {        //过滤空白行
+						continue
+					}
+					cell1 := cells[1].Value
+					//createDate := utils.ConvertToFormatDay(cell1) //录入日期
+					createDate := cell1
+					cell2 := cells[2].Value //指标名称
+					secName := utils.TrimStr(cell2)
+					frequency := cells[4].Value //频度
+					unit := cells[5].Value      //单位
+
+					closeVal := cells[3].Value //值
+					if strings.Contains(closeVal, "#N/A") {
+						continue
+					}
+
+					//校验表格中的日期格式
+					if strings.Contains(createDate, "-") {
+						//如果是带有 - 的普通日期格式文本
+						_, timeErr := time.Parse("2006-1-2", createDate)
+						if timeErr != nil {
+							failItem := new(models.EdbdataImportFail)
+							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+							failItem.ClassifyName = classifyName
+							failItem.CreateDate = createDate
+							failItem.SecName = secName
+							failItem.Close = closeVal
+							failItem.Remark = "日期格式异常"
+							failItem.Frequency = frequency
+							failItem.Unit = unit
+							failDatas = append(failDatas, failItem)
+							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+							continue
+						}
+					} else if strings.Contains(createDate, "/") {
+						//如果是带有 / 的普通日期格式文本
+						createDateTime, timeErr := time.Parse("2006/1/2", createDate)
+						if timeErr != nil {
+							failItem := new(models.EdbdataImportFail)
+							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+							failItem.ClassifyName = classifyName
+							failItem.CreateDate = createDate
+							failItem.SecName = secName
+							failItem.Close = closeVal
+							failItem.Remark = "日期格式异常"
+							failItem.Frequency = frequency
+							failItem.Unit = unit
+							failDatas = append(failDatas, failItem)
+							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+							continue
+						}
+						createDate = createDateTime.Format("2006-01-02")
+					} else {
+						//可能是excel的日期格式
+						_, tmpErr := strconv.Atoi(createDate)
+						if tmpErr != nil {
+							failItem := new(models.EdbdataImportFail)
+							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+							failItem.ClassifyName = classifyName
+							failItem.CreateDate = createDate
+							failItem.SecName = secName
+							failItem.Close = closeVal
+							failItem.Remark = "日期格式异常"
+							failItem.Frequency = frequency
+							failItem.Unit = unit
+							failDatas = append(failDatas, failItem)
+							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+							continue
+						}
+						createDate = utils.ConvertToFormatDay(createDate) //录入日期
+					}
+
+					closeValFloat, err := cells[3].Float() //值
+
+					if err != nil {
+						failItem := new(models.EdbdataImportFail)
+						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+						failItem.ClassifyName = classifyName
+						failItem.CreateDate = createDate
+						failItem.SecName = secName
+						failItem.Close = cells[3].Value
+						failItem.Remark = "值类型异常"
+						failItem.Frequency = frequency
+						failItem.Unit = unit
+						failDatas = append(failDatas, failItem)
+						continue
+					}
+					newDecimal := decimal.NewFromFloat(closeValFloat)
+					newDecimal.Round(4)
+					closeVal = newDecimal.String()
+					if strings.Contains(closeVal, "#N/A") {
+						continue
+					}
+					//closeVal := closeValFloat.
+
+					//判断指标,类型,等数据是否正常
+					classifyName = strings.Trim(classifyName, " ")
+					frequency = utils.TrimStr(frequency)
+					unit = utils.TrimStr(unit)
+
+					// 成功数量超过20个指标,那就不导入了
+					if len(targetMap) >= 150 {
+						failItem := new(models.EdbdataImportFail)
+						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+						failItem.ClassifyName = classifyName
+						failItem.CreateDate = createDate
+						failItem.SecName = secName
+						failItem.Close = closeVal
+						failItem.Remark = "导入指标数量过多"
+						failItem.Frequency = frequency
+						failItem.Unit = unit
+						failDatas = append(failDatas, failItem)
+						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+						continue
+					}
+
+					if !strings.Contains(strings.Join(classifyNameStrList, ","), classifyName) {
+						failItem := new(models.EdbdataImportFail)
+						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+						failItem.ClassifyName = classifyName
+						failItem.CreateDate = createDate
+						failItem.SecName = secName
+						failItem.Close = closeVal
+						failItem.Remark = "没有该品种分类权限"
+						failItem.Frequency = frequency
+						failItem.Unit = unit
+						failDatas = append(failDatas, failItem)
+						continue
+					}
+					//fmt.Println(classifyName, createDate, secName, closeVal)
+
+					//获取指标分类信息
+					classify, ok := edbDataClassifyMap[classifyName]
+					if !ok {
+						failItem := new(models.EdbdataImportFail)
+						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+						failItem.ClassifyName = classifyName
+						failItem.CreateDate = createDate
+						failItem.SecName = secName
+						failItem.Close = closeVal
+						failItem.Remark = "指标分类不存在"
+						failItem.Frequency = frequency
+						failItem.Unit = unit
+						failDatas = append(failDatas, failItem)
+						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+						continue
+					}
+
+					//判断指标是否存在
+					target, ok := targetMap[secName]
+					if !ok {
+						tmpTarget, err := models.GetTargetBySecName(secName)
+						if err != nil {
+							//如果是找不到该指标,那么新增指标
+							if err.Error() == utils.ErrNoRow() {
+								if frequency == "" {
+									failItem := new(models.EdbdataImportFail)
+									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+									failItem.ClassifyName = classifyName
+									failItem.CreateDate = createDate
+									failItem.SecName = secName
+									failItem.Close = closeVal
+									failItem.Remark = "新增指标失败,频度字段为空"
+									failItem.Frequency = frequency
+									failItem.Unit = unit
+									failDatas = append(failDatas, failItem)
+									continue
+								}
+								if unit == "" {
+									failItem := new(models.EdbdataImportFail)
+									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+									failItem.ClassifyName = classifyName
+									failItem.CreateDate = createDate
+									failItem.SecName = secName
+									failItem.Close = closeVal
+									failItem.Remark = "新增指标失败,单位字段为空"
+									failItem.Frequency = frequency
+									failItem.Unit = unit
+									failDatas = append(failDatas, failItem)
+									continue
+								}
+								tmpErr := data.AddEdbInfo(secName, unit, frequency, "", sysUser.Mobile, classify.ClassifyId, sysUser.AdminId)
+								if tmpErr != nil {
+									fmt.Println("line 158")
+									failItem := new(models.EdbdataImportFail)
+									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+									failItem.ClassifyName = classifyName
+									failItem.CreateDate = createDate
+									failItem.SecName = secName
+									failItem.Close = closeVal
+									failItem.Remark = "新增指标失败"
+									failItem.Frequency = frequency
+									failItem.Unit = unit
+									failDatas = append(failDatas, failItem)
+									continue
+								}
+								tmpTarget, tmpErr := models.GetTargetBySecName(secName)
+								target = tmpTarget
+								targetMap[secName] = target
+							} else {
+								fmt.Println("导入数据 获取指标:Err:" + err.Error())
+							}
+						} else {
+							target = tmpTarget
+							targetMap[secName] = target
+						}
+
+						//设置10分钟缓存,不允许其他地方删除
+						key := "import:edbinfo:data:" + target.TradeCode
+						utils.Rc.SetNX(key, 1, time.Second*600)
+					}
+
+					if target == nil {
+						fmt.Println("指标不存在")
+						failItem := new(models.EdbdataImportFail)
+						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+						failItem.ClassifyName = classifyName
+						failItem.CreateDate = createDate
+						failItem.SecName = secName
+						failItem.Close = closeVal
+						failItem.Remark = "指标不存在"
+						failItem.Frequency = frequency
+						failItem.Unit = unit
+						failDatas = append(failDatas, failItem)
+						continue
+					}
+
+					//更新指标信息
+					updateCols := make([]string, 0)
+					//更新指标分类
+					if target.ClassifyId <= 0 && classify.ClassifyId > 0 {
+						target.ClassifyId = classify.ClassifyId
+						updateCols = append(updateCols, "ClassifyId")
+					}
+					if target.Frequency != frequency {
+						target.Frequency = frequency
+						target.NoticeTime = ""
+						updateCols = append(updateCols, "Frequency", "NoticeTime")
+					}
+					if target.Unit != unit {
+						target.Unit = unit
+						updateCols = append(updateCols, "Unit")
+					}
+					if len(updateCols) > 0 {
+						_ = target.Update(updateCols)
+					}
+
+					// 判断指标数据列表是否已经存在
+					tmpDataMap, ok := edbCodeDataMap[target.TradeCode]
+					if !ok {
+						tmpDataMap = make(map[string]string)
+						dataList, tmpErr := models.GetTargetsDataList(target.TradeCode)
+						if tmpErr != nil {
+							go alarm_msg.SendAlarmMsg("导入数据"+target.TradeCode+" 获取指标的数据失败,Err:"+err.Error(), 3)
+						}
+						for _, tmpData := range dataList {
+							tmpDataMap[tmpData.Dt] = tmpData.Close
+						}
+						edbCodeDataMap[target.TradeCode] = tmpDataMap
+					}
+
+					//判断数据是否已经存在
+					tmpVal, ok := tmpDataMap[createDate]
+					//数据已存在,进行更新操作
+					if ok {
+						if tmpVal != closeVal {
+							err = models.ModifyTargetsDataByImport(target.TradeCode, createDate, closeVal)
+							if err != nil {
+								go alarm_msg.SendAlarmMsg("导入数据 修改数据失败,Err:"+err.Error(), 3)
+								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+							}
+						}
+					} else { //数据不存在,进行新增操作
+						if target.TradeCode != "" && createDate != "" && closeVal != "" {
+							models.AddTargetsDataByImport(target.TradeCode, createDate, closeVal)
+							if err != nil {
+								go alarm_msg.SendAlarmMsg("导入数据 新增数据失败,Err:"+err.Error(), 3)
+								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+							}
+							tmpDataMap[createDate] = closeVal
+							edbCodeDataMap[target.TradeCode] = tmpDataMap
+						}
+					}
+					successCount++
+				} else {
+					//br.ErrMsg = "请填写导入内容"
+					//br.Msg = "请填写导入内容"
+					//return
+				}
+			}
+		}
+	}
+	resp := models.EdbdataImportResp{
+		SuccessCount: successCount,
+		FailCount:    len(failDatas),
+	}
+	fmt.Println("failDatas:", len(failDatas))
+	if len(failDatas) > 0 {
+		//先删除导入失败记录
+		_ = models.DelEdbDataImportFail(sysUser.AdminId)
+		//错误信息字符串切片,最后作为发送邮件通知使用
+		failContents := make([]string, 0)
+		for _, v := range failDatas {
+			failContents = append(failContents, fmt.Sprint(v.SecName, "导入失败:", v.Remark))
+			err = models.AddEdbdataImportFail(v)
+			if err != nil {
+				go alarm_msg.SendAlarmMsg("导入数据 新增失败记录失败,Err:"+err.Error(), 3)
+				//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增失败记录失败:Err:"+err.Error(), utils.EmailSendToUsers)
+			}
+		}
+		//导入失败的话,最后统一邮件提醒就好啦,不需要每次都去提醒
+		go alarm_msg.SendAlarmMsg("导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), 3)
+		//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), utils.EmailSendToUsers)
+
+		if successCount == 0 {
+			resp.Status = -1
+			resp.Msg = "导入失败"
+		} else {
+			resp.Status = 1
+			resp.Msg = "存在部分导入失败"
+		}
+	} else {
+		resp.Status = 0
+	}
+	br.Msg = "导入成功"
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+}
+
+// RecordList
+// @Title 获取指标操作记录列表
+// @Description 获取指标操作记录列表
+// @Param   TradeCode   query   string  false       "指标编码"
+// @Success 200 {object} models.EdbinfoOpRecordListResp
+// @router /target/edb/op/record/list [get]
+func (c *ManualEdbController) RecordList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 频度
+	tradeCode := c.GetString("TradeCode")
+	if tradeCode == `` {
+		br.Msg = `请选择指标`
+		br.ErrMsg = `请选择指标`
+		br.IsSendEmail = false
+		return
+	}
+
+	total, list, err := models.GetEdbinfoOpRecordPageList(tradeCode, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	resp := models.EdbinfoOpRecordListResp{
+		List:   list,
+		Paging: paging.GetPaging(currentIndex, pageSize, total),
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 6 - 0
controllers/data_manage/multiple_graph_config.go

@@ -634,6 +634,12 @@ func (this *ChartInfoController) MultipleGraphConfigSaveChart() {
 		_ = utils.Rc.Delete(cacheKey)
 	}()
 
+	if req.MultipleGraphConfigId <= 0 {
+		br.Msg = "配置未保存"
+		br.IsSendEmail = false
+		return
+	}
+
 	if req.IsSaveAs {
 		CopyMultipleGraphConfigSaveChart(req, this, br, sysUser)
 		return

+ 13 - 2
controllers/data_manage/predict_edb_info.go

@@ -13,12 +13,13 @@ import (
 	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"github.com/shopspring/decimal"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/shopspring/decimal"
 )
 
 // PredictEdbInfoController 预测指标
@@ -771,6 +772,16 @@ func (this *PredictEdbInfoController) Edit() {
 	//修改es
 	data.AddOrEditEdbInfoToEs(resp.EdbInfoId)
 
+	// 添加操作日志
+	newEdbRecord := new(data_manage.EdbInfoEditRecord)
+	newEdbRecord.EdbName = req.EdbName
+	// 频度和单位在此接口不处理
+	newEdbRecord.Frequency = edbInfo.Frequency
+	newEdbRecord.Unit = edbInfo.Unit
+	newEdbRecord.OperateUserId = sysUser.AdminId
+	newEdbRecord.OperateUserRealName = sysUser.RealName
+	go data.AddEditEdbInfoRcord(edbInfo, newEdbRecord)
+
 	// 刷新关联指标
 	go data.EdbInfoRefreshAllFromBaseV2(resp.EdbInfoId, true, false)
 

+ 817 - 0
controllers/data_manage/range_analysis/chart_classify.go

@@ -0,0 +1,817 @@
+package range_analysis
+
+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"
+	"eta/eta_api/services/data/data_manage_permission"
+	"eta/eta_api/utils"
+	"fmt"
+	"sort"
+	"time"
+)
+
+// RangeChartClassifyController 	区间分析图表
+type RangeChartClassifyController struct {
+	controllers.BaseAuthController
+}
+
+// ChartClassifyList
+// @Title 区间分析图表分类列表
+// @Description 区间分析图表分类列表接口
+// @Param   IsShowMe   query   bool  false       "是否只看我的,true、false"
+// @Param   ParentId   query   bool  false       "父级ID"
+// @Param   Source   query   int  false       "图表类型,3:相关性,4:滚动相关性"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /chart_classify/list [get]
+func (this *RangeChartClassifyController) ChartClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	resp := new(data_manage.ChartClassifyListResp)
+
+	// 获取当前账号的不可见指标
+	//noPermissionChartIdMap := make(map[int]bool)
+	//{
+	//	obj := data_manage.EdbInfoNoPermissionAdmin{}
+	//	confList, err := obj.GetAllChartListByAdminId(this.SysUser.AdminId)
+	//	if err != nil && err.Error() != utils.ErrNoRow() {
+	//		br.Msg = "获取失败"
+	//		br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
+	//		return
+	//	}
+	//	for _, v := range confList {
+	//		noPermissionChartIdMap[v.ChartInfoId] = true
+	//	}
+	//}
+
+	isShowMe, _ := this.GetBool("IsShowMe")
+	parentId, _ := this.GetInt("ParentId")
+	source, _ := this.GetInt("Source", utils.CHART_SOURCE_RANGE_ANALYSIS)
+
+	nodeAll := make([]*data_manage.ChartClassifyItems, 0)
+	// 查询分类节点
+	rootList, err := data_manage.GetChartClassifyByParentId(parentId, utils.CHART_SOURCE_RANGE_ANALYSIS)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	if len(rootList) > 0 {
+		permissionClassifyIdList, e := data_manage_permission.GetUserChartClassifyPermissionList(this.SysUser.AdminId, 0)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取已授权分类id数据失败,Err:" + e.Error()
+			return
+		}
+
+		for _, v := range rootList {
+			// 操作按钮权限
+			v.HaveOperaAuth = data_manage_permission.CheckChartClassifyPermissionByPermissionIdList(v.IsJoinPermission, v.ChartClassifyId, permissionClassifyIdList)
+			button := data.GetChartClassifyOpButton(this.SysUser, v.SysUserId, v.HaveOperaAuth)
+			v.Button = button
+			v.ParentId = parentId
+			v.Children = make([]*data_manage.ChartClassifyItems, 0)
+
+			nodeAll = append(nodeAll, v)
+		}
+	}
+
+	// 查询图表节点, ParentId=0时说明仅查询一级目录节点
+	if parentId > 0 {
+		// 查询当前分类信息
+		currClassify, e := data_manage.GetChartClassifyById(parentId)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取当前分类信息失败,Err:" + e.Error()
+			return
+		}
+
+		// 获取所有有权限的指标和分类
+		permissionEdbIdList, permissionClassifyIdList, e := data_manage_permission.GetUserChartAndClassifyPermissionList(this.SysUser.AdminId, 0, 0)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + e.Error()
+			return
+		}
+
+		var adminId int
+		if isShowMe {
+			adminId = this.SysUser.AdminId
+		}
+
+		charts, e := data_manage.GetChartInfoBySourceAndParentId(source, parentId, adminId)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取图表信息失败, Err: %v", e)
+			return
+		}
+		for _, v := range charts {
+			// 操作按钮权限
+			v.HaveOperaAuth = data_manage_permission.CheckChartPermissionByPermissionIdList(v.IsJoinPermission, currClassify.IsJoinPermission, v.ChartInfoId, v.ChartClassifyId, permissionEdbIdList, permissionClassifyIdList)
+			button := data.GetChartOpButton(this.SysUser, v.SysUserId, v.HaveOperaAuth)
+			button.AddButton = false //不管有没有权限,图表都是没有添加按钮的
+			v.Button = button
+			v.ParentId = parentId
+			v.Children = make([]*data_manage.ChartClassifyItems, 0)
+
+			nodeAll = append(nodeAll, v)
+		}
+	}
+
+	// 整体排序
+	if len(nodeAll) > 0 {
+		sort.Slice(nodeAll, func(i, j int) bool {
+			return nodeAll[i].Sort < nodeAll[j].Sort
+		})
+	}
+
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// getChartClassifyListForMe 获取我创建的图表
+func getChartClassifyListForMe(adminInfo system.Admin, resp *data_manage.ChartClassifyListResp) (errMsg string, err error) {
+	rootList, err := data_manage.GetChartClassifyByParentId(0, utils.CHART_SOURCE_RANGE_ANALYSIS)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	classifyAll, err := data_manage.GetChartClassifyAll(utils.CHART_SOURCE_RANGE_ANALYSIS)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	allChartInfo, err := data_manage.GetChartInfoByAdminId([]int{utils.CHART_SOURCE_RANGE_ANALYSIS, utils.CHART_SOURCE_RANGE_ANALYSIS}, adminInfo.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	chartInfoMap := make(map[int][]*data_manage.ChartClassifyItems)
+	for _, v := range allChartInfo {
+		chartInfoMap[v.ChartClassifyId] = append(chartInfoMap[v.ChartClassifyId], v)
+	}
+	rootChildMap := make(map[int][]*data_manage.ChartClassifyItems)
+	for _, v := range classifyAll {
+		rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
+		if existItems, ok := chartInfoMap[v.ChartClassifyId]; ok {
+			v.Children = existItems
+		} else {
+			items := make([]*data_manage.ChartClassifyItems, 0)
+			v.Children = items
+		}
+	}
+	nodeAll := make([]*data_manage.ChartClassifyItems, 0)
+	for _, v := range rootList {
+		if existItems, ok := rootChildMap[v.ChartClassifyId]; ok {
+			v.Children = existItems
+		} else {
+			items := make([]*data_manage.ChartClassifyItems, 0)
+			v.Children = items
+		}
+		nodeAll = append(nodeAll, v)
+	}
+	resp.AllNodes = nodeAll
+
+	return
+}
+
+// ChartClassifyItems
+// @Title 获取所有区间分析图表分类接口-不包含图表
+// @Description 获取所有区间分析图表分类接口-不包含图表
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /chart_classify/items [get]
+func (this *RangeChartClassifyController) ChartClassifyItems() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	rootList, err := data_manage.GetChartClassifyByParentId(0, utils.CHART_SOURCE_RANGE_ANALYSIS)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	nodeAll := make([]*data_manage.ChartClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		nodeAll = append(nodeAll, rootNode)
+	}
+	resp := new(data_manage.ChartClassifyListResp)
+	resp.AllNodes = nodeAll
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// AddChartClassify
+// @Title 新增区间分析图表分类
+// @Description 新增区间分析图表分类接口
+// @Param	request	body data_manage.AddChartClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /chart_classify/add [post]
+func (this *RangeChartClassifyController) AddChartClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req data_manage.AddChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ChartClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 新增图表分类
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_RANGE_ANALYSIS, this.Lang, this.SysUser)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = "添加分类失败,Err:" + err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "添加成功"
+	br.Success = true
+}
+
+// EditChartClassify
+// @Title 修改区间分析图表分类
+// @Description 修改区间分析图表分类接口
+// @Param	request	body data_manage.EditChartClassifyReq true "type json string"
+// @Success 200 Ret=200 修改成功
+// @router /chart_classify/edit [post]
+func (this *RangeChartClassifyController) EditChartClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req data_manage.EditChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ChartClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.ChartClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 编辑图表分类
+	_, err, errMsg, isSendEmail := data.EditChartClassify(req.ChartClassifyId, utils.CHART_SOURCE_RANGE_ANALYSIS, req.ChartClassifyName, this.Lang, this.SysUser)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = "保存分类失败,Err:" + err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "修改成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// DeleteChartClassifyCheck
+// @Title 删除图表检测接口
+// @Description 删除图表检测接口
+// @Param	request	body data_manage.ChartClassifyDeleteCheckResp true "type json string"
+// @Success 200 Ret=200 检测成功
+// @router /chart_classify/delete/check [post]
+func (this *RangeChartClassifyController) DeleteChartClassifyCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req data_manage.ChartClassifyDeleteCheckReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ChartClassifyId < 0 && req.ChartInfoId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+	var deleteStatus int
+	var tipsMsg string
+	//删除分类
+	if req.ChartClassifyId > 0 && req.ChartInfoId == 0 {
+		//判断区间分析图表分类下,是否含有图表
+		count, err := data_manage.GetChartInfoCountByClassifyId(req.ChartClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有图表失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			deleteStatus = 1
+			tipsMsg = "该分类下关联图表不可删除"
+		}
+	}
+
+	if deleteStatus != 1 && req.ChartInfoId == 0 {
+		classifyCount, err := data_manage.GetChartClassifyCountByClassifyId(req.ChartClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有图表失败,Err:" + err.Error()
+			return
+		}
+		if classifyCount > 0 {
+			deleteStatus = 2
+			tipsMsg = "确认删除当前目录及包含的子目录吗"
+		}
+	}
+	if deleteStatus == 0 {
+		tipsMsg = "可删除,进行删除操作"
+	}
+
+	resp := new(data_manage.ChartClassifyDeleteCheckResp)
+	resp.DeleteStatus = deleteStatus
+	resp.TipsMsg = tipsMsg
+	br.Ret = 200
+	br.Msg = "检测成功"
+	br.Success = true
+	br.Data = resp
+}
+
+// DeleteChartClassify
+// @Title 删除区间分析图表分类/图表
+// @Description 删除区间分析图表分类/图表接口
+// @Param	request	body data_manage.DeleteChartClassifyReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /chart_classify/delete [post]
+func (this *RangeChartClassifyController) DeleteChartClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req data_manage.DeleteChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ChartClassifyId < 0 && req.ChartInfoId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	//删除分类
+	if req.ChartClassifyId > 0 && req.ChartInfoId == 0 {
+		//判断是否含有指标
+		count, err := data_manage.GetChartInfoCountByClassifyId(req.ChartClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			br.Msg = "该目录下存在关联指标,不可删除"
+			br.IsSendEmail = false
+			return
+		}
+
+		err = data_manage.DeleteChartClassify(req.ChartClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+	}
+	resp := new(data_manage.AddChartInfoResp)
+	//删除图表
+	if req.ChartInfoId > 0 {
+		chartInfo, err := data_manage.GetChartInfoById(req.ChartInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "图表已删除,请刷新页面"
+				br.ErrMsg = "指标不存在,Err:" + err.Error()
+				return
+			} else {
+				br.Msg = "删除失败"
+				br.ErrMsg = "删除失败,获取指标信息失败,Err:" + err.Error()
+				return
+			}
+		}
+		if chartInfo == nil {
+			br.Msg = "图表已删除,请刷新页面"
+			return
+		}
+		//图表操作权限
+		ok := data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, true)
+		if !ok {
+			br.Msg = "没有该图表的操作权限"
+			br.ErrMsg = "没有该图表的操作权限"
+			return
+		}
+
+		// 获取引用该图表的MyCharts, 用于ES删除
+		var myCond string
+		var myPars []interface{}
+		myCond += ` AND a.chart_info_id = ? `
+		myPars = append(myPars, chartInfo.ChartInfoId)
+		myCharts, e := data_manage.GetMyChartListGroupByCharyInfoIdAndAdminIdByCondition(myCond, myPars)
+		if e != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "获取引用图表的MyChats失败, Err: " + e.Error()
+			return
+		}
+		myIds := make([]int, 0)
+		for _, m := range myCharts {
+			myIds = append(myIds, m.MyChartId)
+		}
+
+		source := chartInfo.Source // 区间分析图表(滚动相关性)
+		//删除图表及关联指标
+		err = data_manage.DeleteChartInfoAndData(chartInfo.ChartInfoId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+
+		// 删除图表系列
+		chartSeriesOb := new(data_manage.FactorEdbSeriesChartMapping)
+		seriesMappingItem, e := chartSeriesOb.GetItemByChartInfoId(chartInfo.ChartInfoId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+			} else {
+				br.Msg = "删除失败"
+				br.ErrMsg = "获取图表关联失败, Err: " + e.Error()
+				return
+			}
+		} else {
+			factorSeriesOb := new(data_manage.FactorEdbSeries)
+			e = factorSeriesOb.RemoveSeriesAndMappingByFactorEdbSeriesId(seriesMappingItem)
+			if e != nil {
+				br.Msg = "删除失败"
+				br.ErrMsg = "获取图表关联失败, Err: " + e.Error()
+				return
+			}
+		}
+		//删除ES
+		{
+			go data.EsDeleteChartInfo(chartInfo.ChartInfoId)
+			// 删除MY ETA 图表 es数据
+			//go data.EsDeleteMyChartInfoByChartInfoId(chartInfo.ChartInfoId)
+			go data.EsDeleteMyChartInfoByMyChartIds(myIds)
+		}
+
+		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)
+
+		nextItem, err := data_manage.GetChartInfoByCondition(condition, pars)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "获取下一级图库信息失败,Err:" + err.Error()
+			return
+		}
+
+		if nextItem != nil {
+			resp.UniqueCode = nextItem.UniqueCode
+			resp.ChartInfoId = nextItem.ChartInfoId
+		} else {
+			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)
+
+			classifyItem, err := data_manage.GetChartClassifyByCondition(condition, pars)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "删除失败"
+				br.ErrMsg = "获取下一级图库分类信息失败,Err:" + err.Error()
+				return
+			}
+			if classifyItem != nil {
+				nextItem, err = data_manage.GetNextChartInfo(chartInfo.ChartClassifyId)
+				if err != nil && err.Error() != utils.ErrNoRow() {
+					br.Msg = "删除失败"
+					br.ErrMsg = "获取下一级图库信息失败,Err:" + err.Error()
+					return
+				}
+				if nextItem != nil {
+					resp.UniqueCode = nextItem.UniqueCode
+					resp.ChartInfoId = nextItem.ChartInfoId
+				}
+			}
+		}
+		//新增操作日志
+		{
+			chartLog := new(data_manage.ChartInfoLog)
+			chartLog.ChartName = chartInfo.ChartName
+			chartLog.ChartInfoId = req.ChartInfoId
+			chartLog.ChartClassifyId = chartInfo.ChartClassifyId
+			chartLog.SysUserId = sysUser.AdminId
+			chartLog.SysUserRealName = sysUser.RealName
+			chartLog.UniqueCode = chartInfo.UniqueCode
+			chartLog.CreateTime = time.Now()
+			chartLog.Content = string(this.Ctx.Input.RequestBody)
+			chartLog.Status = "删除图表"
+			chartLog.Method = this.Ctx.Input.URI()
+			go data_manage.AddChartInfoLog(chartLog)
+		}
+	}
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// ChartClassifyMove
+// @Title 区间分析图表分类移动接口
+// @Description 区间分析图表分类移动接口
+// @Success 200 {object} data_manage.MoveChartClassifyReq
+// @router /chart_classify/move [post]
+func (this *RangeChartClassifyController) ChartClassifyMove() {
+	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.MoveChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyId <= 0 && req.ChartInfoId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "请选择拖动目标,分类目录或者指标"
+		return
+	}
+
+	err, errMsg := data.MoveChartClassify(req, sysUser, utils.CHART_SOURCE_RANGE_ANALYSIS)
+	if errMsg != `` {
+		br.Msg = errMsg
+		br.ErrMsg = errMsg
+		if err != nil {
+			br.ErrMsg = err.Error()
+		} else {
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	//if req.ClassifyId <= 0 {
+	//	br.Msg = "参数错误"
+	//	br.ErrMsg = "分类id小于等于0"
+	//	return
+	//}
+	//
+	////判断分类是否存在
+	//chartClassifyInfo, err := data_manage.GetChartClassifyById(req.ClassifyId)
+	//if err != nil {
+	//	br.Msg = "移动失败"
+	//	br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+	//	return
+	//}
+	//if chartClassifyInfo.Source != utils.CHART_SOURCE_RANGE_ANALYSIS {
+	//	br.Msg = "分类异常"
+	//	br.ErrMsg = "分类异常,不是区间分析图表的分类"
+	//	return
+	//}
+	//updateCol := make([]string, 0)
+	//
+	////判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+	//if chartClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
+	//	parentChartClassifyInfo, err := data_manage.GetChartClassifyById(req.ParentClassifyId)
+	//	if err != nil {
+	//		br.Msg = "移动失败"
+	//		br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+	//		return
+	//	}
+	//	chartClassifyInfo.ParentId = parentChartClassifyInfo.ChartClassifyId
+	//	chartClassifyInfo.Level = parentChartClassifyInfo.Level + 1
+	//	chartClassifyInfo.ModifyTime = time.Now()
+	//	updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+	//}
+	//
+	////如果有传入 上一个兄弟节点分类id
+	//if req.PrevClassifyId > 0 {
+	//	//上一个兄弟节点
+	//	prevClassify, err := data_manage.GetChartClassifyById(req.PrevClassifyId)
+	//	if err != nil {
+	//		br.Msg = "移动失败"
+	//		br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+	//		return
+	//	}
+	//
+	//	//如果是移动在两个兄弟节点之间
+	//	if req.NextClassifyId > 0 {
+	//		//下一个兄弟节点
+	//		nextClassify, err := data_manage.GetChartClassifyById(req.NextClassifyId)
+	//		if err != nil {
+	//			br.Msg = "移动失败"
+	//			br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+	//			return
+	//		}
+	//		//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+	//		if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == chartClassifyInfo.Sort {
+	//			//变更兄弟节点的排序
+	//			updateSortStr := `sort + 2`
+	//			_ = data_manage.UpdateChartClassifySortByParentId(prevClassify.ParentId, prevClassify.ChartClassifyId, prevClassify.Sort, updateSortStr)
+	//		} else {
+	//			//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+	//			if nextClassify.Sort-prevClassify.Sort == 1 {
+	//				//变更兄弟节点的排序
+	//				updateSortStr := `sort + 1`
+	//				_ = data_manage.UpdateChartClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+	//			}
+	//		}
+	//	}
+	//
+	//	chartClassifyInfo.Sort = prevClassify.Sort + 1
+	//	chartClassifyInfo.ModifyTime = time.Now()
+	//	updateCol = append(updateCol, "Sort", "ModifyTime")
+	//
+	//} else {
+	//	firstClassify, err := data_manage.GetFirstChartClassifyByParentId(chartClassifyInfo.ParentId)
+	//	if err != nil && err.Error() != utils.ErrNoRow() {
+	//		br.Msg = "移动失败"
+	//		br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+	//		return
+	//	}
+	//
+	//	//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+	//	if firstClassify != nil && firstClassify.Sort == 0 {
+	//		updateSortStr := ` sort + 1 `
+	//		_ = data_manage.UpdateChartClassifySortByParentId(firstClassify.ParentId, firstClassify.ChartClassifyId-1, 0, updateSortStr)
+	//	}
+	//
+	//	chartClassifyInfo.Sort = 0 //那就是排在第一位
+	//	chartClassifyInfo.ModifyTime = time.Now()
+	//	updateCol = append(updateCol, "Sort", "ModifyTime")
+	//}
+	//
+	////更新
+	//if len(updateCol) > 0 {
+	//	err = chartClassifyInfo.Update(updateCol)
+	//	if err != nil {
+	//		br.Msg = "移动失败"
+	//		br.ErrMsg = "修改失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}
+
+// ClassifyTree
+// @Title 多层分类列表树
+// @Description 多层分类列表树
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /chart_classify/tree [get]
+func (this *RangeChartClassifyController) ClassifyTree() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	allList, err := data_manage.GetChartClassifyAllBySource(utils.CHART_SOURCE_RANGE_ANALYSIS)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取所有分类失败, Err:" + err.Error()
+		return
+	}
+	nodeAll := make([]*data_manage.ChartClassifyItems, 0)
+
+	if len(allList) > 0 {
+		// 已授权分类id
+		permissionClassifyIdList, e := data_manage_permission.GetUserChartClassifyPermissionList(this.SysUser.AdminId, 0)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取已授权分类id数据失败,Err:" + e.Error()
+			return
+		}
+
+		for k, v := range allList {
+			// 数据权限
+			v.HaveOperaAuth = data_manage_permission.CheckChartClassifyPermissionByPermissionIdList(v.IsJoinPermission, v.ChartClassifyId, permissionClassifyIdList)
+			// 按钮权限
+			button := data.GetChartClassifyOpButton(this.SysUser, v.SysUserId, v.HaveOperaAuth)
+			allList[k].Button = button
+		}
+
+		nodeAll = data.GetChartClassifyTreeRecursive(allList, 0)
+		//根据sort值排序
+		sort.Slice(nodeAll, func(i, j int) bool {
+			return nodeAll[i].Sort < nodeAll[j].Sort
+		})
+	}
+
+	language := `CN`
+	// 显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.ChartLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.ChartLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	// 是否允许添加一级分类
+	canOpClassify := true
+	button := data.GetChartClassifyOpButton(this.SysUser, 0, true)
+	if !button.AddButton {
+		canOpClassify = false
+	}
+
+	resp := new(data_manage.ChartClassifyListResp)
+	resp.AllNodes = nodeAll
+	resp.Language = language
+	resp.CanOpClassify = canOpClassify
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 1302 - 0
controllers/data_manage/range_analysis/chart_info.go

@@ -0,0 +1,1302 @@
+package range_analysis
+
+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"
+	"eta/eta_api/services/data/data_manage_permission"
+	rangeServ "eta/eta_api/services/data/range_analysis"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// RangeChartChartInfoController 区间计算图表管理
+type RangeChartChartInfoController struct {
+	controllers.BaseAuthController
+}
+
+// Preview
+// @Title 区间计算图表-预览数据
+// @Description 区间计算图表-获取预览数据
+// @Param   StartDate	query	string	true	"自定义开始日期"
+// @Param   EndDate		query	string	true	"自定义结束日期"
+// @Param   LeadValue	query	int		true	"领先值"
+// @Param   LeadUnit	query	string	true	"领先单位:年,天,月,季,周"
+// @Success 200 {object} data_manage.ChartEdbInfoDetailResp
+// @router /chart_info/preview [post]
+func (this *RangeChartChartInfoController) Preview() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.ChartRangeAnalysisPreviewReq
+	if err := json.Unmarshal(this.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	// 基础校验
+	if len(req.EdbInfoIds) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	// todo 校验时间区间配置
+	switch req.ExtraConf.DateRangeType {
+	case 0:
+	case 1:
+		if len(req.ExtraConf.ManualDateConf) == 0 {
+			br.Msg = "请选择时间区间"
+			return
+		}
+		// 先按开始时间排序
+		sort.Sort(data_manage.ChartRangeAnalysisManualDateConfList(req.ExtraConf.ManualDateConf))
+		// 校验日期
+		// 1.如果截止时间小于指标的截止日期,需要重置为指标的截止日期
+		// 2.时间区间不能重叠
+		for i := 1; i < len(req.ExtraConf.ManualDateConf); i++ {
+			start1, err := time.Parse(utils.FormatDate, req.ExtraConf.ManualDateConf[i-1].EndDate)
+			if err != nil {
+				br.Msg = "截止日期格式有误"
+				return
+			}
+			start2, err := time.Parse(utils.FormatDate, req.ExtraConf.ManualDateConf[i].EndDate)
+			if err != nil {
+				br.Msg = "截止日期格式有误"
+				return
+			}
+
+			start3, err := time.Parse(utils.FormatDate, req.ExtraConf.ManualDateConf[i].StartDate)
+			if err != nil {
+				br.Msg = "截止日期格式有误"
+				return
+			}
+			// 如果当前区间的开始时间小于等于前一个区间的结束时间,则存在重叠
+			if !start2.After(start1) || start3.Before(start1) {
+				br.Msg = "日期区间存在重叠"
+				return
+			}
+		}
+		//如果截止时间大于指标的截止日期,需要重置为指标的截止日期
+	case 2:
+		if req.ExtraConf.YearDateConf.StartDay == "" || req.ExtraConf.YearDateConf.EndDay == "" {
+			br.Msg = "请选择时间区间"
+			return
+		}
+		if _, e := time.Parse(utils.FormatMonthDay, req.ExtraConf.YearDateConf.StartDay); e != nil {
+			br.Msg = "开始日期格式有误"
+			return
+		}
+		if _, e := time.Parse(utils.FormatMonthDay, req.ExtraConf.YearDateConf.EndDay); e != nil {
+			br.Msg = "结束日期格式有误"
+			return
+		}
+	}
+
+	// 获取指标信息
+	//chartInfo.CorrelationLeadUnit = req.LeadUnit
+	edbInfoMappingList := make([]*data_manage.ChartEdbInfoMapping, 0)
+	for _, v := range req.EdbInfoIds {
+		edbInfoMapping, e := data_manage.GetChartEdbMappingByEdbInfoId(v)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取区间计算图表, A指标mapping信息失败, Err:" + e.Error()
+			return
+		}
+		edbInfoMappingList = append(edbInfoMappingList, edbInfoMapping)
+	}
+
+	chartInfo := new(data_manage.ChartInfoView)
+	chartInfo.ChartType = 1
+
+	// 获取图表x轴y轴
+	edbList, xEdbIdValue, dataResp, e := rangeServ.GetChartDataByEdbInfoList(0, "", "", edbInfoMappingList, &req.ExtraConf)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取区间计算图表, 图表计算值失败, Err:" + e.Error()
+		return
+	}
+
+	resp := new(data_manage.ChartInfoDetailResp)
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.DataResp = dataResp
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增图表接口
+// @Description 新增图表接口
+// @Param	request	body data_manage.AddChartInfoReq true "type json string"
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /chart_info/add [post]
+func (this *RangeChartChartInfoController) Add() {
+	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_CHART_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.AddChartInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	chartInfo, err, errMsg, isSendEmail := rangeServ.AddChartInfo(req, utils.CHART_SOURCE_RANGE_ANALYSIS, sysUser, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	//新增操作日志
+	{
+		chartLog := new(data_manage.ChartInfoLog)
+		chartLog.ChartInfoId = chartInfo.ChartInfoId
+		chartLog.ChartName = req.ChartName
+		chartLog.ChartClassifyId = req.ChartClassifyId
+		chartLog.SysUserId = sysUser.AdminId
+		chartLog.SysUserRealName = sysUser.RealName
+		chartLog.UniqueCode = chartInfo.UniqueCode
+		chartLog.CreateTime = time.Now()
+		chartLog.Content = string(this.Ctx.Input.RequestBody)
+		chartLog.Status = "新增区间计算图表"
+		chartLog.Method = this.Ctx.Input.URI()
+		go data_manage.AddChartInfoLog(chartLog)
+	}
+
+	resp := new(data_manage.AddChartInfoResp)
+	resp.ChartInfoId = chartInfo.ChartInfoId
+	resp.UniqueCode = chartInfo.UniqueCode
+	resp.ChartType = chartInfo.ChartType
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// Edit
+// @Title 编辑图表接口
+// @Description 编辑图表接口
+// @Param	request	body data_manage.EditChartInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /chart_info/edit [post]
+func (this *RangeChartChartInfoController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req data_manage.EditChartInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	chartItem, err, errMsg, isSendEmail := rangeServ.EditChartInfo(req, sysUser, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	resp := new(data_manage.AddChartInfoResp)
+	resp.ChartInfoId = chartItem.ChartInfoId
+	resp.UniqueCode = chartItem.UniqueCode
+	resp.ChartType = req.ChartType
+
+	//新增操作日志
+	{
+		chartLog := new(data_manage.ChartInfoLog)
+		chartLog.ChartName = chartItem.ChartName
+		chartLog.ChartInfoId = req.ChartInfoId
+		chartLog.ChartClassifyId = chartItem.ChartClassifyId
+		chartLog.SysUserId = sysUser.AdminId
+		chartLog.SysUserRealName = sysUser.RealName
+		chartLog.UniqueCode = chartItem.UniqueCode
+		chartLog.CreateTime = time.Now()
+		chartLog.Content = string(this.Ctx.Input.RequestBody)
+		chartLog.Status = "编辑区间计算图表"
+		chartLog.Method = this.Ctx.Input.URL()
+		go data_manage.AddChartInfoLog(chartLog)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// Detail
+// @Title 获取图表详情
+// @Description 获取图表详情接口
+// @Param   ChartInfoId   query   int  true       "图表id"
+// @Param   DateType   query   int  true       "日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"
+// @Param   StartDate   query   string  true       "自定义开始日期"
+// @Param   EndDate   query   string  true       "自定义结束日期"
+// @Param   Calendar   query   string  true       "公历/农历"
+// @Param   SeasonStartDate   query   string  true       "季节性图开始日期"
+// @Param   SeasonEndDate   query   string  true       "季节性图结束日期"
+// @Param   EdbInfoId   query   string  true       "指标ID,多个用英文逗号隔开"
+// @Param   ChartType   query   int  true       "生成样式:1:曲线图,2:季节性图"
+// @Success 200 {object} data_manage.ChartInfoDetailResp
+// @router /chart_info/detail [get]
+func (this *RangeChartChartInfoController) Detail() {
+	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
+	}
+	chartInfoId, _ := this.GetInt("ChartInfoId")
+	if chartInfoId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	dateType, _ := this.GetInt("DateType")
+	fmt.Println("dateType:", dateType)
+
+	startDate := this.GetString("StartDate")
+	endDate := this.GetString("EndDate")
+	startYear, _ := this.GetInt("StartYear")
+
+	var err error
+	chartInfo := new(data_manage.ChartInfoView)
+	chartInfo, err = data_manage.GetChartInfoViewById(chartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "图被删除,请刷新页面"
+			br.ErrMsg = "图被删除,请刷新页面,Err:" + err.Error()
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+	if dateType <= 0 {
+		dateType = chartInfo.DateType
+	}
+	// 获取主题样式
+	chartTheme, err := data.GetChartThemeConfig(chartInfo.ChartThemeId, utils.CHART_SOURCE_DEFAULT, 1)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取主题信息失败,Err:" + err.Error()
+		return
+	}
+	chartInfo.ChartThemeStyle = chartTheme.Config
+	chartInfo.ChartThemeId = chartTheme.ChartThemeId
+
+	// 获取指标信息
+	//chartInfo.CorrelationLeadUnit = req.LeadUnit
+	edbInfoMappingList, err := data_manage.GetChartEdbMappingList(chartInfoId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	// 获取图表数据
+	if len(edbInfoMappingList) == 0 {
+		br.Msg = "获取失败"
+		br.ErrMsg = "图表没有指标,无法计算"
+		return
+	}
+	yearMax := 0
+
+	if dateType == utils.DateTypeNYears {
+		for _, v := range edbInfoMappingList {
+			if v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	// 开始/结束日期
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
+	// 区间计算图表配置校验
+	var extraConfig data_manage.ChartRangeAnalysisExtraConf
+	if chartInfo.ExtraConfig == `` {
+		br.Msg = "配置信息错误"
+		return
+	}
+	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConfig)
+	if err != nil {
+		br.Msg = "配置信息错误"
+		br.ErrMsg = "图表配置信息错误,Err:" + err.Error()
+		return
+	}
+
+	// 获取图表x轴y轴
+	// todo 日期处理,
+	edbList, xEdbIdValue, dataResp, e := rangeServ.GetChartDataByEdbInfoList(chartInfoId, "", "", edbInfoMappingList, &extraConfig)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取区间计算图表, 图表计算值失败, Err:" + e.Error()
+		return
+	}
+
+	// 判断是否加入我的图库
+	if chartInfoId > 0 && chartInfo != nil {
+		{
+			var myChartCondition string
+			var myChartPars []interface{}
+			myChartCondition += ` AND a.admin_id=? `
+			myChartPars = append(myChartPars, sysUser.AdminId)
+			myChartCondition += ` AND a.chart_info_id=? `
+			myChartPars = append(myChartPars, chartInfo.ChartInfoId)
+
+			myChartList, err := data_manage.GetMyChartByCondition(myChartCondition, myChartPars)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+				return
+			}
+			if myChartList != nil && len(myChartList) > 0 {
+				chartInfo.IsAdd = true
+				chartInfo.MyChartId = myChartList[0].MyChartId
+				chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
+			}
+		}
+	}
+
+	//图表操作权限
+	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, true)
+	//判断是否需要展示英文标识
+	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList[0:1], chartInfo.Source, chartInfo.ChartType)
+	// todo 中英文处理
+	chartInfo.UnitEn = extraConfig.DataConvertConf.EnUnit
+
+	sourceNameList, sourceNameEnList := data.GetEdbSourceByEdbInfoIdList(edbList)
+	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+	// 另存为
+	chartInfo.Button = data_manage.ChartViewButton{
+		IsEdit:    chartInfo.IsEdit,
+		IsEnChart: chartInfo.IsEnChart,
+		IsAdd:     chartInfo.IsAdd,
+		IsCopy:    chartInfo.IsEdit,
+		IsSetName: chartInfo.IsSetName,
+	}
+
+	// 指标权限
+	{
+		edbClassifyPermissionMap := make(map[int]data_manage_permission.EdbClassifyPermission)
+		classifyMap := make(map[int]*data_manage.EdbClassify)
+		// 分类
+		{
+			classifyIdList := make([]int, 0)
+			for _, v := range edbList {
+				classifyIdList = append(classifyIdList, v.ClassifyId)
+			}
+			classifyList, tmpErr := data_manage.GetEdbClassifyByIdList(classifyIdList)
+			if tmpErr != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取分类列表失败,Err:" + tmpErr.Error()
+				return
+			}
+			for _, v := range classifyList {
+				classifyMap[v.ClassifyId] = v
+			}
+		}
+
+		// 遍历到校验map
+		for _, v := range edbList {
+			edbClassifyPermissionMap[v.EdbInfoId] = data_manage_permission.EdbClassifyPermission{
+				ClassifyId:       v.ClassifyId,
+				IsJoinPermission: v.IsJoinPermission,
+				EdbInfoId:        v.EdbInfoId,
+			}
+		}
+
+		// 获取所有有权限的指标和分类
+		permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserEdbAndClassifyPermissionList(sysUser.AdminId, 0, 0)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
+			return
+		}
+
+		for _, v := range edbList {
+			// 数据权限
+			edbItem, ok := edbClassifyPermissionMap[v.EdbInfoId]
+			if !ok {
+				continue
+			}
+
+			if currClassify, ok := classifyMap[edbItem.ClassifyId]; ok {
+				v.HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(edbItem.IsJoinPermission, currClassify.IsJoinPermission, edbItem.EdbInfoId, edbItem.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
+			}
+		}
+	}
+
+	// 图表当前分类的分类树
+	classifyLevels := make([]string, 0)
+	{
+		list, e := data_manage.GetChartClassifyAllBySource(utils.CHART_SOURCE_RANGE_ANALYSIS)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取图表分类失败, Err: %v", e)
+			return
+		}
+		parents := data.GetChartClassifyParentRecursive(list, chartInfo.ChartClassifyId)
+		sort.Slice(parents, func(i, j int) bool {
+			return parents[i].Level < parents[i].Level
+		})
+		for _, v := range parents {
+			classifyLevels = append(classifyLevels, v.UniqueCode)
+		}
+	}
+
+	resp := new(data_manage.ChartInfoDetailResp)
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.DataResp = dataResp
+	resp.ClassifyLevels = classifyLevels
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// DetailFromUniqueCode
+// @Title 根据编码获取图表详情
+// @Description 根据编码获取图表详情接口
+// @Param   UniqueCode   query   int  true       "图表唯一编码,如果是管理后台访问,传固定字符串:7c69b590249049942070ae9dcd5bf6dc"
+// @Param   IsCache   query   bool  true       "是否走缓存,默认false"
+// @Success 200 {object} data_manage.ChartInfoDetailFromUniqueCodeResp
+// @router /chart_info/detail/from_unique_code [get]
+func (this *RangeChartChartInfoController) DetailFromUniqueCode() {
+	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
+	}
+	adminId := sysUser.AdminId
+
+	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
+	chartInfo, err := data_manage.GetChartInfoViewByUniqueCode(uniqueCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			status = false
+		} else {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+			return
+		}
+	}
+	if chartInfo == nil {
+		status = false
+	}
+	if !status {
+		endInfoList := make([]*data_manage.ChartEdbInfoMapping, 0)
+		resp.EdbInfoList = endInfoList
+		resp.ChartInfo = chartInfo
+		resp.Status = false
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}
+
+	//判断是否存在缓存,如果存在缓存,那么直接从缓存中获取
+	key := data.GetChartInfoDataKey(chartInfo.ChartInfoId)
+	if utils.Re == nil && isCache {
+		if utils.Re == nil && utils.Rc.IsExist(key) {
+			if d, e := utils.Rc.RedisBytes(key); e == nil {
+				err := json.Unmarshal(d, &resp)
+				if err == nil && resp != nil {
+					// 这里跟当前用户相关的信息重新查询写入resp, 不使用缓存中的
+					var myCond string
+					var myPars []interface{}
+					myCond += ` AND a.admin_id=? `
+					myPars = append(myPars, adminId)
+					myCond += ` AND a.chart_info_id=? `
+					myPars = append(myPars, chartInfo.ChartInfoId)
+					myList, err := data_manage.GetMyChartByCondition(myCond, myPars)
+					if err != nil && err.Error() != utils.ErrNoRow() {
+						br.Msg = "获取失败"
+						br.ErrMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+						return
+					}
+					resp.ChartInfo.IsAdd = false
+					resp.ChartInfo.MyChartId = 0
+					resp.ChartInfo.MyChartClassifyId = ""
+					if myList != nil && len(myList) > 0 {
+						resp.ChartInfo.IsAdd = true
+						resp.ChartInfo.MyChartId = myList[0].MyChartId
+						resp.ChartInfo.MyChartClassifyId = myList[0].MyChartClassifyId
+					}
+
+					br.Ret = 200
+					br.Success = true
+					br.Msg = "获取成功"
+					br.Data = resp
+					return
+				}
+			}
+		}
+	}
+	resp, isOk, msg, errMsg := GetChartInfoDetailFromUniqueCode(chartInfo, isCache, sysUser)
+	if !isOk {
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		return
+	}
+	if !isOk {
+		br.Msg = msg
+		br.ErrMsg = errMsg
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// GetChartInfoDetailFromUniqueCode 根据编码获取图表详情
+func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCache bool, sysUser *system.Admin) (resp *data_manage.ChartInfoDetailFromUniqueCodeResp, isOk bool, msg, errMsg string) {
+	resp = new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+
+	adminId := sysUser.AdminId
+
+	// 指标数据map
+	edbClassifyPermissionMap := make(map[int]data_manage_permission.EdbClassifyPermission)
+	defer func() {
+		if isOk {
+			// 这里跟当前用户相关的信息重新查询写入resp, 不使用缓存中的
+			{
+				//判断是否加入我的图库
+				var myChartCondition string
+				var myChartPars []interface{}
+				myChartCondition += ` AND a.admin_id=? `
+				myChartPars = append(myChartPars, adminId)
+				myChartCondition += ` AND a.chart_info_id=? `
+				myChartPars = append(myChartPars, chartInfo.ChartInfoId)
+
+				myChartList, err := data_manage.GetMyChartByCondition(myChartCondition, myChartPars)
+				if err != nil && err.Error() != utils.ErrNoRow() {
+					msg = "获取失败"
+					errMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+					return
+				}
+				if myChartList != nil && len(myChartList) > 0 {
+					chartInfo.IsAdd = true
+					chartInfo.MyChartId = myChartList[0].MyChartId
+					chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
+				}
+			}
+
+			resp.ChartInfo.HaveOperaAuth = true
+
+			// 指标权限
+			{
+				classifyMap := make(map[int]*data_manage.EdbClassify)
+				// 分类
+				{
+					classifyIdList := make([]int, 0)
+					for _, v := range resp.EdbInfoList {
+						classifyIdList = append(classifyIdList, v.ClassifyId)
+					}
+					classifyList, tmpErr := data_manage.GetEdbClassifyByIdList(classifyIdList)
+					if tmpErr != nil {
+						errMsg = "获取分类列表失败,Err:" + tmpErr.Error()
+						return
+					}
+					for _, v := range classifyList {
+						classifyMap[v.ClassifyId] = v
+					}
+				}
+
+				// 指标
+				if len(edbClassifyPermissionMap) < 0 {
+					edbInfoIdList := make([]int, 0)
+					for _, v := range resp.EdbInfoList {
+						edbInfoIdList = append(edbInfoIdList, v.EdbInfoId)
+					}
+					edbInfoList, tmpErr := data_manage.GetEdbInfoByIdList(edbInfoIdList)
+					if tmpErr != nil {
+						errMsg = "获取指标列表失败,Err:" + tmpErr.Error()
+						return
+					}
+					for _, v := range edbInfoList {
+						edbClassifyPermissionMap[v.EdbInfoId] = data_manage_permission.EdbClassifyPermission{
+							ClassifyId:       v.ClassifyId,
+							IsJoinPermission: v.IsJoinPermission,
+							EdbInfoId:        v.EdbInfoId,
+						}
+					}
+				}
+
+				// 获取所有有权限的指标和分类
+				permissionEdbIdList, permissionClassifyIdList, err := data_manage_permission.GetUserEdbAndClassifyPermissionList(sysUser.AdminId, 0, 0)
+				if err != nil {
+					errMsg = "获取所有有权限的指标和分类失败,Err:" + err.Error()
+					return
+				}
+
+				for _, v := range resp.EdbInfoList {
+					// 数据权限
+					edbItem, ok := edbClassifyPermissionMap[v.EdbInfoId]
+					if !ok {
+						continue
+					}
+
+					if currClassify, ok := classifyMap[edbItem.ClassifyId]; ok {
+						v.HaveOperaAuth = data_manage_permission.CheckEdbPermissionByPermissionIdList(edbItem.IsJoinPermission, currClassify.IsJoinPermission, edbItem.EdbInfoId, edbItem.ClassifyId, permissionEdbIdList, permissionClassifyIdList)
+					}
+				}
+			}
+		}
+	}()
+
+	//判断是否存在缓存,如果存在缓存,那么直接从缓存中获取
+	key := data.GetChartInfoDataKey(chartInfo.ChartInfoId)
+	if utils.Re == nil && isCache {
+		if utils.Re == nil && utils.Rc.IsExist(key) {
+			if chartData, err1 := utils.Rc.RedisBytes(key); err1 == nil {
+				err := json.Unmarshal(chartData, &resp)
+				if err != nil || resp == nil {
+					return
+				}
+				isOk = true
+				fmt.Println("source redis")
+				return
+			}
+		}
+	}
+
+	// 获取主题样式
+	chartTheme, err := data.GetChartThemeConfig(chartInfo.ChartThemeId, utils.CHART_SOURCE_DEFAULT, 1)
+	if err != nil {
+		msg = "获取失败"
+		errMsg = "获取主题信息失败,Err:" + err.Error()
+		return
+	}
+	chartInfo.ChartThemeStyle = chartTheme.Config
+	chartInfo.ChartThemeId = chartTheme.ChartThemeId
+
+	chartInfoId := chartInfo.ChartInfoId
+
+	// 获取指标信息
+	//chartInfo.CorrelationLeadUnit = req.LeadUnit
+	edbInfoMappingList, err := data_manage.GetChartEdbMappingList(chartInfoId)
+	if err != nil {
+		msg = "获取失败"
+		errMsg = "获取图表,指标信息失败,Err:" + err.Error()
+		return
+	}
+	yearMax := 0
+	dateType := chartInfo.DateType
+	if dateType == utils.DateTypeNYears {
+		for _, v := range edbInfoMappingList {
+			if v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					msg = "获取失败"
+					errMsg = "获取图表日期信息失败,Err:" + tErr.Error()
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	// 开始/结束日期
+	startYear := chartInfo.StartYear
+	startDate := chartInfo.StartDate
+	endDate := chartInfo.EndDate
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
+	// 区间计算图表配置校验
+	var extraConfig data_manage.ChartRangeAnalysisExtraConf
+	if chartInfo.ExtraConfig == `` {
+		msg = "配置信息错误"
+		return
+	}
+	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConfig)
+	if err != nil {
+		msg = "配置信息错误"
+		errMsg = "图表配置信息错误,Err:" + err.Error()
+		return
+	}
+
+	// 获取图表数据
+	if len(edbInfoMappingList) == 0 {
+		msg = "获取失败"
+		errMsg = "图表没有指标,无法计算"
+
+		return
+	}
+
+	// 获取图表x轴y轴
+	// todo 日期处理,
+	edbList, xEdbIdValue, dataResp, e := rangeServ.GetChartDataByEdbInfoList(chartInfoId, "", "", edbInfoMappingList, &extraConfig)
+	if e != nil {
+		msg = "获取失败"
+		errMsg = "获取区间计算图表, 图表计算值失败, Err:" + e.Error()
+		return
+	}
+
+	if chartInfoId > 0 && chartInfo != nil {
+		//判断是否加入我的图库
+		{
+			var myChartCondition string
+			var myChartPars []interface{}
+			myChartCondition += ` AND a.admin_id=? `
+			myChartPars = append(myChartPars, sysUser.AdminId)
+			myChartCondition += ` AND a.chart_info_id=? `
+			myChartPars = append(myChartPars, chartInfo.ChartInfoId)
+
+			myChartList, err := data_manage.GetMyChartByCondition(myChartCondition, myChartPars)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				msg = "获取失败"
+				errMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+				return
+			}
+			if myChartList != nil && len(myChartList) > 0 {
+				chartInfo.IsAdd = true
+				chartInfo.MyChartId = myChartList[0].MyChartId
+				chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
+			}
+		}
+	}
+
+	//图表操作权限
+	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, true)
+	//判断是否需要展示英文标识
+	chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList[0:1], chartInfo.Source, chartInfo.ChartType)
+	chartInfo.UnitEn = ""
+
+	// 另存为
+	chartInfo.Button = data_manage.ChartViewButton{
+		IsEdit:    chartInfo.IsEdit,
+		IsEnChart: chartInfo.IsEnChart,
+		IsAdd:     chartInfo.IsAdd,
+		IsCopy:    chartInfo.IsEdit,
+		IsSetName: chartInfo.IsSetName,
+	}
+
+	// 图表的指标来源
+	sourceNameList, sourceNameEnList := data.GetEdbSourceByEdbInfoIdList(edbList)
+	chartInfo.ChartSource = strings.Join(sourceNameList, ",")
+	chartInfo.ChartSourceEn = strings.Join(sourceNameEnList, ",")
+
+	resp.ChartInfo = chartInfo
+	resp.EdbInfoList = edbList
+	resp.XEdbIdValue = xEdbIdValue
+	resp.DataResp = dataResp
+	resp.Status = true
+
+	// 遍历到校验map
+	for _, v := range edbList {
+		edbClassifyPermissionMap[v.EdbInfoId] = data_manage_permission.EdbClassifyPermission{
+			ClassifyId:       v.ClassifyId,
+			IsJoinPermission: v.IsJoinPermission,
+			EdbInfoId:        v.EdbInfoId,
+		}
+	}
+
+	// 将数据加入缓存
+	if utils.Re == nil {
+		d, _ := json.Marshal(resp)
+		_ = utils.Rc.Put(key, d, 2*time.Hour)
+	}
+	isOk = true
+	return
+}
+
+// List
+// @Title 区间计算图表列表接口
+// @Description 区间计算图表列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ChartClassifyId   query   int  true       "分类id"
+// @Param   Keyword   query   string  true       "搜索关键词"
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Param   Source   query   int  true       "图表类型,3:区间计算,4:滚动区间计算"
+// @Success 200 {object} data_manage.ChartListResp
+// @router /chart_info/list [get]
+func (this *RangeChartChartInfoController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	chartClassifyId, _ := this.GetInt("ChartClassifyId")
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyword := this.GetString("KeyWord")
+
+	var total int
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	source := utils.CHART_SOURCE_RANGE_ANALYSIS
+
+	var condition string
+	var pars []interface{}
+
+	// 普通图表
+	condition += ` AND source = ? `
+	pars = append(pars, source)
+
+	if chartClassifyId > 0 {
+		chartClassifyId, err := data_manage.GetChartClassify(chartClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取图表信息失败"
+			br.ErrMsg = "获取信息失败,GetChartClassify,Err:" + err.Error()
+			return
+		}
+		condition += " AND chart_classify_id IN(" + chartClassifyId + ") "
+	}
+	if keyword != "" {
+		condition += ` AND  ( chart_name LIKE '%` + keyword + `%' )`
+	}
+
+	//只看我的
+	isShowMe, _ := this.GetBool("IsShowMe")
+	if isShowMe {
+		condition += ` AND sys_user_id = ? `
+		pars = append(pars, sysUser.AdminId)
+	}
+
+	// 获取当前账号的不可见指标
+	noPermissionChartIdList := make([]int, 0)
+	{
+		obj := data_manage.EdbInfoNoPermissionAdmin{}
+		confList, err := obj.GetAllChartListByAdminId(this.SysUser.AdminId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range confList {
+			noPermissionChartIdList = append(noPermissionChartIdList, v.ChartInfoId)
+		}
+	}
+
+	lenNoPermissionChartIdList := len(noPermissionChartIdList)
+	if lenNoPermissionChartIdList > 0 {
+		condition += ` AND chart_info_id not in (` + utils.GetOrmInReplace(lenNoPermissionChartIdList) + `) `
+		pars = append(pars, noPermissionChartIdList)
+	}
+
+	//获取图表信息
+	list, err := data_manage.GetChartListByCondition(condition, pars, startSize, pageSize)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Success = true
+		br.Msg = "获取图表信息失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+
+	myChartList, err := data_manage.GetMyChartListByAdminId(sysUser.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取图表信息失败"
+		br.ErrMsg = "获取我的图表信息失败,Err:" + err.Error()
+		return
+	}
+	myChartMap := make(map[int]*data_manage.MyChartView)
+	for _, v := range myChartList {
+		myChartMap[v.ChartInfoId] = v
+	}
+	listLen := len(list)
+	chartEdbMap := make(map[int][]*data_manage.ChartEdbInfoMapping)
+	if listLen > 0 {
+		chartInfoIds := ""
+		for _, v := range list {
+			chartInfoIds += strconv.Itoa(v.ChartInfoId) + ","
+		}
+		if chartInfoIds != "" {
+			chartInfoIds = strings.Trim(chartInfoIds, ",")
+			//判断是否需要展示英文标识
+			edbList, e := data_manage.GetChartEdbMappingListByChartInfoIds(chartInfoIds)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取图表,指标信息失败,Err:" + e.Error()
+				return
+			}
+			for _, v := range edbList {
+				chartEdbMap[v.ChartInfoId] = append(chartEdbMap[v.ChartInfoId], v)
+			}
+		}
+	}
+	for i := 0; i < listLen; i++ {
+		//判断是否需要展示英文标识
+		if edbTmpList, ok := chartEdbMap[list[i].ChartInfoId]; ok {
+			list[i].IsEnChart = data.CheckIsEnChart(list[i].ChartNameEn, edbTmpList, list[i].Source, list[i].ChartType)
+		}
+
+		if existItem, ok := myChartMap[list[i].ChartInfoId]; ok {
+			list[i].IsAdd = true
+			list[i].MyChartId = existItem.MyChartId
+			list[i].MyChartClassifyId = existItem.MyChartClassifyId
+		}
+	}
+
+	resp := new(data_manage.ChartListResp)
+	if list == nil || len(list) <= 0 || (err != nil && err.Error() == utils.ErrNoRow()) {
+		items := make([]*data_manage.ChartInfoView, 0)
+		resp.Paging = page
+		resp.List = items
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	dataCount, err := data_manage.GetChartListCountByCondition(condition, pars)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标数据总数失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, dataCount)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Copy
+// @Title 复制并新增图表接口
+// @Description 新增图表接口
+// @Params	request	body data_manage.CopyAddChartInfoReq true "type json string"
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /chart_info/copy [post]
+func (this *RangeChartChartInfoController) Copy() {
+	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.CopyAddChartInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ChartInfoId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, ChartInfoId: %d", req.ChartInfoId)
+		return
+	}
+	req.ChartName = strings.TrimSpace(req.ChartName)
+	if req.ChartName == "" {
+		br.Msg = "请输入图表名称"
+		return
+	}
+	if req.ChartClassifyId <= 0 {
+		br.Msg = "请选择图表分类"
+		return
+	}
+
+	deleteCache := true
+	cacheKey := "CACHE_CHART_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
+	}
+
+	chartSource := utils.CHART_SOURCE_RANGE_ANALYSIS
+	// 校验分类、图表名称
+	{
+		var cond string
+		var pars []interface{}
+		switch this.Lang {
+		case utils.EnLangVersion:
+			cond += " AND chart_name_en = ? AND source = ? "
+		default:
+			cond += " AND chart_name = ? AND source = ? "
+		}
+		pars = append(pars, req.ChartName, chartSource)
+		count, e := data_manage.GetChartInfoCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = fmt.Sprintf("获取同名图表失败, Err: %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "图表名称已存在, 请重新填写"
+			return
+		}
+
+		_, e = data_manage.GetChartClassifyById(req.ChartClassifyId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "分类不存在"
+				return
+			}
+			br.Msg = "保存失败"
+			br.ErrMsg = fmt.Sprintf("获取图表分类失败, Err: %v", e)
+			return
+		}
+	}
+
+	// 图表信息
+	originChart, e := data_manage.GetChartInfoById(req.ChartInfoId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "原图表不存在"
+			return
+		}
+		br.Msg = "保存失败"
+		br.ErrMsg = fmt.Sprintf("获取原图表信息失败, Err: %v", e)
+		return
+	}
+	if originChart.Source == utils.CHART_SOURCE_ROLLING_CORRELATION {
+		br.Msg = `滚动区间计算图不支持另存为`
+		br.IsSendEmail = false
+		return
+	}
+
+	// 普通区间计算图表
+	chartInfo := new(data_manage.ChartInfo)
+
+	newChart, err, errMsg, isSendEmail := rangeServ.CopyChartInfo(req.ChartClassifyId, req.ChartName, originChart, sysUser, this.Lang)
+	chartInfo = newChart
+
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	// 新增操作日志
+	{
+		chartLog := new(data_manage.ChartInfoLog)
+		chartLog.ChartInfoId = chartInfo.ChartInfoId
+		chartLog.ChartName = req.ChartName
+		chartLog.ChartClassifyId = req.ChartClassifyId
+		chartLog.SysUserId = sysUser.AdminId
+		chartLog.SysUserRealName = sysUser.RealName
+		chartLog.UniqueCode = chartInfo.UniqueCode
+		chartLog.CreateTime = time.Now()
+		chartLog.Content = string(this.Ctx.Input.RequestBody)
+		chartLog.Status = "复制区间计算图表"
+		chartLog.Method = this.Ctx.Input.URI()
+		go data_manage.AddChartInfoLog(chartLog)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = data_manage.AddChartInfoResp{
+		ChartInfoId: chartInfo.ChartInfoId,
+		UniqueCode:  chartInfo.UniqueCode,
+		ChartType:   chartInfo.ChartType,
+	}
+	br.IsAddLog = true
+}
+
+// Refresh
+// @Title 图表刷新接口
+// @Description 图表刷新接口
+// @Param   ChartInfoId   query   int  true       "图表id"
+// @Param   UniqueCode   query   string  true       "唯一code"
+// @Success Ret=200 刷新成功
+// @router /chart_info/refresh [get]
+func (this *RangeChartChartInfoController) Refresh() {
+	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
+	}
+	chartInfoId, _ := this.GetInt("ChartInfoId")
+	uniqueCode := this.GetString("UniqueCode")
+	if chartInfoId <= 0 && uniqueCode == `` {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误:chartInfoId:" + strconv.Itoa(chartInfoId) + ",UniqueCode:" + uniqueCode
+		return
+	}
+
+	var chartInfo *data_manage.ChartInfo
+	var err error
+	if chartInfoId > 0 {
+		chartInfo, err = data_manage.GetChartInfoById(chartInfoId)
+	} else {
+		chartInfo, err = data_manage.GetChartInfoByUniqueCode(uniqueCode)
+	}
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "图表已被删除,无需刷新"
+			br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+			return
+		}
+		br.Msg = "刷新失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+
+	// 刷新区间计算图表
+	isAsync, e := rangeServ.ChartInfoRefresh(chartInfo.ChartInfoId, chartInfo.UniqueCode)
+	if e != nil {
+		br.Msg = "刷新失败"
+		br.ErrMsg = "刷新区间计算图表失败, Err:" + err.Error()
+		return
+	}
+	// 多因子区间计算异步刷新, 前端提示
+	if isAsync {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "刷新时间较长, 请10分钟后查看"
+		return
+	}
+
+	//清除图表缓存
+	{
+		key := utils.HZ_CHART_LIB_DETAIL + chartInfo.UniqueCode
+		_ = utils.Rc.Delete(key)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "刷新成功"
+}

+ 0 - 1
controllers/data_source/guagnzhouqihuo.go

@@ -13,7 +13,6 @@ import (
 	"time"
 )
 
-// 广州期货交易所
 type DataSourceController struct {
 	controllers.BaseAuthController
 }

+ 6 - 2
controllers/data_source/icpi.go

@@ -1,6 +1,7 @@
 package data_source
 
 import (
+	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_source"
 	"eta/eta_api/utils"
@@ -13,13 +14,16 @@ import (
 )
 
 // 消费者价格指数
+type DataSourceIcpiController struct {
+	controllers.BaseAuthController
+}
 
 // ComTradeCountryList
 // @Title 获取居民消费价格指数分类
 // @Description 获取居民消费价格指数分类
 // @Success 200 {object} []data_manage.ComTradeCountryItem
 // @router /icpi/classify/list [get]
-func (this *DataSourceController) IcpiClassifyList() {
+func (this *DataSourceIcpiController) IcpiClassifyList() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
 		this.Data["json"] = br
@@ -54,7 +58,7 @@ func (this *DataSourceController) IcpiClassifyList() {
 // @Param   KeyWord   query   string  true       "关键词"
 // @Success 200 {object} data_source.BaseFromIcpiIndexView
 // @router /icpi/index/data [get]
-func (this *DataSourceController) IcpiData() {
+func (this *DataSourceIcpiController) IcpiData() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
 		this.Data["json"] = br

+ 0 - 1
controllers/english_report/report.go

@@ -475,7 +475,6 @@ func (this *EnglishReportController) ListReport() {
 
 	var authOk bool
 	adminMap := make(map[int]string, 0) // 编辑中的研究员姓名
-
 	total, e := models.GetEnglishReportListCount(condition, pars, companyType)
 	if e != nil {
 		br.Msg = "获取失败"

+ 55 - 4
controllers/ppt_english.go

@@ -92,6 +92,16 @@ func (this *PptEnglishController) ListPpt() {
 		} else {
 			list[i].IsAuth = false
 		}
+		if list[i].PptPage == 0 {
+			var pptContent []services.PPTContent
+			er := json.Unmarshal([]byte(list[i].Content), &pptContent)
+			if er != nil {
+				br.Msg = "content参数解析失败"
+				br.ErrMsg = "content参数解析失败, Err:" + er.Error()
+				return
+			}
+			list[i].PptPage = len(pptContent)
+		}
 	}
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := new(ppt_english.PptEnglishListResp)
@@ -126,6 +136,13 @@ func (this *PptEnglishController) AddPpt() {
 		br.Msg = "标题不能为空"
 		return
 	}
+	var pptContent []services.PPTContent
+	err = json.Unmarshal([]byte(req.Content), &pptContent)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
 
 	var newId int64
 	var msg string
@@ -190,9 +207,9 @@ func (this *PptEnglishController) AddPpt() {
 		pptInfo.Content = req.Content
 		pptInfo.CoverContent = req.CoverContent
 		pptInfo.ModifyTime = time.Now()
+		pptInfo.PptPage = len(pptContent)
 		pptInfo.TitleSetting = req.TitleSetting
-		err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
-
+		err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "ppt_page", "TitleSetting"})
 		msg = "保存成功"
 	}
 	resp := ppt_english.AddPptEnglishResp{
@@ -223,6 +240,15 @@ func (this *PptEnglishController) EditPpt() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
+
+	var pptContent []services.PPTContent
+	err = json.Unmarshal([]byte(req.Content), &pptContent)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
 	if req.FirstPage.Title == "" {
 		br.Msg = "标题不能为空"
 		return
@@ -270,8 +296,10 @@ func (this *PptEnglishController) EditPpt() {
 	pptInfo.Content = req.Content
 	pptInfo.CoverContent = req.CoverContent
 	pptInfo.ModifyTime = time.Now()
+	pptInfo.PptPage = len(pptContent)
+
 	pptInfo.TitleSetting = req.TitleSetting
-	err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
+	err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "ppt_page", "TitleSetting"})
 	if err != nil {
 		br.Msg = "编辑失败"
 		br.ErrMsg = "编辑失败,Err:" + err.Error()
@@ -500,6 +528,29 @@ func (this *PptEnglishController) Publish() {
 		br.ErrMsg = "发布失败,Err:" + err.Error()
 		return
 	}
+
+	pptMap, err := ppt_english.GetPptMappingByPptId(int64(req.PptId))
+	if err != nil {
+		br.Msg = `该PPT信息不存在, 保存失败`
+		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+		br.IsSendEmail = false
+		return
+	}
+	pptMapList, err := ppt_english.GetPptMappingListByGroupIdDesc(pptMap.GroupId)
+	if err != nil {
+		br.ErrMsg = "PPT目录信息异常"
+		return
+	}
+	if !pptMap.IsMoved && len(pptMapList) > 1 {
+		// 如果没有人为移动位置, 默认将当前ppt置顶
+		err = ppt.MoveGroupPptEnglish(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = err.Error()
+			br.ErrMsg = "移动失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	//添加发布记录
 	{
 		record := new(ppt_english.PptEnglishPublishRecord)
@@ -667,6 +718,7 @@ func (this *PptEnglishController) SaveLog() {
 		br.Msg = "标题不能为空"
 		return
 	}
+
 	var pageContent []services.PPTContent
 	err = json.Unmarshal([]byte(req.Content), &pageContent)
 	if err != nil {
@@ -674,7 +726,6 @@ func (this *PptEnglishController) SaveLog() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
-
 	// 获取ppt
 	item, err := ppt_english.GetPptEnglishByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
 	if err != nil && err.Error() != utils.ErrNoRow() {

+ 11 - 1
controllers/ppt_english_group.go

@@ -301,7 +301,17 @@ func (this *PptEnglishGroupController) MoveGroupPpt() {
 		br.Msg = "请输入目录ID"
 		return
 	}
-	err = ppt.MoveGroupPptEnglish(req.GroupId, req.GroupPptId, req.PrevGroupPptId, req.NextGroupPptId, this.SysUser.AdminId)
+	// 由于是倒序, 所以需要颠倒NextGroupPptId和PrevGroupPptId的位置
+	err = ppt.MoveGroupPptEnglish(req.GroupId, req.GroupPptId, req.NextGroupPptId, req.PrevGroupPptId, this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+	// 增加人为移动的标识
+	mappingInfo := &ppt_english.PptEnglishGroupMapping{}
+	mappingInfo.GroupPptId = req.GroupPptId
+	mappingInfo.IsMoved = true
+	err = mappingInfo.Update([]string{"is_moved"})
 	if err != nil {
 		br.Msg = err.Error()
 		return

+ 58 - 4
controllers/ppt_v2.go

@@ -92,6 +92,16 @@ func (this *PptV2Controller) ListPpt() {
 		} else {
 			list[i].IsAuth = false
 		}
+		if list[i].PptPage == 0 {
+			var pptContent []services.PPTContent
+			er := json.Unmarshal([]byte(list[i].Content), &pptContent)
+			if er != nil {
+				br.Msg = "content参数解析失败"
+				br.ErrMsg = "content参数解析失败, Err:" + er.Error()
+				return
+			}
+			list[i].PptPage = len(pptContent)
+		}
 	}
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := new(models.PptV2ListResp)
@@ -127,6 +137,14 @@ func (this *PptV2Controller) AddPpt() {
 		return
 	}
 
+	var pptContent []services.PPTContent
+	err = json.Unmarshal([]byte(req.Content), &pptContent)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
 	var newId int64
 	var msg string
 	if req.PptId <= 0 {
@@ -159,6 +177,7 @@ func (this *PptV2Controller) AddPpt() {
 			AdminId:       this.SysUser.AdminId,
 			AdminRealName: this.SysUser.RealName,
 			PptVersion:    2,
+			PptPage:       len(pptContent),
 			TitleSetting:  req.TitleSetting,
 		}
 		newId, err = models.AddPptV2(pptInfo)
@@ -191,9 +210,9 @@ func (this *PptV2Controller) AddPpt() {
 		pptInfo.Content = req.Content
 		pptInfo.CoverContent = req.CoverContent
 		pptInfo.ModifyTime = time.Now()
+		pptInfo.PptPage = len(pptContent)
 		pptInfo.TitleSetting = req.TitleSetting
-		err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
-
+		err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "ppt_page", "TitleSetting"})
 		msg = "保存成功"
 	}
 	resp := models.AddPptResp{
@@ -228,6 +247,15 @@ func (this *PptV2Controller) EditPpt() {
 		br.Msg = "标题不能为空"
 		return
 	}
+
+	var pptContent []services.PPTContent
+	err = json.Unmarshal([]byte(req.Content), &pptContent)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
 	item, err := models.GetPptV2ByTitleAndId(req.FirstPage.Title, this.SysUser.AdminId)
 	if err != nil && err.Error() != utils.ErrNoRow() {
 		br.Msg = "获取数据异常!"
@@ -271,8 +299,10 @@ func (this *PptV2Controller) EditPpt() {
 	pptInfo.Content = req.Content
 	pptInfo.CoverContent = req.CoverContent
 	pptInfo.ModifyTime = time.Now()
+	pptInfo.PptPage = len(pptContent)
+
 	pptInfo.TitleSetting = req.TitleSetting
-	err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "TitleSetting"})
+	err = pptInfo.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "CoverContent", "ppt_page", "TitleSetting"})
 	if err != nil {
 		br.Msg = "编辑失败"
 		br.ErrMsg = "编辑失败,Err:" + err.Error()
@@ -500,6 +530,29 @@ func (this *PptV2Controller) Publish() {
 		br.ErrMsg = "发布失败,Err:" + err.Error()
 		return
 	}
+
+	pptMap, err := models.GetPptMappingByPptId(int64(req.PptId))
+	if err != nil {
+		br.Msg = `该PPT信息不存在, 保存失败`
+		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+		br.IsSendEmail = false
+		return
+	}
+	pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
+	if err != nil {
+		br.ErrMsg = "PPT目录信息异常"
+		return
+	}
+	if !pptMap.IsMoved && len(pptMapList) > 1 {
+		// 如果没有人为移动位置, 默认将当前ppt置顶
+		err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = err.Error()
+			br.ErrMsg = "移动失败,Err:" + err.Error()
+			return
+		}
+	}
+
 	//添加发布记录
 	{
 		record := new(models.PptV2PublishRecord)
@@ -688,7 +741,8 @@ func (this *PptV2Controller) SaveLog() {
 	pptItem.ModifyTime = time.Now()
 	pptItem.TitleSetting = req.TitleSetting
 	pptItem.PptPage = len(pptContent)
-	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "TitleSetting", "ppt_page"})
+
+	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "ppt_page", "TitleSetting"})
 
 	// 将更新后的PPT, 置顶
 	pptMap, err := models.GetPptMappingByPptId(int64(req.PptId))

+ 11 - 1
controllers/ppt_v2_group.go

@@ -303,7 +303,17 @@ func (this *PptV2GroupController) MoveGroupPpt() {
 		br.Msg = "请输入目录ID"
 		return
 	}
-	err = ppt.MoveGroupPpt(req.GroupId, req.GroupPptId, req.PrevGroupPptId, req.NextGroupPptId, this.SysUser.AdminId)
+	// 由于是倒序, 所以需要颠倒NextGroupPptId和PrevGroupPptId的位置
+	err = ppt.MoveGroupPpt(req.GroupId, req.GroupPptId, req.NextGroupPptId, req.PrevGroupPptId, this.SysUser.AdminId)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+	// 增加人为移动的标识
+	mappingInfo := &models.PptV2GroupMapping{}
+	mappingInfo.GroupPptId = req.GroupPptId
+	mappingInfo.IsMoved = true
+	err = mappingInfo.Update([]string{"is_moved"})
 	if err != nil {
 		br.Msg = err.Error()
 		return

+ 1 - 0
controllers/report.go

@@ -1865,6 +1865,7 @@ func (this *ReportController) SendMsg() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
+
 	if req.ReportId <= 0 {
 		br.Msg = "参数错误"
 		br.ErrMsg = "参数错误"

+ 2 - 2
controllers/sandbox/sandbox.go

@@ -1832,9 +1832,9 @@ func (this *SandboxController) SaveV2() {
 		//缩略图为空时不更新
 		var updateSandboxColumn = []string{}
 		if req.PicUrl == "" {
-			updateSandboxColumn = []string{"Content", "MindmapData", "ModifyTime", "SandboxClassifyId", "Style"}
+			updateSandboxColumn = []string{"Name", "Content", "MindmapData", "ModifyTime", "SandboxClassifyId", "Style"}
 		} else {
-			updateSandboxColumn = []string{"Content", "MindmapData", "PicUrl", "ModifyTime", "SandboxClassifyId", "Style"}
+			updateSandboxColumn = []string{"Name", "Content", "MindmapData", "PicUrl", "ModifyTime", "SandboxClassifyId", "Style"}
 		}
 		err = sandboxInfo.Update(updateSandboxColumn)
 		if err != nil {

+ 55 - 27
controllers/sys_admin.go

@@ -12,10 +12,11 @@ import (
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // 系统用户
@@ -737,32 +738,55 @@ func (this *SysAdminController) Edit() {
 		}
 	}
 
-	adminInfo.AdminName = req.AdminName
-	adminInfo.RealName = req.RealName
-	adminInfo.LastUpdatedTime = time.Now().Format(utils.FormatDateTime)
-	adminInfo.Mobile = req.Mobile
-	adminInfo.RoleId = req.RoleId
-	adminInfo.RoleName = roleName
-	adminInfo.Enabled = req.Enabled
-	adminInfo.Authority = authority
-	adminInfo.Position = req.Position
-	adminInfo.RoleTypeCode = roleItem.RoleTypeCode
-	adminInfo.Province = req.Province
-	adminInfo.ProvinceCode = req.ProvinceCode
-	adminInfo.City = req.City
-	adminInfo.CityCode = req.CityCode
-	adminInfo.EmployeeId = req.EmployeeId
-	adminInfo.Email = req.Email
-	adminInfo.TelAreaCode = req.TelAreaCode
-	adminInfo.IsLdap = req.IsLdap
-	cols := []string{
-		"AdminName", "RealName", "LastUpdatedTime", "Mobile", "RoleId", "RoleName", "Enabled", "Authority",
-		"Position", "RoleTypeCode", "Province", "ProvinceCode", "City", "CityCode", "EmployeeId", "Email", "TelAreaCode", "IsLdap",
-	}
-	if e := adminInfo.Update(cols); e != nil {
-		br.Msg = "编辑失败"
-		br.ErrMsg = "更新用户信息失败, Err:" + e.Error()
-		return
+	if adminInfo.RoleName == "admin" && adminInfo.RealName == "admin" {
+		adminInfo.LastUpdatedTime = time.Now().Format(utils.FormatDateTime)
+		adminInfo.Mobile = req.Mobile
+		adminInfo.Position = req.Position
+		adminInfo.Province = req.Province
+		adminInfo.ProvinceCode = req.ProvinceCode
+		adminInfo.City = req.City
+		adminInfo.CityCode = req.CityCode
+		adminInfo.EmployeeId = req.EmployeeId
+		adminInfo.Email = req.Email
+		adminInfo.TelAreaCode = req.TelAreaCode
+		adminInfo.IsLdap = req.IsLdap
+		cols := []string{
+			"LastUpdatedTime", "Mobile", "Position", "Province", "ProvinceCode",
+			"City", "CityCode", "EmployeeId", "Email", "TelAreaCode", "IsLdap",
+		}
+		if e := adminInfo.Update(cols); e != nil {
+			br.Msg = "编辑失败"
+			br.ErrMsg = "更新用户信息失败, Err:" + e.Error()
+			return
+		}
+	} else {
+		adminInfo.AdminName = req.AdminName
+		adminInfo.RealName = req.RealName
+		adminInfo.LastUpdatedTime = time.Now().Format(utils.FormatDateTime)
+		adminInfo.Mobile = req.Mobile
+		adminInfo.RoleId = req.RoleId
+		adminInfo.RoleName = roleName
+		adminInfo.Enabled = req.Enabled
+		adminInfo.Authority = authority
+		adminInfo.Position = req.Position
+		adminInfo.RoleTypeCode = roleItem.RoleTypeCode
+		adminInfo.Province = req.Province
+		adminInfo.ProvinceCode = req.ProvinceCode
+		adminInfo.City = req.City
+		adminInfo.CityCode = req.CityCode
+		adminInfo.EmployeeId = req.EmployeeId
+		adminInfo.Email = req.Email
+		adminInfo.TelAreaCode = req.TelAreaCode
+		adminInfo.IsLdap = req.IsLdap
+		cols := []string{
+			"AdminName", "RealName", "LastUpdatedTime", "Mobile", "RoleId", "RoleName", "Enabled", "Authority",
+			"Position", "RoleTypeCode", "Province", "ProvinceCode", "City", "CityCode", "EmployeeId", "Email", "TelAreaCode", "IsLdap",
+		}
+		if e := adminInfo.Update(cols); e != nil {
+			br.Msg = "编辑失败"
+			br.ErrMsg = "更新用户信息失败, Err:" + e.Error()
+			return
+		}
 	}
 
 	// 同步用户缓存
@@ -831,6 +855,10 @@ func (this *SysAdminController) EditEnabled() {
 		br.ErrMsg = "获取系统用户数据失败,Err:" + err.Error()
 		return
 	}
+	if adminItem.RoleName == "admin" && adminItem.RealName == "admin" {
+		br.Msg = "禁止对admin使用<禁用>功能"
+		return
+	}
 
 	if req.Enabled == 0 {
 		//禁用

+ 21 - 1
controllers/sys_role.go

@@ -7,10 +7,11 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 type SysRoleController struct {
@@ -113,6 +114,11 @@ func (this *SysRoleController) Edit() {
 		br.Msg = "角色不存在, 请刷新页面"
 		return
 	}
+	// 不允许编辑admin用户
+	if item.RoleName == "admin" {
+		br.Msg = "admin用户不可编辑"
+		return
+	}
 	exists, e := system.GetSysRoleByName(req.RoleName)
 	if e != nil && e.Error() != utils.ErrNoRow() {
 		br.Msg = "操作失败"
@@ -179,6 +185,10 @@ func (this *SysRoleController) Delete() {
 		br.ErrMsg = "获取角色信息失败, Err: " + e.Error()
 		return
 	}
+	if role.RoleName == "admin" {
+		br.Msg = "admin用户不可删除"
+		return
+	}
 
 	err = system.DeleteSysRole(req.RoleId)
 	if err != nil {
@@ -515,6 +525,16 @@ func (this *SysRoleController) SysRoleMenuAuthList() {
 		}
 	}
 
+	sysRole, err := system.GetSysRoleById(roleId)
+	if err == nil {
+		if sysRole.RoleName == "admin" {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			return
+		}
+	}
+
 	order := `sort ASC, create_time DESC, menu_id DESC`
 	list, e := system.GetSysMenuItemsByCondition(` AND hidden = 0`, make([]interface{}, 0), []string{}, order)
 	if e != nil {

+ 3 - 3
controllers/sys_team.go

@@ -63,7 +63,7 @@ func (this *SysTeamController) Add() {
 			}
 
 			// 同步分组缓存
-			if utils.BusinessCode == utils.BusinessCodeRelease {
+			if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
 				var syncData system.SyncGroupData
 				syncData.Source = utils.SOURCE_ETA_FLAG
 				syncData.GroupId = int(groupId)
@@ -123,7 +123,7 @@ func (this *SysTeamController) Edit() {
 	}
 
 	// 同步分组缓存
-	if utils.BusinessCode == utils.BusinessCodeRelease {
+	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
 		var syncData system.SyncGroupData
 		syncData.Source = utils.SOURCE_ETA_FLAG
 		syncData.GroupId = item.GroupId
@@ -172,7 +172,7 @@ func (this *SysTeamController) Delete() {
 	}
 
 	// 同步分组缓存
-	if utils.BusinessCode == utils.BusinessCodeRelease {
+	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
 		var syncData system.SyncGroupData
 		syncData.Source = utils.SOURCE_ETA_FLAG
 		syncData.GroupId = req.TeamId

+ 42 - 458
controllers/target.go

@@ -27,7 +27,7 @@ import (
 	"time"
 
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"github.com/shopspring/decimal"
+	//"github.com/shopspring/decimal"
 	"github.com/tealeg/xlsx"
 )
 
@@ -184,6 +184,14 @@ func (this *TargetController) DataAdd() {
 		br.ErrMsg = "新增失败,Err:" + err.Error()
 		return
 	}
+	//将该指标的code加入到 “手工数据导入后刷新” 缓存
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, edbdata.TradeCode)
+		if err != nil {
+			fmt.Println("DataAdd CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+		}
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "新增成功"
@@ -315,6 +323,13 @@ func (this *TargetController) DataEdit() {
 			}
 		}
 	}
+	//将该指标的code加入到 “手工数据导入后刷新” 缓存
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, edbdata.TradeCode)
+		if err != nil {
+			fmt.Println("DataEdit CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+		}
+	}
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "编辑成功"
@@ -345,11 +360,13 @@ func (this *TargetController) BatchDataEdit() {
 	}
 
 	edbDataEditList := make([]data.EdbDataEdit, 0)
+	edbCodeMap := make(map[string]string)
 	for _, trade := range req.List {
 		if trade.TradeCode == "" {
 			br.Msg = "指标编码不可为空!"
 			return
 		}
+		edbCodeMap[trade.TradeCode] = trade.TradeCode
 
 		close := ""
 		loc := reflect.ValueOf(trade.Close).Kind().String()
@@ -380,6 +397,15 @@ func (this *TargetController) BatchDataEdit() {
 		br.ErrMsg = "批量修改失败:" + err.Error()
 		return
 	}
+	//将该指标的code加入到 “手工数据导入后刷新” 缓存
+	if utils.Re == nil {
+		for _, edbCode := range edbCodeMap {
+			err = utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, edbCode)
+			if err != nil {
+				fmt.Println("BatchDataEdit CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+			}
+		}
+	}
 	br.Ret = 200
 	br.Success = true
 	br.Data = failEdbDataList
@@ -444,7 +470,7 @@ func (this *TargetController) TargetList() {
 		return
 	}
 	total := 0
-	list := make([]*models.Edbinfo, 0)
+	list := make([]*models.EdbinfoItem, 0)
 
 	//有分类数据权限才查询
 	if len(classifyIdStrList) > 0 {
@@ -483,14 +509,14 @@ func (this *TargetController) TargetList() {
 			br.ErrMsg = "获取失败,Err:" + err.Error()
 			return
 		}
-		tmpList, err := models.GetEdbinfoList(condition, pars, startSize, pageSize, mobile, sysUser.RoleType)
+		tmpList, err := models.GetEdbinfoItemList(condition, pars, startSize, pageSize, mobile, sysUser.RoleType)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取失败,Err:" + err.Error()
 			return
 		}
 		if len(tmpList) <= 0 {
-			list = make([]*models.Edbinfo, 0)
+			list = make([]*models.EdbinfoItem, 0)
 		} else {
 			list = tmpList
 		}
@@ -750,10 +776,12 @@ func (this *TargetController) ClassifyList() {
 			edbInfoGroupCountMap[edbInfoGroupCount.ClassifyId] = edbInfoGroupCount.Count
 		}
 		for _, classifyList := range list {
+			classifyList.UniqueCode = utils.MD5(fmt.Sprint(classifyList.ClassifyId))
 			if classifyList.Child != nil {
 				for _, classify := range classifyList.Child {
 					if total, ok := edbInfoGroupCountMap[classify.ClassifyId]; ok {
 						classify.EdbInfoTotal = total
+						classify.UniqueCode = utils.MD5(fmt.Sprint(classify.ClassifyId))
 					}
 				}
 			}
@@ -771,6 +799,7 @@ func (this *TargetController) ClassifyList() {
 // @Title 下载模板
 // @Description 下载模板
 // @Success 200 {object} models.EdbdataClassifyResp
+// @Param   Source   query   int  false       "来源:1:模板1;2:模板2"
 // @router /template [get]
 func (this *TargetCommonController) TemplateDownload() {
 	br := new(models.BaseResponse).Init()
@@ -805,460 +834,6 @@ func (this *TargetCommonController) TemplateDownloadEn() {
 	br.Msg = "下载成功"
 }
 
-// ImportData
-// @Title 导入数据
-// @Description 导入数据
-// @Param   EntryFile   query   file  true       "文件"
-// @Success 200 Ret=200 录入成功
-// @router /import/data [post]
-func (this *TargetController) ImportData() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
-	sysUser := this.SysUser
-	if sysUser == nil {
-		br.Msg = "请重新登录"
-		return
-	}
-	file, _, err := this.GetFile("EntryFile")
-	if err != nil {
-		br.Msg = "获取文件失败"
-		br.ErrMsg = "获取文件失败,Err:" + err.Error()
-		return
-	}
-	path := "./static/数据导入_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
-	defer file.Close()
-	err = this.SaveToFile("EntryFile", path)
-	if err != nil {
-		br.Msg = "文件保存失败"
-		br.ErrMsg = "文件保存失败,Err:" + err.Error()
-		return
-	}
-	if utils.RunMode == "debug" {
-		defer os.Remove(path)
-	}
-	xlFile, err := xlsx.OpenFile(path)
-	if err != nil {
-		fmt.Println(err.Error())
-		return
-	}
-
-	//超管账号可以查看分类下的所有频度数据
-	userId := sysUser.AdminId
-	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
-		userId = 0
-	}
-	//获取账户所拥有权限的分类id集合
-	classifyNameStrList, edbDataClassifyMap, err := data.GetEdbClassifyNameListByAdminId(int64(userId))
-	if err != nil {
-		br.Msg = "获取分类数据失败"
-		return
-	}
-
-	//导入成功数量
-	successCount := 0
-	//导入失败数据
-	failDatas := make([]*models.EdbdataImportFail, 0)
-
-	//指标map
-	targetMap := make(map[string]*models.Edbinfo)
-	defer func() {
-		for _, target := range targetMap {
-			//结束后,清除掉对应的缓存
-			key := "import:edbinfo:data:" + target.TradeCode
-			utils.Rc.Delete(key)
-
-			//将该指标的code加入到 “手工数据导入后刷新” 缓存
-			if utils.Re == nil {
-				err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, target.TradeCode)
-				if err != nil {
-					fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
-				}
-			}
-		}
-	}()
-
-	// 所有的指标数据map
-	edbCodeDataMap := make(map[string]map[string]string)
-
-	// 遍历sheet页读取
-	for _, sheet := range xlFile.Sheets {
-		fmt.Println("sheet name: ", sheet.Name)
-		//遍历行读取
-		maxRow := sheet.MaxRow
-		fmt.Println("maxRow:", maxRow)
-		fmt.Println("maxRow")
-		for i := 0; i < maxRow; i++ {
-			if i == 1 {
-				row := sheet.Row(i)
-				cells := row.Cells
-				if len(cells) < 6 {
-					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
-					br.Msg = "导入文件异常,请下载最新导入模板文件"
-					return
-				}
-				templateFail := false
-				if cells[0].Value != "品种分类" && cells[0].Value != "Species Category" {
-					templateFail = true
-				}
-				if cells[1].Value != "录入日期" && cells[1].Value != "Input Date" {
-					templateFail = true
-				}
-				if cells[2].Value != "指标名称" && cells[2].Value != "Indicator Name Indicator Name" {
-					templateFail = true
-				}
-				if cells[3].Value != "值" && cells[3].Value != "Value" {
-					templateFail = true
-				}
-				if cells[4].Value != "频度" && cells[4].Value != "Frequency" {
-					templateFail = true
-				}
-				if cells[5].Value != "单位" && cells[5].Value != "Unit" {
-					templateFail = true
-				}
-				if templateFail {
-					br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
-					br.Msg = "导入文件异常,请下载最新导入模板文件"
-					return
-				}
-			}
-			if i > 1 {
-				row := sheet.Row(i)
-				cells := row.Cells
-				if len(cells) >= 6 {
-					classifyName := cells[0].Value //分类
-					if classifyName == "" {        //过滤空白行
-						continue
-					}
-					cell1 := cells[1].Value
-					//createDate := utils.ConvertToFormatDay(cell1) //录入日期
-					createDate := cell1
-					cell2 := cells[2].Value //指标名称
-					secName := utils.TrimStr(cell2)
-					frequency := cells[4].Value //频度
-					unit := cells[5].Value      //单位
-
-					closeVal := cells[3].Value //值
-					if strings.Contains(closeVal, "#N/A") {
-						continue
-					}
-
-					//校验表格中的日期格式
-					if strings.Contains(createDate, "-") {
-						//如果是带有 - 的普通日期格式文本
-						_, timeErr := time.Parse("2006-1-2", createDate)
-						if timeErr != nil {
-							failItem := new(models.EdbdataImportFail)
-							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-							failItem.ClassifyName = classifyName
-							failItem.CreateDate = createDate
-							failItem.SecName = secName
-							failItem.Close = closeVal
-							failItem.Remark = "日期格式异常"
-							failItem.Frequency = frequency
-							failItem.Unit = unit
-							failDatas = append(failDatas, failItem)
-							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-							continue
-						}
-					} else if strings.Contains(createDate, "/") {
-						//如果是带有 / 的普通日期格式文本
-						createDateTime, timeErr := time.Parse("2006/1/2", createDate)
-						if timeErr != nil {
-							failItem := new(models.EdbdataImportFail)
-							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-							failItem.ClassifyName = classifyName
-							failItem.CreateDate = createDate
-							failItem.SecName = secName
-							failItem.Close = closeVal
-							failItem.Remark = "日期格式异常"
-							failItem.Frequency = frequency
-							failItem.Unit = unit
-							failDatas = append(failDatas, failItem)
-							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-							continue
-						}
-						createDate = createDateTime.Format("2006-01-02")
-					} else {
-						//可能是excel的日期格式
-						_, tmpErr := strconv.Atoi(createDate)
-						if tmpErr != nil {
-							failItem := new(models.EdbdataImportFail)
-							failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-							failItem.ClassifyName = classifyName
-							failItem.CreateDate = createDate
-							failItem.SecName = secName
-							failItem.Close = closeVal
-							failItem.Remark = "日期格式异常"
-							failItem.Frequency = frequency
-							failItem.Unit = unit
-							failDatas = append(failDatas, failItem)
-							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-							continue
-						}
-						createDate = utils.ConvertToFormatDay(createDate) //录入日期
-					}
-
-					closeValFloat, err := cells[3].Float() //值
-
-					if err != nil {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = cells[3].Value
-						failItem.Remark = "值类型异常"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						continue
-					}
-					newDecimal := decimal.NewFromFloat(closeValFloat)
-					newDecimal.Round(4)
-					closeVal = newDecimal.String()
-					if strings.Contains(closeVal, "#N/A") {
-						continue
-					}
-					//closeVal := closeValFloat.
-
-					//判断指标,类型,等数据是否正常
-					classifyName = strings.Trim(classifyName, " ")
-					frequency = utils.TrimStr(frequency)
-					unit = utils.TrimStr(unit)
-
-					// 成功数量超过20个指标,那就不导入了
-					if len(targetMap) >= 150 {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "导入指标数量过多"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-						continue
-					}
-
-					if !strings.Contains(strings.Join(classifyNameStrList, ","), classifyName) {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "没有该品种分类权限"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						continue
-					}
-					//fmt.Println(classifyName, createDate, secName, closeVal)
-
-					//获取指标分类信息
-					classify, ok := edbDataClassifyMap[classifyName]
-					if !ok {
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "指标分类不存在"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
-						continue
-					}
-
-					//判断指标是否存在
-					target, ok := targetMap[secName]
-					if !ok {
-						tmpTarget, err := models.GetTargetBySecName(secName)
-						if err != nil {
-							//如果是找不到该指标,那么新增指标
-							if err.Error() == utils.ErrNoRow() {
-								if frequency == "" {
-									failItem := new(models.EdbdataImportFail)
-									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-									failItem.ClassifyName = classifyName
-									failItem.CreateDate = createDate
-									failItem.SecName = secName
-									failItem.Close = closeVal
-									failItem.Remark = "新增指标失败,频度字段为空"
-									failItem.Frequency = frequency
-									failItem.Unit = unit
-									failDatas = append(failDatas, failItem)
-									continue
-								}
-								if unit == "" {
-									failItem := new(models.EdbdataImportFail)
-									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-									failItem.ClassifyName = classifyName
-									failItem.CreateDate = createDate
-									failItem.SecName = secName
-									failItem.Close = closeVal
-									failItem.Remark = "新增指标失败,单位字段为空"
-									failItem.Frequency = frequency
-									failItem.Unit = unit
-									failDatas = append(failDatas, failItem)
-									continue
-								}
-								tmpErr := data.AddEdbInfo(secName, unit, frequency, "", sysUser.Mobile, classify.ClassifyId, sysUser.AdminId)
-								if tmpErr != nil {
-									fmt.Println("line 158")
-									failItem := new(models.EdbdataImportFail)
-									failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-									failItem.ClassifyName = classifyName
-									failItem.CreateDate = createDate
-									failItem.SecName = secName
-									failItem.Close = closeVal
-									failItem.Remark = "新增指标失败"
-									failItem.Frequency = frequency
-									failItem.Unit = unit
-									failDatas = append(failDatas, failItem)
-									continue
-								}
-								tmpTarget, tmpErr := models.GetTargetBySecName(secName)
-								target = tmpTarget
-								targetMap[secName] = target
-							} else {
-								fmt.Println("导入数据 获取指标:Err:" + err.Error())
-							}
-						} else {
-							target = tmpTarget
-							targetMap[secName] = target
-						}
-
-						//设置10分钟缓存,不允许其他地方删除
-						key := "import:edbinfo:data:" + target.TradeCode
-						utils.Rc.SetNX(key, 1, time.Second*600)
-					}
-
-					if target == nil {
-						fmt.Println("指标不存在")
-						failItem := new(models.EdbdataImportFail)
-						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
-						failItem.ClassifyName = classifyName
-						failItem.CreateDate = createDate
-						failItem.SecName = secName
-						failItem.Close = closeVal
-						failItem.Remark = "指标不存在"
-						failItem.Frequency = frequency
-						failItem.Unit = unit
-						failDatas = append(failDatas, failItem)
-						continue
-					}
-
-					//更新指标信息
-					updateCols := make([]string, 0)
-					//更新指标分类
-					if target.ClassifyId <= 0 && classify.ClassifyId > 0 {
-						target.ClassifyId = classify.ClassifyId
-						updateCols = append(updateCols, "ClassifyId")
-					}
-					if target.Frequency != frequency {
-						target.Frequency = frequency
-						target.NoticeTime = ""
-						updateCols = append(updateCols, "Frequency", "NoticeTime")
-					}
-					if target.Unit != unit {
-						target.Unit = unit
-						updateCols = append(updateCols, "Unit")
-					}
-					if len(updateCols) > 0 {
-						_ = target.Update(updateCols)
-					}
-
-					// 判断指标数据列表是否已经存在
-					tmpDataMap, ok := edbCodeDataMap[target.TradeCode]
-					if !ok {
-						tmpDataMap = make(map[string]string)
-						dataList, tmpErr := models.GetTargetsDataList(target.TradeCode)
-						if tmpErr != nil {
-							go alarm_msg.SendAlarmMsg("导入数据"+target.TradeCode+" 获取指标的数据失败,Err:"+err.Error(), 3)
-						}
-						for _, tmpData := range dataList {
-							tmpDataMap[tmpData.Dt] = tmpData.Close
-						}
-						edbCodeDataMap[target.TradeCode] = tmpDataMap
-					}
-
-					//判断数据是否已经存在
-					tmpVal, ok := tmpDataMap[createDate]
-					//数据已存在,进行更新操作
-					if ok {
-						if tmpVal != closeVal {
-							err = models.ModifyTargetsDataByImport(target.TradeCode, createDate, closeVal)
-							if err != nil {
-								go alarm_msg.SendAlarmMsg("导入数据 修改数据失败,Err:"+err.Error(), 3)
-								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
-							}
-						}
-					} else { //数据不存在,进行新增操作
-						if target.TradeCode != "" && createDate != "" && closeVal != "" {
-							models.AddTargetsDataByImport(target.TradeCode, createDate, closeVal)
-							if err != nil {
-								go alarm_msg.SendAlarmMsg("导入数据 新增数据失败,Err:"+err.Error(), 3)
-								//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
-							}
-							tmpDataMap[createDate] = closeVal
-							edbCodeDataMap[target.TradeCode] = tmpDataMap
-						}
-					}
-					successCount++
-				} else {
-					//br.ErrMsg = "请填写导入内容"
-					//br.Msg = "请填写导入内容"
-					//return
-				}
-			}
-		}
-	}
-	resp := models.EdbdataImportResp{
-		SuccessCount: successCount,
-		FailCount:    len(failDatas),
-	}
-	fmt.Println("failDatas:", len(failDatas))
-	if len(failDatas) > 0 {
-		//先删除导入失败记录
-		_ = models.DelEdbDataImportFail(sysUser.AdminId)
-		//错误信息字符串切片,最后作为发送邮件通知使用
-		failContents := make([]string, 0)
-		for _, v := range failDatas {
-			failContents = append(failContents, fmt.Sprint(v.SecName, "导入失败:", v.Remark))
-			err = models.AddEdbdataImportFail(v)
-			if err != nil {
-				go alarm_msg.SendAlarmMsg("导入数据 新增失败记录失败,Err:"+err.Error(), 3)
-				//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增失败记录失败:Err:"+err.Error(), utils.EmailSendToUsers)
-			}
-		}
-		//导入失败的话,最后统一邮件提醒就好啦,不需要每次都去提醒
-		go alarm_msg.SendAlarmMsg("导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), 3)
-		//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 存在部分数据导入失败:"+strings.Join(failContents, ";"), utils.EmailSendToUsers)
-
-		if successCount == 0 {
-			resp.Status = -1
-			resp.Msg = "导入失败"
-		} else {
-			resp.Status = 1
-			resp.Msg = "存在部分导入失败"
-		}
-	} else {
-		resp.Status = 0
-	}
-	br.Msg = "导入成功"
-	br.Ret = 200
-	br.Success = true
-	br.Data = resp
-}
-
 // ImportFailListDownload
 // @Title 下载失败列表
 // @Description 下载失败列表
@@ -1648,6 +1223,15 @@ func (this *TargetController) DataDelete() {
 		br.ErrMsg = "删除失败,Err:" + err.Error()
 		return
 	}
+
+	//将该指标的code加入到 “手工数据导入后刷新” 缓存
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, req.TradeCode)
+		if err != nil {
+			fmt.Println("DataDelete CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+		}
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "删除成功"

+ 56 - 52
go.mod

@@ -4,52 +4,53 @@ go 1.21.7
 
 require (
 	baliance.com/gooxml v1.0.1
-	github.com/PuerkitoBio/goquery v1.9.1
-	github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.2
-	github.com/alibabacloud-go/alimt-20181012/v2 v2.2.0
-	github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6
-	github.com/alibabacloud-go/dm-20151123/v2 v2.0.9
+	github.com/PuerkitoBio/goquery v1.9.2
+	github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.3
+	github.com/alibabacloud-go/alimt-20181012/v2 v2.3.0
+	github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8
+	github.com/alibabacloud-go/dm-20151123/v2 v2.2.1
 	github.com/alibabacloud-go/tea v1.2.2
-	github.com/alibabacloud-go/tea-utils/v2 v2.0.5
-	github.com/aliyun/alibaba-cloud-sdk-go v1.62.695
+	github.com/alibabacloud-go/tea-utils/v2 v2.0.6
+	github.com/aliyun/alibaba-cloud-sdk-go v1.62.800
 	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
 	github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
-	github.com/aws/aws-sdk-go v1.51.2
+	github.com/aws/aws-sdk-go v1.55.4
 	github.com/beego/bee/v2 v2.1.0
-	github.com/beego/beego/v2 v2.1.0
-	github.com/beevik/etree v1.3.0
+	github.com/beego/beego/v2 v2.2.2
+	github.com/beevik/etree v1.4.1
+	github.com/dengsgo/math-engine v0.0.0-20230823154425-78f211b48149
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-ldap/ldap v3.0.3+incompatible
-	github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc
-	github.com/go-sql-driver/mysql v1.7.0
+	github.com/go-redis/redis/v8 v8.11.5
+	github.com/go-sql-driver/mysql v1.8.1
 	github.com/go-xorm/xorm v0.7.9
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
-	github.com/gorilla/websocket v1.5.1
+	github.com/gorilla/websocket v1.5.3
 	github.com/h2non/filetype v1.1.3
 	github.com/jung-kurt/gofpdf v1.16.2
 	github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53
-	github.com/microcosm-cc/bluemonday v1.0.26
-	github.com/minio/minio-go/v7 v7.0.69
+	github.com/microcosm-cc/bluemonday v1.0.27
+	github.com/minio/minio-go/v7 v7.0.74
 	github.com/mojocn/base64Captcha v1.3.6
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.32
 	github.com/qiniu/qmgo v1.1.8
 	github.com/rdlucklib/rdluck_tools v1.0.3
-	github.com/shopspring/decimal v1.3.1
+	github.com/shopspring/decimal v1.4.0
 	github.com/silenceper/wechat/v2 v2.1.6
-	github.com/spf13/viper v1.7.0
+	github.com/spf13/viper v1.19.0
 	github.com/tealeg/xlsx v1.0.5
-	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr v1.0.873
-	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.880
-	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.880
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr v1.0.973
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.973
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.973
 	github.com/xuri/excelize/v2 v2.8.1
-	github.com/yidane/formula v0.0.0-20220322063702-c9da84ba3476
-	go.mongodb.org/mongo-driver v1.15.0
-	golang.org/x/net v0.21.0
+	go.mongodb.org/mongo-driver v1.16.0
+	golang.org/x/net v0.27.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 )
 
 require (
+	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
 	github.com/alibabacloud-go/debug v1.0.0 // indirect
 	github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
@@ -62,7 +63,6 @@ require (
 	github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
 	github.com/aliyun/credentials-go v1.3.1 // indirect
 	github.com/andybalholm/cascadia v1.3.2 // indirect
-	github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02 // indirect
 	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
@@ -71,13 +71,14 @@ require (
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/fatih/structs v1.1.0 // indirect
-	github.com/fsnotify/fsnotify v1.6.0 // indirect
+	github.com/fsnotify/fsnotify v1.7.0 // indirect
+	github.com/go-ini/ini v1.67.0 // indirect
 	github.com/go-playground/locales v0.13.0 // indirect
 	github.com/go-playground/universal-translator v0.17.0 // indirect
 	github.com/go-playground/validator/v10 v10.4.1 // indirect
+	github.com/goccy/go-json v0.10.3 // indirect
 	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
-	github.com/golang/protobuf v1.5.3 // indirect
-	github.com/golang/snappy v0.0.1 // indirect
+	github.com/golang/snappy v0.0.4 // indirect
 	github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac // indirect
 	github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
 	github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2 // indirect
@@ -85,63 +86,66 @@ require (
 	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
 	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
 	github.com/google/uuid v1.6.0 // indirect
-	github.com/gorilla/css v1.0.0 // indirect
+	github.com/gorilla/css v1.0.1 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/klauspost/compress v1.17.6 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.6 // indirect
+	github.com/klauspost/compress v1.17.9 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.8 // indirect
 	github.com/leodido/go-urn v1.2.0 // indirect
-	github.com/magiconair/properties v1.8.1 // indirect
+	github.com/magiconair/properties v1.8.7 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/minio/md5-simd v1.1.2 // indirect
-	github.com/minio/sha256-simd v1.0.1 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
-	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+	github.com/montanaflynn/stats v0.7.1 // indirect
 	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
-	github.com/pelletier/go-toml v1.9.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
-	github.com/prometheus/client_golang v1.16.0 // indirect
-	github.com/prometheus/client_model v0.3.0 // indirect
-	github.com/prometheus/common v0.42.0 // indirect
-	github.com/prometheus/procfs v0.10.1 // indirect
+	github.com/prometheus/client_golang v1.19.0 // indirect
+	github.com/prometheus/client_model v0.5.0 // indirect
+	github.com/prometheus/common v0.48.0 // indirect
+	github.com/prometheus/procfs v0.12.0 // indirect
 	github.com/richardlehane/mscfb v1.0.4 // indirect
 	github.com/richardlehane/msoleps v1.0.3 // indirect
 	github.com/rs/xid v1.5.0 // indirect
+	github.com/sagikazarmark/locafero v0.4.0 // indirect
+	github.com/sagikazarmark/slog-shim v0.1.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
-	github.com/sirupsen/logrus v1.9.3 // indirect
-	github.com/spf13/afero v1.1.2 // indirect
-	github.com/spf13/cast v1.5.0 // indirect
-	github.com/spf13/jwalterweatherman v1.0.0 // indirect
+	github.com/sirupsen/logrus v1.9.0 // indirect
+	github.com/sourcegraph/conc v0.3.0 // indirect
+	github.com/spf13/afero v1.11.0 // indirect
+	github.com/spf13/cast v1.6.0 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
-	github.com/subosito/gotenv v1.2.0 // indirect
+	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/tidwall/gjson v1.14.1 // indirect
 	github.com/tidwall/match v1.1.1 // indirect
 	github.com/tidwall/pretty v1.2.0 // indirect
 	github.com/tjfoc/gmsm v1.3.2 // indirect
+	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/scram v1.1.2 // indirect
 	github.com/xdg-go/stringprep v1.0.4 // indirect
 	github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
 	github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
-	golang.org/x/crypto v0.19.0 // indirect
-	golang.org/x/image v0.15.0 // indirect
-	golang.org/x/sync v0.2.0 // indirect
-	golang.org/x/sys v0.17.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
-	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
-	google.golang.org/protobuf v1.30.0 // indirect
+	go.uber.org/atomic v1.9.0 // indirect
+	go.uber.org/multierr v1.9.0 // indirect
+	golang.org/x/crypto v0.25.0 // indirect
+	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
+	golang.org/x/image v0.14.0 // indirect
+	golang.org/x/sync v0.7.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
+	golang.org/x/time v0.5.0 // indirect
+	google.golang.org/protobuf v1.34.1 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	xorm.io/builder v0.3.6 // indirect
 	xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb // indirect

+ 127 - 266
go.sum

@@ -3,27 +3,19 @@ baliance.com/gooxml v1.0.1/go.mod h1:+gpUgmkAF4zCtwOFPNRLDAvpVRWoKs5EeQTSv/HYFnw
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
+cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
 github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
-github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
-github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.2 h1:enQwehstpeaAnsyse1Aqb6r0sU5UJbiNvIqVmPo+KWI=
-github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.2/go.mod h1:SQq4xfIdvf6WYKSDxAJc+xOJdolt+/bc1jnQKMtPMvQ=
+github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
+github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
+github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.3 h1:vrA6+R1BMLKMTbos8jAeuBrImHPGtY4gTlcue3OIej8=
+github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.3/go.mod h1:SQq4xfIdvf6WYKSDxAJc+xOJdolt+/bc1jnQKMtPMvQ=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
@@ -33,17 +25,17 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
 github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
-github.com/alibabacloud-go/alimt-20181012/v2 v2.2.0 h1:9AwDOjOZvhycl60jlXuMBSSl52rpWlcAuwxJOAQM4Bo=
-github.com/alibabacloud-go/alimt-20181012/v2 v2.2.0/go.mod h1:4gZhZ+BvRg/k14Z8SZnmu86zNqjslSpcC1wFl0jabl4=
+github.com/alibabacloud-go/alimt-20181012/v2 v2.3.0 h1:IDXPw5MOdK3EmDw3xFLtm6FgXnaYvj981UCyHY9GDG8=
+github.com/alibabacloud-go/alimt-20181012/v2 v2.3.0/go.mod h1:NR9bEoCnxfA6/7Q3VAqUOD76ervfR12m+3nw4WuKjP8=
 github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6 h1:y1K+zKhpWcxso8zqI03CcYuwgyZPFwQdwAQOXAeuOVM=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
+github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.7/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
+github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8 h1:benoD0QHDrylMzEQVpX/6uKtrN8LohT66ZlKXVJh7pM=
+github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.8/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
 github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
 github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA=
 github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
-github.com/alibabacloud-go/dm-20151123/v2 v2.0.9 h1:3OPVk25YWcJ48whyVCFOf168dafhxSh7uRo+DFNNyg0=
-github.com/alibabacloud-go/dm-20151123/v2 v2.0.9/go.mod h1:AhCnEI1csfLmYL5fS5TbrxPR2xZZHRQ9MmkF04Iyez8=
+github.com/alibabacloud-go/dm-20151123/v2 v2.2.1 h1:qE4yvFGOyM+AukL7ALK7wQw17WVWXki5td0M5vn9QzE=
+github.com/alibabacloud-go/dm-20151123/v2 v2.2.1/go.mod h1:ogBn6PUJHYDhbZNC3gqOhdj22+zGpeyAR05/EY7aRJA=
 github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
 github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
 github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
@@ -71,9 +63,9 @@ github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQ
 github.com/alibabacloud-go/tea-utils v1.3.6 h1:bVjrxHztM8hAs6nOfLWCgxQfAtKb9RgFFMV6J3rdvB4=
 github.com/alibabacloud-go/tea-utils v1.3.6/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
 github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=
-github.com/alibabacloud-go/tea-utils/v2 v2.0.4/go.mod h1:sj1PbjPodAVTqGTA3olprfeeqqmwD0A5OQz94o9EuXQ=
-github.com/alibabacloud-go/tea-utils/v2 v2.0.5 h1:EUakYEUAwr6L3wLT0vejIw2rc0IA1RSXDwLnIb3f2vU=
 github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
+github.com/alibabacloud-go/tea-utils/v2 v2.0.6 h1:ZkmUlhlQbaDC+Eba/GARMPy6hKdCLiSke5RsN5LcyQ0=
+github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
 github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
 github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
 github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
@@ -85,8 +77,8 @@ github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j
 github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
 github.com/alicebob/miniredis/v2 v2.30.0 h1:uA3uhDbCxfO9+DI/DuGeAMr9qI+noVWwGPNTFuKID5M=
 github.com/alicebob/miniredis/v2 v2.30.0/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q=
-github.com/aliyun/alibaba-cloud-sdk-go v1.62.695 h1:Lk9qjMhhkzZaD4eyx23v0E2+4nAIfwreJ/ecKdaTU6E=
-github.com/aliyun/alibaba-cloud-sdk-go v1.62.695/go.mod h1:CJJYa1ZMxjlN/NbXEwmejEnBkhi0DV+Yb3B2lxf+74o=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.800 h1:1NucX8xUg3F6m8Z6ermSniqyNXvUsfaW7B6CRMTpc0s=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.800/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
 github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
@@ -94,39 +86,31 @@ github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/x
 github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
 github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
 github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
-github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02 h1:o2oaBQGTzO+xNh12e7xWkphNe7H2DTiWv1ml9a2P9PQ=
-github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
-github.com/aws/aws-sdk-go v1.51.2 h1:Ruwgz5aqIXin5Yfcgc+PCzoqW5tEGb9aDL/JWDsre7k=
-github.com/aws/aws-sdk-go v1.51.2/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-sdk-go v1.55.4 h1:u7sFWQQs5ivGuYvCxi7gJI8nN/P9Dq04huLaw39a4lg=
+github.com/aws/aws-sdk-go v1.55.4/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
 github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 github.com/beego/bee/v2 v2.1.0 h1:4WngbAnkvVOyKy74WXcRH3clon76wkjhuzrV2mx2fQU=
 github.com/beego/bee/v2 v2.1.0/go.mod h1:wDhKy5TNxv46LHKsK2gyxo38ObCOm9PbCN89lWHK3EU=
-github.com/beego/beego/v2 v2.1.0 h1:Lk0FtQGvDQCx5V5yEu4XwDsIgt+QOlNjt5emUa3/ZmA=
-github.com/beego/beego/v2 v2.1.0/go.mod h1:6h36ISpaxNrrpJ27siTpXBG8d/Icjzsc7pU1bWpp0EE=
+github.com/beego/beego/v2 v2.2.2 h1:h6TNybAiMPXx9RXxK71Wz+JkPE7rpsL+ctjSZpv5yB0=
+github.com/beego/beego/v2 v2.2.2/go.mod h1:A3BC73uulBnqW3O1uBEN7q+oykprxipZTYRdZtEuKyY=
 github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
 github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
-github.com/beevik/etree v1.3.0 h1:hQTc+pylzIKDb23yYprodCWWTt+ojFfUZyzU09a/hmU=
-github.com/beevik/etree v1.3.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
+github.com/beevik/etree v1.4.1 h1:PmQJDDYahBGNKDcpdX8uPy1xRCwoCGVUiW669MEirVI=
+github.com/beevik/etree v1.4.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
 github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
 github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
 github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
@@ -139,26 +123,23 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
 github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
 github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dengsgo/math-engine v0.0.0-20230823154425-78f211b48149 h1:TkVfb0s14IUHDGvjQfq3f0PZnV1zh609did4DrnD4q4=
+github.com/dengsgo/math-engine v0.0.0-20230823154425-78f211b48149/go.mod h1:zkR27k4K0I8FS6rkEd8qBhPeS8i3X2FKfvSPdF64OpQ=
 github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
 github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -169,22 +150,22 @@ github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox
 github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
 github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
 github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
-github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
-github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
 github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
+github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHjsvuZyatzwk=
@@ -200,36 +181,33 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
 github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
 github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
 github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
 github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc h1:jZY+lpZB92nvBo2f31oPC/ivGll6NcsnEOORm8Fkr4M=
-github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25mL1NKxbJhB63ihiK8MnNeTRd+xAizd6bOdydrTLUQ=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
-github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
 github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
 github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
+github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
+github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@@ -238,12 +216,11 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac h1:Q0Jsdxl5jbxouNs1TQYt0gxesYMU4VXRbsTlgDloZ50=
 github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
@@ -260,7 +237,6 @@ github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP
 github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b h1:fbskpz/cPqWH8VqkQ7LJghFkl2KPAiIFUHrTJ2O3RGk=
 github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -268,70 +244,42 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
-github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
+github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
+github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
-github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
 github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
-github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
 github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -345,14 +293,13 @@ github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5Pt
 github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53 h1:+8X3HMX8A2QhvNg3dImiQTCiVUt6BQXz1mW+/DrWI+k=
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53/go.mod h1:E61jD6q4yJ6Cu9uDGRAfiENM1G5TVZhOog0Y3+GgTpQ=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
-github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
+github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
+github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
 github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
-github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
+github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -369,35 +316,21 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
 github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
-github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
-github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
+github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
-github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ=
-github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
-github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/minio/minio-go/v7 v7.0.74 h1:fTo/XlPBTSpo3BAMshlwKL5RspXRv9us5UeHEGYCFe0=
+github.com/minio/minio-go/v7 v7.0.74/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -411,8 +344,9 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw=
 github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E=
-github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
+github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -421,7 +355,6 @@ github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19/go.mod h1:Lj
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
 github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -436,17 +369,15 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
 github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
-github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
-github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
 github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
 github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.9.2 h1:7NiByeVF4jKSG1lDF3X8LTIkq2/bu+1uYbIm1eS5tzk=
-github.com/pelletier/go-toml v1.9.2/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
 github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
 github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -454,37 +385,32 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
-github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
+github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
+github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
-github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
 github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
-github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
+github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
+github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
-github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
 github.com/qiniu/qmgo v1.1.8 h1:E64M+P59aqQpXKI24ClVtluYkLaJLkkeD2hTVhrdMks=
 github.com/qiniu/qmgo v1.1.8/go.mod h1:QvZkzWNEv0buWPx0kdZsSs6URhESVubacxFPlITmvB8=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
@@ -496,22 +422,22 @@ github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK
 github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
 github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
 github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
+github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
-github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
-github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
 github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
 github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
 github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
@@ -519,32 +445,30 @@ github.com/silenceper/wechat/v2 v2.1.6 h1:2br2DxNzhksmvIBJ+PfMqjqsvoZmd/5BnMIfjK
 github.com/silenceper/wechat/v2 v2.1.6/go.mod h1:7Iu3EhQYVtDUJAj+ZVRy8yom75ga7aDWv8RurLkVm0s=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
 github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
-github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
-github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
-github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
+github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
 github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -552,21 +476,22 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
-github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
 github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
 github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
 github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr v1.0.873 h1:CbeN5Fdzq3xea36+ZKPQcuRwwJk0ZYQRxcyWkyK5768=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr v1.0.873/go.mod h1:vzSh5OxbOCyFt+SdlEd9oCQGBb1oObkD7Xfod/UPvVk=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.873/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.880 h1:0Ok1pZ06/zZMCiW8Dm8wYOGJK1HCU5OXwNSyE5UVOAM=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.880/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.880 h1:PnzU5KS7x3LQGE0yetGLEGwJtLb6Uzsd79mbCiRh1rw=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.880/go.mod h1:5DGl21YNTtHXi9+QIr8XmlSelukl+Weaz+beLQQ+NE0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr v1.0.973 h1:Vi7wT+fjCLzdlSAq87cjVHZwRAPgNjTtYvXCVeNZpd0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr v1.0.973/go.mod h1:rZKC4v/5EbMhXe35m+/44RJ93xuZWEYGKmzxMBYzqjY=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.973 h1:XtrGe/XEyusLgGiv1DpEg9SRR7sxsSbIgAhMxcEVbjY=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.973/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.973 h1:fd68qywl6VYMTt1C41VJJI7p4kyOXVmm54uVgUKpU60=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.973/go.mod h1:MdyACChA7ITk0lIom3MjaCKp8s8Yykkq3vKAporZGdc=
 github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
 github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -576,12 +501,13 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
 github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
 github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
 github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
 github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
 github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
 github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
 github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@@ -591,15 +517,12 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k
 github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
 github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
 github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1jLZA6uaoiwwH3vSuF3IW0=
 github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
 github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ=
 github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE=
 github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4=
 github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
-github.com/yidane/formula v0.0.0-20220322063702-c9da84ba3476 h1:66fLxv8xlhSr42ZhVAYjUY/sEF0olUUAESVlsxVduuw=
-github.com/yidane/formula v0.0.0-20220322063702-c9da84ba3476/go.mod h1:9/dQiKiN04yPMdgsuFmKGuI2Hdp6OmFV9gSWS1col6g=
 github.com/ylywyn/jpush-api-go-client v0.0.0-20190906031852-8c4466c6e369/go.mod h1:Nv7wKD2/bCdKUFNKcJRa99a+1+aSLlCRJFriFYdjz/I=
 github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
 github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
@@ -612,24 +535,18 @@ github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRF
 github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
 github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
 github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.mongodb.org/mongo-driver v1.11.6/go.mod h1:G9TgswdsWjX4tmDA5zfs2+6AEPpYJwqblyjsfuh8oXY=
-go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
-go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
+go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4=
+go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
 go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
+go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -639,33 +556,27 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
 golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
 golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
-golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
-golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
-golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
-golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
+golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
+golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -674,18 +585,12 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -703,11 +608,10 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
 golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
-golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -719,14 +623,11 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
-golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -734,10 +635,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -755,15 +652,14 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
-golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -783,33 +679,22 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
-golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -824,30 +709,14 @@ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
 gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -856,8 +725,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
@@ -869,22 +738,17 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
 gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
 gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -900,9 +764,6 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
 xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=

+ 1 - 1
main.go

@@ -11,7 +11,7 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
 
-	"github.com/beego/beego/v2/adapter/logs"
+	"github.com/beego/beego/v2/core/logs"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web/context"
 )

+ 34 - 35
models/business_conf.go

@@ -10,44 +10,43 @@ import (
 )
 
 const (
-	BusinessConfUseXf                     = "UseXf"
-	BusinessConfXfAppid                   = "XfAppid"
-	BusinessConfXfApiKey                  = "XfApiKey"
-	BusinessConfXfApiSecret               = "XfApiSecret"
-	BusinessConfXfVcn                     = "XfVcn"
-	BusinessConfEnPptCoverImgs            = "EnPptCoverImgs"
-	BusinessConfIsReportApprove           = "IsReportApprove"
-	BusinessConfReportApproveType         = "ReportApproveType"
-	BusinessConfCompanyName               = "CompanyName"
-	BusinessConfCompanyWatermark          = "CompanyWatermark"
-	BusinessConfWatermarkChart            = "WatermarkChart"
-	BusinessConfLoginSmsTpId              = "LoginSmsTpId"
-	BusinessConfLoginSmsGjTpId            = "LoginSmsGjTpId"
-	BusinessConfSmsJhgnAppKey             = "SmsJhgnAppKey"
-	BusinessConfSmsJhgjAppKey             = "SmsJhgjAppKey"
-	BusinessConfLdapHost                  = "LdapHost"
-	BusinessConfLdapBase                  = "LdapBase"
-	BusinessConfLdapPort                  = "LdapPort"
-	BusinessConfEmailClient               = "EmailClient"
-	BusinessConfEmailServerHost           = "EmailServerHost"
-	BusinessConfEmailServerPort           = "EmailServerPort"
-	BusinessConfEmailSender               = "EmailSender"
-	BusinessConfEmailSenderUserName       = "EmailSenderUserName"
-	BusinessConfEmailSenderPassword       = "EmailSenderPassword"
-	BusinessConfSmsClient                 = "SmsClient"
-	BusinessConfNanHuaSmsAppKey           = "NanHuaSmsAppKey"
-	BusinessConfNanHuaSmsAppSecret        = "NanHuaSmsAppSecret"
-	BusinessConfNanHuaSmsApiHost          = "NanHuaSmsApiHost"
-	BusinessConfLoginSmsTplContent        = "LoginSmsTplContent"
-	BusinessConfLoginEmailTemplateSubject = "LoginEmailTemplateSubject"
-	BusinessConfLoginEmailTemplateContent = "LoginEmailTemplateContent"
-	BusinessConfLdapBindUserSuffix        = "LdapBindUserSuffix"
-	BusinessConfLdapUserFilter            = "LdapUserFilter"
-
+	BusinessConfUseXf                        = "UseXf"
+	BusinessConfXfAppid                      = "XfAppid"
+	BusinessConfXfApiKey                     = "XfApiKey"
+	BusinessConfXfApiSecret                  = "XfApiSecret"
+	BusinessConfXfVcn                        = "XfVcn"
+	BusinessConfEnPptCoverImgs               = "EnPptCoverImgs"
+	BusinessConfIsReportApprove              = "IsReportApprove"
+	BusinessConfReportApproveType            = "ReportApproveType"
+	BusinessConfCompanyName                  = "CompanyName"
+	BusinessConfCompanyWatermark             = "CompanyWatermark"
+	BusinessConfWatermarkChart               = "WatermarkChart"
+	BusinessConfLoginSmsTpId                 = "LoginSmsTpId"
+	BusinessConfLoginSmsGjTpId               = "LoginSmsGjTpId"
+	BusinessConfSmsJhgnAppKey                = "SmsJhgnAppKey"
+	BusinessConfSmsJhgjAppKey                = "SmsJhgjAppKey"
+	BusinessConfLdapHost                     = "LdapHost"
+	BusinessConfLdapBase                     = "LdapBase"
+	BusinessConfLdapPort                     = "LdapPort"
+	BusinessConfEmailClient                  = "EmailClient"
+	BusinessConfEmailServerHost              = "EmailServerHost"
+	BusinessConfEmailServerPort              = "EmailServerPort"
+	BusinessConfEmailSender                  = "EmailSender"
+	BusinessConfEmailSenderUserName          = "EmailSenderUserName"
+	BusinessConfEmailSenderPassword          = "EmailSenderPassword"
+	BusinessConfSmsClient                    = "SmsClient"
+	BusinessConfNanHuaSmsAppKey              = "NanHuaSmsAppKey"
+	BusinessConfNanHuaSmsAppSecret           = "NanHuaSmsAppSecret"
+	BusinessConfNanHuaSmsApiHost             = "NanHuaSmsApiHost"
+	BusinessConfLoginSmsTplContent           = "LoginSmsTplContent"
+	BusinessConfLoginEmailTemplateSubject    = "LoginEmailTemplateSubject"
+	BusinessConfLoginEmailTemplateContent    = "LoginEmailTemplateContent"
+	BusinessConfLdapBindUserSuffix           = "LdapBindUserSuffix"
+	BusinessConfLdapUserFilter               = "LdapUserFilter"
+	BusinessConfSmsJhgjVariable              = "SmsJhgjVariable"              // 聚合国际短信变量
 	BusinessConfTencentApiSecretId           = "TencentApiSecretId"           // 腾讯云API-密钥对
 	BusinessConfTencentApiSecretKey          = "TencentApiSecretKey"          // 腾讯云API-密钥对
 	BusinessConfTencentApiRecTaskCallbackUrl = "TencentApiRecTaskCallbackUrl" // 腾讯云API-语音识别回调地址
-	BusinessConfSmsJhgjVariable              = "SmsJhgjVariable"              // 聚合国际短信变量
 
 	BusinessConfEdbStopRefreshRule = "EdbStopRefreshRule" // 是否停止指标刷新规则
 )

+ 8 - 0
models/chart_permission.go

@@ -215,6 +215,14 @@ func (c *ChartPermission) GetFirstChartPermissionByParentId(parentId int) (item
 	return
 }
 
+// GetChartPermissionList 获取品种权限列表
+func GetChartPermissionList() (list []*ChartPermission, err error) {
+	o := orm.NewOrmUsingDB("weekly")
+	sql := `SELECT * FROM chart_permission ORDER BY product_id ASC, sort ASC`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
 // GetChartPermissionById 主键获取品种
 func GetChartPermissionById(permissionId int) (item *ChartPermission, err error) {
 	o := orm.NewOrmUsingDB("rddp")

+ 3 - 2
models/data_manage/base_from_trade_index.go

@@ -256,6 +256,7 @@ type BaseFromCoalmineFirmIndex struct {
 	IndexName                   string // 省份/企业名称
 	IndexCode                   string // 持买单量指标编码
 	DataTime                    string // 指标时间
+	DataTimeDate                string // 指标时间
 	DealValue                   string // 数据量
 	GroupName                   string // 集团名
 	Source                      string // 来源
@@ -477,7 +478,7 @@ func GetClassifyFirmByGroupName(groupName string) (items []*string, err error) {
 // 查询指标
 func GetPageFromCoalmineFirmIndexByFrequency(frequency, classify string, startSize, pageSize int) (items []*BaseFromCoalmineFirmIndex, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := `SELECT * FROM base_from_coalmine_firm_index WHERE frequency=? AND index_code=? ORDER BY data_time DESC LIMIT ?,?  `
+	sql := `SELECT * FROM base_from_coalmine_firm_index WHERE frequency=? AND index_code=? ORDER BY data_time_date DESC LIMIT ?,?  `
 	_, err = o.Raw(sql, frequency, classify, startSize, pageSize).QueryRows(&items)
 	return
 }
@@ -698,6 +699,6 @@ type BaseFromCoalmineClassify struct {
 func GetCoalmineClassifyList() (list []*BaseFromCoalmineClassify, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := "SELECT * FROM base_from_coalmine_classify"
-	_,err = o.Raw(sql).QueryRows(&list)
+	_, err = o.Raw(sql).QueryRows(&list)
 	return
 }

+ 236 - 71
models/data_manage/chart_info.go

@@ -58,6 +58,7 @@ type ChartInfo struct {
 	UnitEn            string `description:"英文单位名称"`
 	IsJoinPermission  int    `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	ForumChartInfoId  int    `description:"社区的图表ID"`
+	ChartAlias        string `description:"图表别名"`
 }
 
 type ChartInfoMore struct {
@@ -261,6 +262,7 @@ type EditChartInfoReq struct {
 	MarkersAreas         string                  `description:"标识区"`
 	Unit                 string                  `description:"中文单位名称"`
 	UnitEn               string                  `description:"英文单位名称"`
+	ChartAlias           string                  `description:"图表别名"`
 }
 
 type EditFutureGoodChartInfoReq struct {
@@ -321,6 +323,7 @@ type EditChartEnInfoReq struct {
 	ChartNameEn      string                    `description:"英文图表名称"`
 	ChartEdbInfoList []*EditChartEnInfoEdbItem `description:"指标及配置信息"`
 	ExtraConfig      string                    `description:"图表额外配置信息,json字符串"`
+	ChartAlias       string                    `description:"图表别名"`
 }
 type EditChartEnInfoEdbItem struct {
 	EdbInfoId int    `description:"指标ID"`
@@ -335,6 +338,7 @@ type EditChartInfoBaseReq struct {
 	ChartName        string                      `description:"图表名称(根据语言版本区分)"`
 	ChartEdbInfoList []*EditChartInfoEdbBaseItem `description:"指标及配置信息"`
 	ExtraConfig      string                      `description:"图表额外配置信息,json字符串"`
+	ChartAlias       string                      `description:"图表别名"`
 }
 type EditChartInfoEdbBaseItem struct {
 	EdbInfoId int    `description:"指标ID"`
@@ -375,6 +379,7 @@ type ChartInfoList struct {
 	CreateTime           time.Time
 	ModifyTime           time.Time
 	EdbInfoList          []*EdbInfoList
+	ChartAlias           string `description:"图表别名"`
 }
 
 type ChartInfoListResp struct {
@@ -1058,6 +1063,9 @@ func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calenda
 	sql += `,min_max_save = ? `
 	pars = append(pars, req.MinMaxSave)
 
+	sql += `,chart_alias = ? `
+	pars = append(pars, req.ChartAlias)
+
 	sql += `WHERE chart_info_id = ?`
 
 	pars = append(pars, req.ChartInfoId)
@@ -1068,85 +1076,115 @@ func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calenda
 	}
 	chartEdbMappingIdList := make([]string, 0)
 
-	for _, v := range chartEdbInfoList {
-		// 查询该指标是否存在,如果存在的话,那么就去修改,否则新增
-		var tmpChartEdbMapping *ChartEdbMapping
-		csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = ? `
-		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId, utils.CHART_SOURCE_DEFAULT).QueryRow(&tmpChartEdbMapping)
-		if err != nil && err.Error() != utils.ErrNoRow() {
-			fmt.Println("QueryRow Err:", err.Error())
-			return err
-		}
-		if tmpChartEdbMapping != nil {
-			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(tmpChartEdbMapping.ChartEdbMappingId))
-			tmpChartEdbMapping.ModifyTime = time.Now()
-			tmpChartEdbMapping.MaxData = v.MaxData
-			tmpChartEdbMapping.MinData = v.MinData
-			tmpChartEdbMapping.IsOrder = v.IsOrder
-			tmpChartEdbMapping.IsAxis = v.IsAxis
-			tmpChartEdbMapping.EdbInfoType = v.EdbInfoType
-			tmpChartEdbMapping.LeadValue = v.LeadValue
-			tmpChartEdbMapping.LeadUnit = v.LeadUnit
-			tmpChartEdbMapping.ChartStyle = v.ChartStyle
-			tmpChartEdbMapping.ChartColor = v.ChartColor
-			tmpChartEdbMapping.PredictChartColor = v.PredictChartColor
-			tmpChartEdbMapping.ChartWidth = v.ChartWidth
-			tmpChartEdbMapping.EdbAliasName = v.EdbAliasName
-			tmpChartEdbMapping.IsConvert = v.IsConvert
-			tmpChartEdbMapping.ConvertType = v.ConvertType
-			tmpChartEdbMapping.ConvertValue = v.ConvertValue
-			tmpChartEdbMapping.ConvertUnit = v.ConvertUnit
-			tmpChartEdbMapping.ConvertEnUnit = v.ConvertEnUnit
-			_, err = to.Update(tmpChartEdbMapping, "ModifyTime", "MaxData", "MinData", "IsOrder", "IsAxis", "EdbInfoType", "LeadValue", "LeadUnit", "ChartStyle", "ChartColor", "PredictChartColor", "ChartWidth", "EdbAliasName",
-				"IsConvert", "ConvertType", "ConvertValue", "ConvertUnit", "ConvertEnUnit")
-			if err != nil {
-				fmt.Println("chart_edb_mapping Err:" + err.Error())
-				return err
-			}
-		} else {
-			mapItem := new(ChartEdbMapping)
-			mapItem.ChartInfoId = req.ChartInfoId
-			mapItem.EdbInfoId = v.EdbInfoId
-			mapItem.CreateTime = time.Now()
-			mapItem.ModifyTime = time.Now()
-			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
-			mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp + "_" + strconv.Itoa(v.EdbInfoId))
-			mapItem.MaxData = v.MaxData
-			mapItem.MinData = v.MinData
-			mapItem.IsOrder = v.IsOrder
-			mapItem.IsAxis = v.IsAxis
-			mapItem.EdbInfoType = v.EdbInfoType
-			mapItem.LeadValue = v.LeadValue
-			mapItem.LeadUnit = v.LeadUnit
-			mapItem.ChartStyle = v.ChartStyle
-			mapItem.ChartColor = v.ChartColor
-			mapItem.PredictChartColor = v.PredictChartColor
-			mapItem.ChartWidth = v.ChartWidth
-			mapItem.Source = utils.CHART_SOURCE_DEFAULT
-			mapItem.EdbAliasName = v.EdbAliasName
-			mapItem.IsConvert = v.IsConvert
-			mapItem.ConvertType = v.ConvertType
-			mapItem.ConvertValue = v.ConvertValue
-			mapItem.ConvertUnit = v.ConvertUnit
-			mapItem.ConvertEnUnit = v.ConvertEnUnit
-			tmpId, err := to.Insert(mapItem)
-			if err != nil {
-				fmt.Println("AddChartEdbMapping Err:" + err.Error())
-				return err
+	// 获取已经配置的关联指标列表
+	var tmpChartEdbMappingList []*ChartEdbMapping
+	csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND source = ? ORDER BY chart_edb_mapping_id ASC`
+	_, err = to.Raw(csql, req.ChartInfoId, utils.CHART_SOURCE_DEFAULT).QueryRows(&tmpChartEdbMappingList)
+	if err != nil {
+		fmt.Println("获取已经配置的关联指标列表 Err:", err.Error())
+		return err
+	}
+
+	// 已经配置的关联指标列表长度下标
+	tmpEdbIndex := len(tmpChartEdbMappingList) - 1
+	// 当下要配置的关联指标列表长度下标
+	reqEdbIndex := len(chartEdbInfoList) - 1
+
+	addChartEdbList := make([]*ChartEdbMapping, 0)
+	removeIdList := make([]int, 0)
+	for k, v := range chartEdbInfoList {
+		// 如果当前下标小于等于已经配置的关联指标的最大长度,那么就校验
+		if k <= tmpEdbIndex {
+			tmpChartEdbMapping := tmpChartEdbMappingList[k]
+
+			// 顺序未变
+			if tmpChartEdbMapping.EdbInfoId == v.EdbInfoId {
+				chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(tmpChartEdbMapping.ChartEdbMappingId))
+				tmpChartEdbMapping.ModifyTime = time.Now()
+				tmpChartEdbMapping.MaxData = v.MaxData
+				tmpChartEdbMapping.MinData = v.MinData
+				tmpChartEdbMapping.IsOrder = v.IsOrder
+				tmpChartEdbMapping.IsAxis = v.IsAxis
+				tmpChartEdbMapping.EdbInfoType = v.EdbInfoType
+				tmpChartEdbMapping.LeadValue = v.LeadValue
+				tmpChartEdbMapping.LeadUnit = v.LeadUnit
+				tmpChartEdbMapping.ChartStyle = v.ChartStyle
+				tmpChartEdbMapping.ChartColor = v.ChartColor
+				tmpChartEdbMapping.PredictChartColor = v.PredictChartColor
+				tmpChartEdbMapping.ChartWidth = v.ChartWidth
+				tmpChartEdbMapping.EdbAliasName = v.EdbAliasName
+				tmpChartEdbMapping.IsConvert = v.IsConvert
+				tmpChartEdbMapping.ConvertType = v.ConvertType
+				tmpChartEdbMapping.ConvertValue = v.ConvertValue
+				tmpChartEdbMapping.ConvertUnit = v.ConvertUnit
+				tmpChartEdbMapping.ConvertEnUnit = v.ConvertEnUnit
+				_, err = to.Update(tmpChartEdbMapping, "ModifyTime", "MaxData", "MinData", "IsOrder", "IsAxis", "EdbInfoType", "LeadValue", "LeadUnit", "ChartStyle", "ChartColor", "PredictChartColor", "ChartWidth", "EdbAliasName",
+					"IsConvert", "ConvertType", "ConvertValue", "ConvertUnit", "ConvertEnUnit")
+				if err != nil {
+					fmt.Println("chart_edb_mapping Err:" + err.Error())
+					return err
+				}
+				continue
+			} else {
+				// 如果是指标的顺序变了,那么需要删除该配置,进行新的配置
+				removeIdList = append(removeIdList, tmpChartEdbMapping.ChartEdbMappingId)
 			}
-			mapItem.ChartEdbMappingId = int(tmpId)
-			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(mapItem.ChartEdbMappingId))
+		}
+
+		// 如果是指标的顺序变了 或者
+		// 如果当前下标是超过了已经配置的关联指标的最大长度,那么就新增
+		mapItem := new(ChartEdbMapping)
+		mapItem.ChartInfoId = req.ChartInfoId
+		mapItem.EdbInfoId = v.EdbInfoId
+		mapItem.CreateTime = time.Now()
+		mapItem.ModifyTime = time.Now()
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+		mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp + "_" + strconv.Itoa(v.EdbInfoId))
+		mapItem.MaxData = v.MaxData
+		mapItem.MinData = v.MinData
+		mapItem.IsOrder = v.IsOrder
+		mapItem.IsAxis = v.IsAxis
+		mapItem.EdbInfoType = v.EdbInfoType
+		mapItem.LeadValue = v.LeadValue
+		mapItem.LeadUnit = v.LeadUnit
+		mapItem.ChartStyle = v.ChartStyle
+		mapItem.ChartColor = v.ChartColor
+		mapItem.PredictChartColor = v.PredictChartColor
+		mapItem.ChartWidth = v.ChartWidth
+		mapItem.Source = utils.CHART_SOURCE_DEFAULT
+		mapItem.EdbAliasName = v.EdbAliasName
+		mapItem.IsConvert = v.IsConvert
+		mapItem.ConvertType = v.ConvertType
+		mapItem.ConvertValue = v.ConvertValue
+		mapItem.ConvertUnit = v.ConvertUnit
+		mapItem.ConvertEnUnit = v.ConvertEnUnit
+
+		addChartEdbList = append(addChartEdbList, mapItem)
+	}
+
+	// 当前保存的指标数与已有的指标数不一致,需要删除已有的指标关联配置
+	if reqEdbIndex < tmpEdbIndex {
+		for i := reqEdbIndex + 1; i <= tmpEdbIndex; i++ {
+			removeIdList = append(removeIdList, tmpChartEdbMappingList[i].ChartEdbMappingId)
 		}
 	}
-	if len(chartEdbMappingIdList) > 0 {
-		chartEdbMappingIdStr := strings.Join(chartEdbMappingIdList, ",")
-		dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND chart_edb_mapping_id NOT IN(` + chartEdbMappingIdStr + `)`
-		_, err = to.Raw(dsql, req.ChartInfoId).Exec()
+
+	// 删除已经移除了的指标关联配置
+	removeNum := len(removeIdList)
+	if removeNum > 0 {
+		dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND chart_edb_mapping_id  IN(` + utils.GetOrmInReplace(removeNum) + `)`
+		_, err = to.Raw(dsql, req.ChartInfoId, removeIdList).Exec()
 		if err != nil {
 			fmt.Println("delete err:" + err.Error())
 			return err
 		}
 	}
+
+	// 将新的指标关联配置写入
+	if len(addChartEdbList) > 0 {
+		_, err = to.InsertMulti(500, addChartEdbList)
+	}
+
 	return
 }
 
@@ -1332,6 +1370,9 @@ func EditChartEnInfoAndEdbEnInfo(req *EditChartEnInfoReq) (err error) {
 		pars = append(pars, req.ExtraConfig)
 	}
 
+	updateField += ` chart_alias =?, `
+	pars = append(pars, req.ChartAlias)
+
 	sql := ` UPDATE  chart_info
 			SET ` + updateField + ` modify_time = NOW()
 			WHERE chart_info_id = ?`
@@ -1405,6 +1446,7 @@ func EditChartBaseInfoAndEdbEnInfo(req *EditChartInfoBaseReq, chartItem *ChartIn
 		chartItem.ExtraConfig = req.ExtraConfig
 		updateChartCols = append(updateChartCols, "ExtraConfig")
 	}
+
 	chartItem.ModifyTime = time.Now()
 	updateChartCols = append(updateChartCols, "ModifyTime")
 	_, err = to.Update(chartItem, updateChartCols...)
@@ -1487,6 +1529,7 @@ type AddChartInfoReq struct {
 	MarkersAreas         string                  `description:"标识区"`
 	Unit                 string                  `description:"中文单位名称"`
 	UnitEn               string                  `description:"英文单位名称"`
+	ChartAlias           string                  `description:"图表别名"`
 }
 
 type AddFutureGoodChartInfoReq struct {
@@ -1504,6 +1547,7 @@ type PreviewChartInfoReq struct {
 	SeasonExtraConfig SeasonExtraItem  `description:"季节性图表中的配置,json数据"`
 	StartYear         int              `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 	ChartSource       int
+	ExtraConfig       string `description:"图表额外配置信息,json字符串"`
 	MarkersLines      string `description:"标识线"`
 }
 
@@ -1941,6 +1985,7 @@ type ChartInfoView struct {
 	IsJoinPermission  int             `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth     bool            `description:"是否有数据权限,默认:false"`
 	ForumChartInfoId  int             `description:"社区的图表ID"`
+	ChartAlias        string          `description:"图表别名"`
 }
 
 type ChartViewButton struct {
@@ -2628,6 +2673,117 @@ type PreviewSeasonChartReq struct {
 	SeasonExtraConfig SeasonExtraItem  `description:"季节图额外配置信息,json字符串"`
 }
 
+// 截面组合图额外配置
+type ChartSectionExtraConf struct {
+	DateConfList        []*ChartSectionDateConfItem
+	IsHeap              int                      `description:"是否堆积(1.堆积,0不堆积)"`
+	XDataList           []XData                  `description:"横轴名称设置"`
+	UnitList            *ChartSectionCombineUnit `description:"纵轴单位设置"`
+	BaseChartSeriesName string                   `description:"基准系列名称"`
+	SortType            int                      `description:"排序类型,0默认,1升序,2降序"`
+}
+
+// 截面组合图额外配置
+type ChartSectionAllExtraConf struct {
+	ChartSectionExtraConf
+	SeriesList []*ChartSectionSeriesItem
+}
+
+type ChartSectionDateConfItem struct {
+	MoveForward    int    `description:"前移的期数"`
+	EdbInfoId      int    `description:"指标ID"`
+	EdbName        string `description:"指标名称"`
+	EdbNameEn      string `description:"指标名称英文"`
+	EdbInfoType    int    `description:"指标类型"`
+	Frequency      string `description:"频度"`
+	EndDate        string `description:"最新日期"`
+	DateType       int    `description:"日期类型:0 指标日期,1系统日期"`
+	DateConfName   string `description:"引用日期名称"` // 引用日期名称不能重复
+	DateConfNameEn string `description:"引用日期英文名称"`
+	DateChange     []*ChartSectionDateChange
+}
+
+// 截面组合图引用日期配置
+type ChartSectionDateChange struct {
+	Year         int
+	Month        int
+	Day          int
+	Frequency    string `description:"频度变换"`
+	FrequencyDay string `description:"频度的固定日期"`
+	ChangeType   int    `description:"日期变换类型1日期位移,2指定频率"`
+}
+
+// 截面组合图系列配置
+type ChartSectionSeriesItem struct {
+	ChartSeriesId int     `description:"系列ID"`
+	SeriesName    string  `description:"系列名称"` //系列名称不可同名
+	SeriesNameEn  string  `description:"系列英文名称"`
+	ChartStyle    string  `description:"图表类型"`
+	ChartColor    string  `description:"颜色"`
+	ChartWidth    int     `description:"线条大小"`
+	IsPoint       int     `description:"是否用数据点展示(0 否,1是)"`
+	IsNumber      int     `description:"是否用数值展示(0 否,1是)"`
+	IsAxis        int     `description:"1:左轴,0:右轴"`
+	MaxData       float64 `description:"上限"`
+	MinData       float64 `description:"下限"`
+	//IsOrder         bool    `description:"true:正序,false:逆序"`
+	EdbInfoList    []*ChartSectionSeriesEdbConf
+	DataList       []float64
+	NoDataEdbIndex []int
+}
+type ChartSectionSeriesEdbConf struct {
+	ChartSeriesEdbMappingId int `description:"映射ID"`
+	ChartSeriesId           int `description:"系列ID"`
+	//ChartInfoId             int `description:"图表ID"`
+	EdbInfoId    int `description:"指标id"`
+	DateConf     *ChartSectionSeriesDateConfItem
+	EdbName      string `description:"中文别名"`
+	EdbNameEn    string `description:"英文别名"`
+	EdbInfoType  int    `description:"指标类型"`
+	Unit         string `description:"单位"`
+	UnitEn       string `description:"英文单位"`
+	DateConfName string `description:"引用日期名称"`
+	DateConfType int    `description:"日期类型,0指标最新日期, 1引用日期"`
+}
+
+type ChartSectionCombineDataResp struct {
+	DateConfList        []*ChartSectionDateConfItem
+	IsHeap              int                      `description:"是否堆积(1.堆积,0不堆积)"`
+	XDataList           []XData                  `description:"横轴名称设置"`
+	UnitList            *ChartSectionCombineUnit `description:"纵轴单位设置"`
+	BaseChartSeriesName string                   `description:"基准系列名称"`
+	SortType            int                      `description:"排序类型,0默认,1升序,2降序"`
+	SeriesList          []*ChartSectionSeriesItem
+	LeftMin             string `description:"图表左侧最小值"`
+	LeftMax             string `description:"图表左侧最大值"`
+	RightMin            string `description:"图表右侧最小值"`
+	RightMax            string `description:"图表右侧最大值"`
+	Right2Min           string `description:"图表右侧最小值"`
+	Right2Max           string `description:"图表右侧最大值"`
+}
+
+// 系列里的指标日期配置
+type ChartSectionSeriesDateConfItem struct {
+	MoveForward int `description:"前移的期数"`
+	DateChange  []*ChartSectionDateChange
+}
+
+// PreviewSectionCombineChartReq 预览截面组合图的请求
+type PreviewSectionCombineChartReq struct {
+	ChartName       string `description:"图表名称"`
+	ChartClassifyId int    `description:"分类id"`
+	ExtraConfig     string `description:"图表额外配置信息,json字符串"`
+}
+
+type ChartSectionCombineUnit struct {
+	LeftName       string `description:"左轴单位"`
+	LeftNameEn     string `description:"左轴英文单位"`
+	RightName      string `description:"右轴单位"`
+	RightNameEn    string `description:"右轴英文单位"`
+	RightTwoName   string `description:"右2轴单位"`
+	RightTwoNameEn string `description:"右2轴英文单位"`
+}
+
 // EditChartInfoImageV2
 // @Description: 修改图表的缩略图
 // @author: Roc
@@ -2648,6 +2804,15 @@ func EditChartInfoImageV2(chartInfoId int, imageUrl string) (err error) {
 	return
 }
 
+// 时序组合图额外配置
+type ChartTimeCombineExtraConf struct {
+	IsHeap int `description:"是否堆积(1.堆积,0不堆积)"`
+}
+
+type ChartTimeCombineDataResp struct {
+	IsHeap int `description:"是否堆积(1.堆积,0不堆积)"`
+}
+
 func getThsHfEdbDataListByMongo(source, subSource, edbInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
 	list = make([]*EdbDataList, 0)
 

+ 268 - 0
models/data_manage/chart_info_range_analysis.go

@@ -0,0 +1,268 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ChartRangeAnalysisExtraConf struct {
+	EdbInfoMode          int                                `description:"指标模式 0: 单指标,1: 多指标"`
+	DateRangeType        int                                `description:"区间划分类型 0:智能划分,1:手工划分,2:跨年划分"`
+	AutoDateConf         ChartRangeAnalysisAutoDateConf     `description:"智能划分时间区间配置"`
+	ManualDateConf       []ChartRangeAnalysisManualDateConf `description:"手工划分时间区间配置"`
+	YearDateConf         ChartRangeAnalysisYearDateConf     `description:"跨年划分时间区间配置"`
+	CalculateType        int                                `description:"计算类型 0: 区间均值,1: 区间累计值,2:区间涨幅,3:区间年化增长率,4:区间最大值,5:区间最小值"`
+	UnNormalDataDealType int                                `description:"异常值处理配置 0:不处理,1:剔除,2替换"`
+	UnNormalDataConf     ChartRangeAnalysisDeleteDataConf
+	DataConvertType      int                               `description:"数据转换类型 0不转, 1乘 2除 3对数"`
+	DataConvertConf      ChartRangeAnalysisDataConvertConf `description:"数据转换详情"`
+	SeriesName           string                            `description:"指标系列名称"`
+	EdbInfoIds           []int
+}
+
+type ChartRangeAnalysisAutoDateChangeConf struct {
+	BaseDateType int `description:"基准日期类型:0指标日期,1系统日期,2固定日期"`
+	MoveForward  int `description:"前移的期数"`
+	DateChange   []*EdbDateConfDateChange
+}
+
+type ChartRangeAnalysisDeleteDataConf struct {
+	Formula string
+	Value   float64
+}
+
+type ChartRangeAnalysisDataConvertConf struct {
+	Value  float64 `description:"数据转换值"`
+	Unit   string  `description:"数据转换单位"`
+	EnUnit string  `description:"数据转换单位"`
+}
+
+type ChartRangeAnalysisManualDateConf struct { //手工划分
+	StartDate string `description:"开始日期"`
+	EndDate   string `description:"结束日期"`
+}
+
+type ChartRangeAnalysisAutoDateConf struct { //智能划分
+	IsAutoStartDate int                                  `description:"起始日期是否是动态设置:0固定,1动态"`
+	StartDate       string                               `description:"起始日期"` //固定模式下,截止日期为指标的最新日期
+	StartDateConf   ChartRangeAnalysisAutoDateChangeConf `description:"动态起始日期配置"`
+	EndDateConf     ChartRangeAnalysisAutoDateChangeConf `description:"动态截止日期配置"`
+}
+
+type ChartRangeAnalysisYearDateConf struct {
+	StartDay string `description:"开始日"`
+	EndDay   string `description:"结束日"`
+}
+
+// ChartRangeAnalysisPreviewReq 相关性图表请求体
+type ChartRangeAnalysisPreviewReq struct {
+	ExtraConf   ChartRangeAnalysisExtraConf
+	EdbInfoIds  []int `description:"指标Id"`
+	EdbInfoType int
+}
+
+type ChartRangeAnalysisManualDateConfList []ChartRangeAnalysisManualDateConf
+
+func (a ChartRangeAnalysisManualDateConfList) Len() int      { return len(a) }
+func (a ChartRangeAnalysisManualDateConfList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ChartRangeAnalysisManualDateConfList) Less(i, j int) bool {
+	return a[i].StartDate < a[j].StartDate
+}
+
+type ChartRangeAnalysisDataResp struct { //图表详情返回值
+	*ChartRangeAnalysisExtraConf
+	SeriesName string `description:"指标系列名称"`
+}
+
+type ChartRangeAnalysisDateDataItem struct {
+	StartDate time.Time
+	EndDate   time.Time
+	DataList  []*EdbDataList
+}
+
+// CreateRangeChartAndEdb 新增区间计算图表
+func CreateRangeChartAndEdb(chartInfo *ChartInfo, edbMappingList []*ChartEdbMapping) (chartInfoId int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+		} else {
+			_ = tx.Commit()
+		}
+	}()
+
+	// 新增图表信息
+	newId, err := tx.Insert(chartInfo)
+	if err != nil {
+		return
+	}
+	// 指标mapping
+	chartInfo.ChartInfoId = int(newId)
+	chartInfoId = int(newId)
+	if len(edbMappingList) > 0 {
+		for i := range edbMappingList {
+			edbMappingList[i].ChartInfoId = chartInfoId
+		}
+		_, err = tx.InsertMulti(len(edbMappingList), edbMappingList)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+// EditRangeChartInfoAndMapping 修改区间计算图表的 图表与指标 的关系
+func EditRangeChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr, lang string, calendar string, dateType, disabled int, barChartConf string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	var pars []interface{}
+	pars = append(pars, req.ChartName)
+	pars = append(pars, edbInfoIdStr)
+	pars = append(pars, req.ChartType)
+	pars = append(pars, req.ChartClassifyId)
+	pars = append(pars, disabled)
+	pars = append(pars, barChartConf)
+	pars = append(pars, req.ExtraConfig)
+	pars = append(pars, req.SourcesFrom)
+
+	sql := ` UPDATE  chart_info
+			SET
+			  chart_name =?,
+              edb_info_ids=?,
+			  chart_type=?,
+			  chart_classify_id = ?,
+			  modify_time = NOW(),
+              disabled = ?,
+              bar_config = ?,
+			  extra_config = ?,
+			  sources_from = ?
+			`
+	if calendar != "" {
+		sql += `,calendar = ? `
+		pars = append(pars, calendar)
+	}
+	if dateType > 0 {
+		sql += `,date_type = ? `
+		pars = append(pars, dateType)
+	}
+
+	sql += `,start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,season_start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,season_end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,left_min = ? `
+	pars = append(pars, req.LeftMin)
+
+	sql += `,left_max = ? `
+	pars = append(pars, req.LeftMax)
+
+	sql += `,right_min = ? `
+	pars = append(pars, req.RightMin)
+
+	sql += `,right_max = ? `
+	pars = append(pars, req.RightMax)
+
+	sql += `WHERE chart_info_id = ?`
+
+	pars = append(pars, req.ChartInfoId)
+	_, err = to.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+	chartEdbMappingIdList := make([]string, 0)
+	for _, v := range req.ChartEdbInfoList {
+		// 查询该指标是否存在,如果存在的话,那么就去修改,否则新增
+		var tmpChartEdbMapping *ChartEdbMapping
+		csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = ? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId, utils.CHART_SOURCE_RANGE_ANALYSIS).QueryRow(&tmpChartEdbMapping)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if tmpChartEdbMapping != nil {
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(tmpChartEdbMapping.ChartEdbMappingId))
+			//tmpChartEdbMapping.ModifyTime = time.Now()
+			//tmpChartEdbMapping.MaxData = v.MaxData
+			//tmpChartEdbMapping.MinData = v.MinData
+			//tmpChartEdbMapping.IsOrder = v.IsOrder
+			//tmpChartEdbMapping.IsAxis = v.IsAxis
+			//tmpChartEdbMapping.EdbInfoType = v.EdbInfoType
+			//tmpChartEdbMapping.LeadValue = v.LeadValue
+			//tmpChartEdbMapping.LeadUnit = v.LeadUnit
+			//tmpChartEdbMapping.ChartStyle = v.ChartStyle
+			//tmpChartEdbMapping.ChartColor = v.ChartColor
+			//tmpChartEdbMapping.PredictChartColor = v.PredictChartColor
+			//tmpChartEdbMapping.ChartWidth = v.ChartWidth
+			//_, err = to.Update(tmpChartEdbMapping, "ModifyTime", "MaxData", "MinData", "IsOrder", "IsAxis", "EdbInfoType", "LeadValue", "LeadUnit", "ChartStyle", "ChartColor", "PredictChartColor", "ChartWidth")
+			//if err != nil {
+			//	fmt.Println("chart_edb_mapping Err:" + err.Error())
+			//	return err
+			//}
+		} else {
+			mapItem := new(ChartEdbMapping)
+			mapItem.ChartInfoId = req.ChartInfoId
+			mapItem.EdbInfoId = v.EdbInfoId
+			mapItem.CreateTime = time.Now()
+			mapItem.ModifyTime = time.Now()
+			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp + "_" + strconv.Itoa(v.EdbInfoId))
+			//mapItem.MaxData = v.MaxData
+			//mapItem.MinData = v.MinData
+			mapItem.IsOrder = true
+			mapItem.IsAxis = 1
+			mapItem.EdbInfoType = 1
+			//mapItem.LeadValue = v.LeadValue
+			//mapItem.LeadUnit = v.LeadUnit
+			//mapItem.ChartStyle = v.ChartStyle
+			//mapItem.ChartColor = v.ChartColor
+			//mapItem.PredictChartColor = v.PredictChartColor
+			//mapItem.ChartWidth = v.ChartWidth
+			mapItem.Source = utils.CHART_SOURCE_RANGE_ANALYSIS
+			tmpId, err := to.Insert(mapItem)
+			if err != nil {
+				fmt.Println("AddChartEdbMapping Err:" + err.Error())
+				return err
+			}
+			mapItem.ChartEdbMappingId = int(tmpId)
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(mapItem.ChartEdbMappingId))
+		}
+	}
+	if len(chartEdbMappingIdList) > 0 {
+		chartEdbMappingIdStr := strings.Join(chartEdbMappingIdList, ",")
+		dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND chart_edb_mapping_id NOT IN(` + chartEdbMappingIdStr + `)`
+		_, err = to.Raw(dsql, req.ChartInfoId).Exec()
+		if err != nil {
+			fmt.Println("delete err:" + err.Error())
+			return err
+		}
+	}
+
+	return
+}

+ 374 - 0
models/data_manage/chart_series.go

@@ -0,0 +1,374 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ChartSeries struct {
+	ChartSeriesId int       `orm:"column(chart_series_id);pk"`
+	SeriesName    string    `description:"系列名称"`
+	SeriesNameEn  string    `description:"系列英文名称"`
+	ChartInfoId   int       `description:"图表ID"`
+	ChartStyle    string    `description:"图表类型"`
+	ChartColor    string    `description:"颜色"`
+	ChartWidth    int       `description:"线条大小"`
+	IsPoint       int       `description:"是否用数据点展示(0 否,1是)"`
+	IsNumber      int       `description:"是否用数值展示(0 否,1是)"`
+	IsAxis        int       `description:"1:左轴,0:右轴"`
+	MaxData       float64   `description:"上限"`
+	MinData       float64   `description:"下限"`
+	IsOrder       bool      `description:"true:正序,false:逆序"`
+	CreateTime    time.Time `description:"创建时间"`
+	ModifyTime    time.Time `description:"修改时间"`
+}
+
+func (c *ChartSeries) TableName() string {
+	return "chart_series"
+}
+
+func GetChartSeriesByChartInfoId(chartInfoId int) (items []*ChartSeries, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM chart_series WHERE chart_info_id = ?"
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&items)
+	return
+}
+
+// EditChartSeriesAndEdbMapping
+func EditChartSeriesAndEdbMapping(extraConfigStr string, chartInfoId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 获取已经存在的系列
+	series, err := GetChartSeriesByChartInfoId(chartInfoId)
+	if err != nil {
+		return
+	}
+	//整理成系列map
+	seriesMap := make(map[int]struct{})
+	seriesDeleteMap := make(map[int]struct{})
+	for _, v := range series {
+		seriesMap[v.ChartSeriesId] = struct{}{}
+		seriesDeleteMap[v.ChartSeriesId] = struct{}{}
+	}
+	var sectionExtraConfig ChartSectionAllExtraConf
+	err = json.Unmarshal([]byte(extraConfigStr), &sectionExtraConfig)
+	if err != nil {
+		err = fmt.Errorf("截面组合图配置异常")
+		return
+	}
+
+	// 删除所有的指标映射
+	_, err = o.Raw("DELETE FROM chart_series_edb_mapping WHERE chart_info_id = ?", chartInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	seriesList := sectionExtraConfig.SeriesList
+	for _, v := range seriesList {
+		seriesId := v.ChartSeriesId
+		tmp := &ChartSeries{
+			ChartSeriesId: v.ChartSeriesId,
+			SeriesName:    v.SeriesName,
+			SeriesNameEn:  v.SeriesNameEn,
+			ChartInfoId:   chartInfoId,
+			ChartStyle:    v.ChartStyle,
+			ChartColor:    v.ChartColor,
+			ChartWidth:    v.ChartWidth,
+			IsPoint:       v.IsPoint,
+			IsNumber:      v.IsNumber,
+			IsAxis:        v.IsAxis,
+			MaxData:       v.MaxData,
+			MinData:       v.MinData,
+			ModifyTime:    time.Now(),
+		}
+		if _, ok := seriesMap[v.ChartSeriesId]; !ok {
+			if seriesId > 0 {
+				err = fmt.Errorf("系列ID错误")
+				return
+			}
+			//新增
+			tmp.CreateTime = time.Now()
+			seriesIdTmp, e := to.Insert(tmp)
+			if e != nil {
+				err = fmt.Errorf("AddChartSeries Err:" + e.Error())
+				return
+			}
+			seriesId = int(seriesIdTmp)
+		} else {
+			//编辑
+			delete(seriesDeleteMap, v.ChartSeriesId)
+			_, e := to.Update(tmp)
+			if e != nil {
+				err = fmt.Errorf("UpdateChartSeries Err:" + e.Error())
+				return
+			}
+		}
+
+		addSeriesEdbList := make([]*ChartSeriesEdbMapping, 0)
+		for _, edbItem := range v.EdbInfoList {
+			dateConfStrByte, e := json.Marshal(edbItem.DateConf)
+			if e != nil {
+				err = e
+				return
+			}
+			dateConfStr := string(dateConfStrByte)
+			edbTmp := &ChartSeriesEdbMapping{
+				ChartSeriesId: seriesId,    //todo 系列ID
+				ChartInfoId:   chartInfoId, //todo 表图ID
+				EdbInfoId:     edbItem.EdbInfoId,
+				//EdbAliasName:            "",
+				//EdbAliasNameEn:          "",
+				DateConfName: edbItem.DateConfName,
+				DateConf:     dateConfStr,
+				DateConfType: edbItem.DateConfType,
+				CreateTime:   time.Now(),
+				ModifyTime:   time.Now(),
+			}
+			addSeriesEdbList = append(addSeriesEdbList, edbTmp)
+		}
+		if len(addSeriesEdbList) > 0 {
+			_, err = to.InsertMulti(len(addSeriesEdbList), addSeriesEdbList)
+			if err != nil {
+				err = fmt.Errorf("AddChartSeries Err:" + err.Error())
+				return
+			}
+		}
+	}
+	//删除旧的系列和ID
+	seriesIds := make([]int, 0)
+	for id, _ := range seriesDeleteMap {
+		seriesIds = append(seriesIds, id)
+	}
+	if len(seriesIds) > 0 {
+		sql := `DELETE FROM chart_series WHERE chart_series_id IN (` + utils.GetOrmInReplace(len(seriesIds)) + `) and chart_info_id=?`
+		_, err = to.Raw(sql, seriesIds, chartInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除系列失败 Err:" + err.Error())
+			return
+		}
+		sql = `DELETE FROM chart_series_edb_mapping WHERE chart_series_id IN (` + utils.GetOrmInReplace(len(seriesIds)) + `) and chart_info_id=?`
+		_, err = to.Raw(sql, seriesIds, chartInfoId).Exec()
+		if err != nil {
+			err = fmt.Errorf("删除系列指标 Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+func AddChartSeriesAndEdbMapping(extraConfigStr string, chartInfoId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	var sectionExtraConfig ChartSectionAllExtraConf
+	err = json.Unmarshal([]byte(extraConfigStr), &sectionExtraConfig)
+	if err != nil {
+		err = fmt.Errorf("截面组合图配置异常")
+		return
+	}
+	seriesList := sectionExtraConfig.SeriesList
+	for _, v := range seriesList {
+		tmp := &ChartSeries{
+			SeriesName:   v.SeriesName,
+			SeriesNameEn: v.SeriesNameEn,
+			ChartInfoId:  chartInfoId,
+			ChartStyle:   v.ChartStyle,
+			ChartColor:   v.ChartColor,
+			ChartWidth:   v.ChartWidth,
+			IsPoint:      v.IsPoint,
+			IsNumber:     v.IsNumber,
+			IsAxis:       v.IsAxis,
+			MaxData:      v.MaxData,
+			MinData:      v.MinData,
+			IsOrder:      false, //todo 是否排序
+			CreateTime:   time.Now(),
+			ModifyTime:   time.Now(),
+		}
+		seriesIdTmp, e := to.Insert(tmp)
+		if e != nil {
+			err = fmt.Errorf("AddChartSeries Err:" + e.Error())
+			return
+		}
+		seriesId := int(seriesIdTmp)
+		addSeriesEdbList := make([]*ChartSeriesEdbMapping, 0)
+		for _, edbItem := range v.EdbInfoList {
+			dateConfStrByte, e := json.Marshal(edbItem.DateConf)
+			if e != nil {
+				err = e
+				return
+			}
+			dateConfStr := string(dateConfStrByte)
+			edbTmp := &ChartSeriesEdbMapping{
+				ChartSeriesId: seriesId,    //todo 系列ID
+				ChartInfoId:   chartInfoId, //todo 表图ID
+				EdbInfoId:     edbItem.EdbInfoId,
+				//EdbAliasName:            "",
+				//EdbAliasNameEn:          "",
+				DateConfName: edbItem.DateConfName,
+				DateConfType: edbItem.DateConfType,
+				DateConf:     dateConfStr,
+				CreateTime:   time.Now(),
+				ModifyTime:   time.Now(),
+			}
+			addSeriesEdbList = append(addSeriesEdbList, edbTmp)
+		}
+		if len(addSeriesEdbList) > 0 {
+			_, e = to.InsertMulti(len(addSeriesEdbList), addSeriesEdbList)
+			if e != nil {
+				err = fmt.Errorf("AddChartSeries Err:" + e.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+func DeleteChartSeriesAndEdbMapping(chartInfoId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := ` DELETE FROM chart_series WHERE chart_info_id=? `
+	_, err = to.Raw(sql, chartInfoId).Exec()
+	if err != nil {
+		return
+	}
+	sql = ` DELETE FROM  chart_series_edb_mapping WHERE chart_info_id=? `
+	_, err = to.Raw(sql, chartInfoId).Exec()
+	return
+}
+
+func CopyChartSeriesAndEdbMapping(seriesList []*ChartSeries, seriesEdbInfoList []*ChartSeriesEdbMapping, chartInfoId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	seriesEdbMap := make(map[int][]*ChartSeriesEdbMapping)
+	for _, v := range seriesEdbInfoList {
+		seriesEdbMap[v.ChartSeriesId] = append(seriesEdbMap[v.ChartSeriesId], v)
+	}
+	for _, v := range seriesList {
+		tmp := &ChartSeries{
+			SeriesName:   v.SeriesName,
+			SeriesNameEn: v.SeriesNameEn,
+			ChartInfoId:  chartInfoId,
+			ChartStyle:   v.ChartStyle,
+			ChartColor:   v.ChartColor,
+			ChartWidth:   v.ChartWidth,
+			IsPoint:      v.IsPoint,
+			IsNumber:     v.IsNumber,
+			IsAxis:       v.IsAxis,
+			MaxData:      v.MaxData,
+			MinData:      v.MinData,
+			CreateTime:   time.Now(),
+			ModifyTime:   time.Now(),
+		}
+		seriesIdTmp, e := to.Insert(tmp)
+		if e != nil {
+			err = fmt.Errorf("AddChartSeries Err:" + e.Error())
+			return
+		}
+		seriesId := int(seriesIdTmp)
+		addSeriesEdbList := make([]*ChartSeriesEdbMapping, 0)
+		mappingList, ok := seriesEdbMap[v.ChartSeriesId]
+		if ok {
+			for _, edbItem := range mappingList {
+				edbTmp := &ChartSeriesEdbMapping{
+					ChartSeriesId: seriesId,    //todo 系列ID
+					ChartInfoId:   chartInfoId, //todo 表图ID
+					EdbInfoId:     edbItem.EdbInfoId,
+					//EdbAliasName:            "",
+					//EdbAliasNameEn:          "",
+					DateConfName: edbItem.DateConfName,
+					DateConfType: edbItem.DateConfType,
+					DateConf:     edbItem.DateConf,
+					CreateTime:   time.Now(),
+					ModifyTime:   time.Now(),
+				}
+				addSeriesEdbList = append(addSeriesEdbList, edbTmp)
+			}
+			if len(addSeriesEdbList) > 0 {
+				_, e = to.InsertMulti(len(addSeriesEdbList), addSeriesEdbList)
+				if e != nil {
+					err = fmt.Errorf("AddChartSeries Err:" + e.Error())
+					return
+				}
+			}
+		}
+	}
+	return
+}
+
+type ChartSectionSeriesValSortAsc []ChartSectionSeriesValSort
+type ChartSectionSeriesValSortDesc []ChartSectionSeriesValSort
+
+type ChartSectionSeriesValSort struct {
+	Index int
+	Value float64
+}
+
+func (s ChartSectionSeriesValSortAsc) Len() int {
+	return len(s)
+}
+
+func (s ChartSectionSeriesValSortAsc) Less(i, j int) bool {
+	return s[i].Value < s[j].Value // 升序排序,如果想要降序则改为 s[i].Value > s[j].Value
+}
+
+func (s ChartSectionSeriesValSortAsc) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+
+func (s ChartSectionSeriesValSortDesc) Len() int {
+	return len(s)
+}
+
+func (s ChartSectionSeriesValSortDesc) Less(i, j int) bool {
+	return s[i].Value > s[j].Value // 升序排序,如果想要降序则改为 s[i].Value > s[j].Value
+}
+
+func (s ChartSectionSeriesValSortDesc) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}

+ 29 - 0
models/data_manage/chart_series_edb_mapping.go

@@ -0,0 +1,29 @@
+package data_manage
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ChartSeriesEdbMapping struct {
+	ChartSeriesEdbMappingId int       `orm:"column(chart_series_edb_mapping_id);pk"`
+	ChartSeriesId           int       `description:"系列ID"`
+	ChartInfoId             int       `description:"图表ID"`
+	EdbInfoId               int       `description:"指标id"`
+	DateConfName            string    `description:"引用日期配置名称"`
+	DateConfType            int       `description:"日期类型,0指标最新日期, 1引用日期"`
+	DateConf                string    `description:"日期配置名称"`
+	ModifyTime              time.Time `description:"修改时间"`
+	CreateTime              time.Time `description:"创建时间"`
+}
+
+func (c *ChartSeriesEdbMapping) TableName() string {
+	return "chart_series_edb_mapping"
+}
+
+func GetChartSeriesEdbByChartInfoId(chartInfoId int) (items []*ChartSeriesEdbMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM chart_series_edb_mapping WHERE chart_info_id = ?"
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&items)
+	return
+}

+ 15 - 2
models/data_manage/chart_theme/chart_theme_type.go

@@ -17,6 +17,17 @@ type ChartThemeType struct {
 	CreateTime          time.Time `description:"创建时间"`
 }
 
+type ChartThemeTypeList struct {
+	ChartThemeTypeId    int    `description:"图表主题类型ID" `
+	ChartTypeName       string `description:"类型名称"`
+	ChartTypeNameEn     string `description:"类型名称"`
+	ChartType           int    `description:"图表类型"`
+	ChartSource         int    `description:"图表来源"`
+	ParentId            int    `description:"父级ID"`
+	DefaultChartThemeId int    `description:"默认使用的主题id"`
+	Child               []*ChartThemeTypeList
+}
+
 // Update
 // @Description: 更新
 // @author: Roc
@@ -36,9 +47,11 @@ func (m *ChartThemeType) Update(cols []string) (err error) {
 // @datetime 2023-12-13 17:31:03
 // @return list []*ChartThemeType
 // @return err error
-func GetAllChartThemeTypeList() (list []*ChartThemeType, err error) {
+func GetAllChartThemeTypeList() (list []*ChartThemeTypeList, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := `SELECT * FROM chart_theme_type ORDER BY chart_theme_type_id ASC `
+	sql := `SELECT c.chart_type_id AS chart_type, c.chart_type_name,c.chart_type_name_en, parent_id, t.chart_theme_type_id,t.chart_source,t.default_chart_theme_id FROM chart_type c 
+left JOIN chart_theme_type t on c.chart_type_id=t.chart_type
+ORDER BY c.sort ASC`
 	_, err = o.Raw(sql).QueryRows(&list)
 
 	return

+ 38 - 0
models/data_manage/chart_type.go

@@ -0,0 +1,38 @@
+package data_manage
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type ChartType struct {
+	ChartTypeId     int    `orm:"column(chart_type_id);pk"`
+	ChartTypeName   string `description:"图表类型名称"`
+	ChartTypeNameEn string `description:"英文图表类型名称"`
+	ParentId        int    `description:"父级ID"`
+	Sort            int    `description:"排序"`
+	CreateTime      string `description:"创建时间"`
+}
+
+type ChartTypeList struct {
+	ChartTypeId     int
+	ChartTypeName   string `description:"图表类型名称"`
+	ChartTypeNameEn string `description:"英文图表类型名称"`
+	ParentId        int    `description:"父级ID"`
+	Child           []ChartType
+}
+
+type ChartTypeListResp struct {
+	List []ChartTypeList
+}
+
+func (c *ChartType) TableName() string {
+	return "chart_type"
+}
+
+// 查询所有图表类型
+func GetChartTypeList() (items []ChartType, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM chart_type order by sort asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 78 - 5
models/data_manage/data_manage_permission/excel.go

@@ -10,11 +10,12 @@ import (
 
 type ExcelInfoPermission struct {
 	ExcelInfoPermissionId int64     `json:"excel_info_permission_id" orm:"column(excel_info_permission_id);pk"`
-	ExcelInfoId           int32     `json:"excel_info_id"` // ETA表格id
-	Source                int32     `json:"source"`        // 表格来源,1:excel插件的表格,2:自定义表格,3:混合表格,4:自定义分析,默认:1
-	SysUserId             int32     `json:"sys_user_id"`   // 系统用户id
-	ModifyTime            time.Time `json:"modify_time"`   // 变更时间
-	CreateTime            time.Time `json:"create_time"`   // 关系建立时间
+	ExcelInfoId           int32     `json:"excel_info_id"`   // ETA表格id
+	Source                int32     `json:"source"`          // 表格来源,1:excel插件的表格,2:自定义表格,3:混合表格,4:自定义分析,默认:1
+	SysUserId             int32     `json:"sys_user_id"`     // 系统用户id
+	ModifyTime            time.Time `json:"modify_time"`     // 变更时间
+	CreateTime            time.Time `json:"create_time"`     // 关系建立时间
+	PermissionType        int       `json:"permission_type"` // 权限类型: 0-默认; 1-查看; 2-编辑
 }
 
 type ExcelClassifyPermission struct {
@@ -971,3 +972,75 @@ func GetExcelInfoDataPermissionClassifyNoAuthRecordListByUserId(userId, excelSou
 
 	return
 }
+
+// ExcelInfoPermissionAdminAuth 含创建人的表格权限
+type ExcelInfoPermissionAdminAuth struct {
+	ExcelInfoPermission
+	ExcelName    string `json:"excel_name"`     // 表格名称
+	UniqueCode   string `json:"unique_code"`    // 唯一编码
+	CreateUserId int    `json:"create_user_id"` // 创建人ID
+}
+
+// GetAdminAuthExcelInfoPermission 获取用户有权限的表格
+func GetAdminAuthExcelInfoPermission(source, adminId int, keywords string) (items []*ExcelInfoPermissionAdminAuth, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT a.*, b.sys_user_id AS create_user_id, b.excel_name, b.unique_code FROM excel_info_permission AS a
+		JOIN excel_info AS b ON a.excel_info_id = b.excel_info_id
+		WHERE a.source = ? AND (b.sys_user_id = ? OR a.sys_user_id = ?)`
+	var pars []interface{}
+	pars = append(pars, source, adminId, adminId)
+	if keywords != "" {
+		sql += ` AND b.excel_name LIKE ?`
+		pars = append(pars, keywords)
+	}
+	sql += ` ORDER BY a.create_time DESC`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func ClearAndSetExcelInfoPermission(source, excelInfoId int, permissions []*ExcelInfoPermission) (err error) {
+	if excelInfoId <= 0 {
+		return
+	}
+	tx, e := orm.NewOrmUsingDB("data").Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %v", e)
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := `DELETE FROM excel_info_permission WHERE excel_info_id = ? AND source = ?`
+	_, e = tx.Raw(sql, excelInfoId, source).Exec()
+	if e != nil {
+		err = fmt.Errorf("clear permission err: %v", e)
+		return
+	}
+	if len(permissions) > 0 {
+		_, e = tx.InsertMulti(500, permissions)
+		if e != nil {
+			err = fmt.Errorf("insert permissions err: %v", e)
+			return
+		}
+	}
+	return
+}
+
+func GetExcelPermissionBySourceAndId(excelId, source int) (items []*ExcelInfoPermission, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_info_permission WHERE source = ? AND excel_info_id = ?`
+	_, err = o.Raw(sql, source, excelId).QueryRows(&items)
+	return
+}
+
+func GetExcelPermissionByExcelIdAndUserId(excelId, userId int) (items []*ExcelInfoPermission, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_info_permission WHERE excel_info_id = ? AND sys_user_id = ?`
+	_, err = o.Raw(sql, excelId, userId).QueryRows(&items)
+	return
+}

+ 15 - 5
models/data_manage/edb_data_base.go

@@ -336,13 +336,15 @@ func GetBaseIndexInfoByEdbCode(edbCode string, source int) (item *BaseIndexInfo,
 	return
 }
 
-func GetBaseIndexTableName(source int) (tableName string) {
-	edbSource := EdbSourceIdMap[source]
-	if edbSource != nil {
-		tableName = edbSource.IndexTableName
-	}
+func GetEdbDataBaseByCodeAndDate(source, subSource int, edbCode string, startDate string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	tableName := GetEdbDataTableName(source, subSource)
+	sql := ` SELECT COUNT(1) AS count FROM %s WHERE edb_code=? AND data_time=? `
+	sql = fmt.Sprintf(sql, tableName)
+	err = o.Raw(sql, edbCode, startDate).QueryRow(&count)
 	return
 }
+
 func GetEdbDataAllByEdbCodeAndSubSource(edbCode string, source, subSource, limit int) (items []*EdbInfoSearchData, err error) {
 	var pars []interface{}
 	pars = append(pars, edbCode)
@@ -385,6 +387,14 @@ func GetEdbDataTableNameAndSubSource(source, subSource int) (tableName string) {
 	return
 }
 
+func GetBaseIndexTableName(source int) (tableName string) {
+	edbSource := EdbSourceIdMap[source]
+	if edbSource != nil {
+		tableName = edbSource.IndexTableName
+	}
+	return
+}
+
 func GetEdbDataAllByEdbCodes(edbCodes []string, limit int) (items []*EdbInfoSearchData, err error) {
 	var pars []interface{}
 	pars = append(pars, edbCodes)

+ 71 - 17
models/data_manage/edb_info.go

@@ -36,6 +36,7 @@ type EdbInfo struct {
 	UniqueCode       string `description:"指标唯一编码"`
 	CreateTime       time.Time
 	ModifyTime       time.Time
+	BaseModifyTime   time.Time
 	MinValue         float64 `description:"指标最小值"`
 	MaxValue         float64 `description:"指标最大值"`
 	CalculateFormula string  `description:"计算公式"`
@@ -52,8 +53,6 @@ type EdbInfo struct {
 	Calendar         string  `description:"公历/农历" orm:"default(公历);"`
 	DataDateType     string  `orm:"column(data_date_type);size(255);null;default(交易日)"`
 	ManualSave       int     `description:"是否有手动保存过上下限: 0-否; 1-是"`
-	EmptyType        int     `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
-	MaxEmptyType     int     `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	TerminalCode     string  `description:"终端编码,用于配置在机器上"`
 	DataUpdateTime   string  `description:"最近一次数据发生变化的时间"`
 	ErDataUpdateDate string  `description:"本次更新,数据发生变化的最早日期"`
@@ -62,6 +61,8 @@ type EdbInfo struct {
 	SubSourceName    string  `description:"子数据来源名称"`
 	IndicatorCode    string  `description:"指标代码"`
 	StockCode        string  `description:"证券代码"`
+	EmptyType        int     `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int     `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
 	Extra            string  `description:"指标额外配置"`
 	IsJoinPermission int     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 }
@@ -1058,6 +1059,33 @@ func ReplaceChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (relationEdbInfoIdList []i
 						// 更新图表配置信息
 						chartInfo.ExtraConfig = sectionScatterConfigStr
 						updateStr = append(updateStr, "ExtraConfig")
+					} else if chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+						//解析配置内容
+						var tmpExtraConfig ChartSectionExtraConf
+						err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &tmpExtraConfig)
+						if err != nil {
+							errmsg = "获取截面散点图配置信息失败 json.Unmarshal:Err:" + err.Error()
+							return
+						}
+						//找到指标信息,并替换
+						if len(tmpExtraConfig.DateConfList) > 0 {
+							for k1, item := range tmpExtraConfig.DateConfList {
+								if item.EdbInfoId > 0 && item.EdbInfoId == oldEdbInfo.EdbInfoId {
+									tmpExtraConfig.DateConfList[k1].EdbInfoId = newEdbInfo.EdbInfoId
+								}
+							}
+						}
+						// 重新序列化
+						configJson, e := json.Marshal(tmpExtraConfig)
+						if e != nil {
+							err = e
+							errmsg = "图表配置信息序列化失败:json.Marshal Err: " + e.Error()
+							return
+						}
+						configStr := string(configJson)
+						// 更新图表配置信息
+						chartInfo.ExtraConfig = configStr
+						updateStr = append(updateStr, "ExtraConfig")
 					}
 				}
 				if len(updateStr) > 0 {
@@ -1107,6 +1135,13 @@ func ReplaceChartEdb(oldEdbInfo, newEdbInfo *EdbInfo) (relationEdbInfoIdList []i
 					return
 				}
 
+				rsql = ` UPDATE chart_series_edb_mapping SET edb_info_id=? WHERE edb_info_id=? and chart_info_id=?`
+				_, err = to.Raw(rsql, newEdbInfo.EdbInfoId, oldEdbInfo.EdbInfoType, mv.ChartInfoId).Exec()
+				if err != nil {
+					errmsg = "更新图库系列指标信息失败:Err:" + err.Error()
+					return
+				}
+
 				chartInfoIdList = append(chartInfoIdList, fmt.Sprint(mv.ChartInfoId))
 			}
 			logMsg += `涉及到的图表id:` + strings.Join(chartInfoIdList, ",") + ";"
@@ -1204,11 +1239,11 @@ type EdbInfoView struct {
 	IsUpdate         int     `description:"当天是否已更新,1:未更新,2:已更新"`
 	LatestDate       string  `description:"数据最新日期(实际日期)"`
 	LatestValue      float64 `description:"数据最新值(实际值)"`
+	EndValue         float64 `description:"数据的最新值(预测日期的最新值)"`
 	SubSource        int     `description:"子数据来源:0:经济数据库,1:日期序列"`
 	SubSourceName    string  `description:"子数据来源名称"`
 	IndicatorCode    string  `description:"指标代码"`
 	StockCode        string  `description:"证券代码"`
-	EndValue         float64 `description:"数据的最新值(预测日期的最新值)"`
 }
 
 // 获取指标的所有计算指标,以及计算指标所依赖计算指标
@@ -1797,6 +1832,25 @@ func GetEdbInfoByCodeArr(codes []string, edbInfoType int) (items []*EdbInfo, err
 	return
 }
 
+type EdbInfoEditRecord struct {
+	EdbInfoId           int    `description:"指标ID"`
+	EdbName             string `description:"指标名称"`
+	Frequency           string `description:"频率"`
+	Unit                string `description:"单位"`
+	ClassifyId          int    `description:"分类id"`
+	CalculateFormula    string `description:"计算公式"`
+	OperateUserId       int    `description:"操作人id"`
+	OperateUserRealName string `description:"操作人姓名"`
+}
+
+func ModifyEdbInfoBaseTimeById(edbInfoId int, cTime time.Time) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	// 更新修改时间
+	sql := ` UPDATE edb_info SET base_modify_time = ? WHERE edb_info_id = ? `
+	_, err = o.Raw(sql, cTime, edbInfoId).Exec()
+	return
+}
+
 // GetEdbCodesBySource 根据来源获取指标编码
 func GetEdbCodesBySource(source int) (items []*EdbInfo, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -1907,6 +1961,11 @@ func getAllDataByMongo(edbInfoId, source, subSource int, startDataTime string) (
 	return
 }
 
+type ReplaceEdbInfoItem struct {
+	OldEdbInfo *EdbInfo
+	NewEdbInfo *EdbInfo
+}
+
 // GetEdbClassifyIdListBySource 获取普通指标的分类列表
 func GetEdbClassifyIdListBySource(source int) (items []int, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -1926,9 +1985,15 @@ func GetEdbInfoByClassifyIdAndSource(classifyId, edbInfoType, source int) (items
 	return
 }
 
-type ReplaceEdbInfoItem struct {
-	OldEdbInfo *EdbInfo
-	NewEdbInfo *EdbInfo
+func GetEdbInfoListByCond(condition string, pars []interface{}) (list []*EdbInfoList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_info WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY create_time DESC `
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
 }
 
 func GetEdbInfoFieldList(cond string, pars []interface{}, fields []string) (items []*EdbInfo, err error) {
@@ -1947,17 +2012,6 @@ type EdbInfoExtra struct {
 	ApiExtraPars string `description:"API-额外参数(如同花顺日期序列)"`
 }
 
-func GetEdbInfoListByCond(condition string, pars []interface{}) (list []*EdbInfoList, err error) {
-	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT * FROM edb_info WHERE 1=1 `
-	if condition != "" {
-		sql += condition
-	}
-	sql += ` ORDER BY create_time DESC `
-	_, err = o.Raw(sql, pars).QueryRows(&list)
-	return
-}
-
 func GetThsHfEdbInfoMaxAndMinInfoByMongo(source, subSource int, edbCode string) (item *EdbInfoMaxAndMinInfo, err error) {
 	mogDataObj := new(mgo.EdbDataThsHf)
 	pipeline := []bson.M{

+ 5 - 0
models/data_manage/edb_info_calculate.go

@@ -208,6 +208,7 @@ type EdbInfoCalculateBatchSaveReq struct {
 	MoveFrequency    string           `description:"移动频度:天/周/月/季/年"`
 	Extra            string           `description:"指标的额外配置"`
 	Calendar         string           `description:"公历/农历"`
+	EmptyType        int              `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 }
 
 type EdbInfoCalculateBatchEditReq struct {
@@ -224,6 +225,7 @@ type EdbInfoCalculateBatchEditReq struct {
 	Calendar      string `description:"公历/农历"`
 	Extra         string `description:"指标的额外配置"`
 	EdbInfoIdArr  []EdbInfoFromTag
+	EmptyType     int `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 }
 
 // EdbInfoCalculateBatchSaveReqByEdbLib edb指标库的请求逻辑
@@ -255,6 +257,8 @@ type EdbInfoCalculateBatchEditReqByEdbLib struct {
 	Frequency     string `description:"频度"`
 	Unit          string `description:"单位"`
 	ClassifyId    int    `description:"分类id"`
+	AdminId       int    `description:"操作人id"`
+	AdminName     string `description:"操作人姓名"`
 	Formula       string `description:"N值"`
 	EdbInfoId     int    `description:"编辑指标id"`
 	FromEdbInfoId int    `description:"计算来源指标id"`
@@ -265,6 +269,7 @@ type EdbInfoCalculateBatchEditReqByEdbLib struct {
 	EdbInfoIdArr  []EdbInfoFromTag
 	Data          interface{} `description:"数据列"`
 	Extra         string      `description:"指标的额外配置"`
+	EmptyType     int         `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 }
 
 func GetEdbInfoCalculateMap(edbInfoId, source int) (list []*EdbInfo, err error) {

+ 28 - 0
models/data_manage/edb_info_record.go

@@ -0,0 +1,28 @@
+package data_manage
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type EdbInfoRecord struct {
+	EdbInfoRecordId     int       `orm:"column(edb_info_record_id);pk"`
+	EdbInfoId           int       `orm:"column(edb_info_id)"`
+	OldEdbName          string    `description:"旧的指标名称"`
+	OldFrequency        string    `description:"旧的频率"`
+	OldUnit             string    `description:"旧的单位"`
+	NewEdbName          string    `description:"新的指标名称"`
+	NewFrequency        string    `description:"新的频率"`
+	NewUnit             string    `description:"新的单位"`
+	OperateUserId       int       `description:"执行人id"`
+	OperateUserRealName string    `description:"执行人名称"`
+	CreateTime          time.Time `description:"记录的生成时间"`
+	Timestamp           int64     `description:"时间戳"`
+}
+
+func AddEditEdbInfoRcord(edbRecord *EdbInfoRecord) (e error) {
+	o := orm.NewOrmUsingDB("data")
+	_, e = o.Insert(edbRecord)
+	return
+}

+ 21 - 0
models/data_manage/excel/excel_classify.go

@@ -7,6 +7,11 @@ import (
 	"time"
 )
 
+const (
+	CustomAnalysisMenuShareId  = 9000000 // 自定义分析目录ID-我共享的
+	CustomAnalysisMenuSharedId = 8000000 // 自定义分析目录ID-收到共享
+)
+
 // ExcelClassify excel表格分类
 type ExcelClassify struct {
 	ExcelClassifyId   int       `orm:"column(excel_classify_id);pk"`
@@ -132,6 +137,8 @@ type ExcelClassifyItems struct {
 	Children          []*ExcelClassifyItems
 	IsJoinPermission  int  `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth     bool `description:"是否有数据权限"`
+	HasShare          bool `description:"是否已共享: true-是; false-否"`
+	ShowShareBtn      bool `description:"是否显示共享按钮: true-是; false-否"`
 }
 
 func GetExcelClassifyByCondition(condition string, pars []interface{}) (item *ExcelClassify, err error) {
@@ -226,3 +233,17 @@ func GetClassifyByIdList(classifyIdList []int) (items []*ExcelClassify, err erro
 
 	return
 }
+
+func GetAdminExcelClassifyBySource(source, adminId int) (items []*ExcelClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_classify WHERE source = ? AND sys_user_id = ? AND is_delete = 0 ORDER BY sort ASC, excel_classify_id ASC;`
+	_, err = o.Raw(sql, source, adminId).QueryRows(&items)
+	return
+}
+
+func GetExcelClassifyModelBySource(source int) (items []*ExcelClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM excel_classify WHERE source = ? AND is_delete = 0 ORDER BY sort ASC, excel_classify_id ASC`
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}

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

@@ -415,7 +415,6 @@ FROM excel_info WHERE 1=1 AND is_delete=0 `
 	_, err = o.Raw(sql, pars).QueryRows(&item)
 	return
 }
-
 func GetExcelListCountByCondition(condition string, pars []interface{}) (count int, err error) {
 	o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT COUNT(1) AS count FROM excel_info WHERE 1=1 AND is_delete=0 `

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

@@ -137,6 +137,16 @@ type CopyExcelInfoReq struct {
 	ExcelClassifyId int    `description:"分类id"`
 }
 
+// AddAndEditSandbox 添加/编辑沙盘的请求数据
+type AddAndEditSandbox struct {
+	ExcelInfoId       int    `description:"excel表格ID"`
+	Name              string `description:"沙盘名称"`
+	ChartPermissionId int    `description:"品种权限id"`
+	Content           string `description:"沙盘内容"`
+	PicUrl            string `description:"沙盘图片地址"`
+	SvgData           string `description:"沙盘svg图片数据"`
+}
+
 // MarkEditExcel 标记编辑表格的请求数据
 type MarkEditExcel struct {
 	ExcelInfoId int `description:"表格id"`
@@ -153,3 +163,10 @@ type SaveExcelInfoWorkerReq struct {
 	ExcelInfoId int    `description:"ETA表格ID"`
 	SysUserIds  string `description:"协作人ID 用英文逗号拼接"`
 }
+
+// ShareExcelInfoReq 分享表格请求
+type ShareExcelInfoReq struct {
+	ExcelInfoId int   `description:"表格ID"`
+	ViewUserIds []int `description:"查看权限用户IDs"`
+	EditUserIds []int `description:"编辑权限用户IDs"`
+}

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

@@ -111,3 +111,10 @@ type BalanceTableVersionListItem struct {
 type BalanceTableVersionListResp struct {
 	List []*BalanceTableVersionListItem
 }
+
+// ShareExcelInfoDetail 分享表格详情
+type ShareExcelInfoDetail struct {
+	ExcelInfoId int   `description:"表格ID"`
+	ViewUserIds []int `description:"查看权限用户IDs"`
+	EditUserIds []int `description:"编辑权限用户IDs"`
+}

+ 14 - 0
models/data_manage/excel_style.go

@@ -213,3 +213,17 @@ type Ct struct {
 	Fa string `json:"fa"`
 	T  string `json:"t"`
 }
+
+// ManualEdbExcelStyleEditReq
+// @Description: 手工数据录入编辑(Excel样式)
+type ManualEdbExcelStyleEditReq struct {
+	EdbName string
+	Data    []struct {
+		Date  string
+		Value float64
+	}
+	TradeCode  string `description:"指标编码"`
+	Unit       string `description:"单位"`
+	Frequency  string `description:"频度"`
+	ClassifyId int    `description:"分类id"`
+}

+ 62 - 0
models/data_manage/factor_edb_series.go

@@ -366,3 +366,65 @@ type CalculateCorrelationMatrixPars struct {
 	SeriesIds     []int             `description:"系列IDs"`
 	Correlation   CorrelationConfig `description:"相关性配置"`
 }
+
+// RemoveSeriesAndMappingByFactorEdbSeriesId 删除系列和指标关联
+func (m *FactorEdbSeries) RemoveSeriesAndMappingByFactorEdbSeriesId(factorEdbSeriesChartMapping *FactorEdbSeriesChartMapping) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("orm begin err: %v", e)
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+	factorEdbSeriesId := factorEdbSeriesChartMapping.FactorEdbSeriesId
+	err = factorEdbSeriesChartMapping.Remove()
+	if err != nil {
+		err = fmt.Errorf("factorEdbSeriesChartMapping.delete err: %v", err)
+		return
+	}
+	if factorEdbSeriesId == 0 {
+		return
+	}
+
+	// 清除原指标关联
+	seriesOb := new(FactorEdbSeries)
+	cond := fmt.Sprintf("%s = ?", seriesOb.Cols().PrimaryId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, factorEdbSeriesId)
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, seriesOb.TableName(), cond)
+	_, e = tx.Raw(sql, pars).Exec()
+	if e != nil {
+		err = fmt.Errorf("remove FactorEdbSeries err: %v", e)
+		return
+	}
+
+	// 清除原指标关联
+	mappingOb := new(FactorEdbSeriesMapping)
+	cond1 := fmt.Sprintf("%s = ?", mappingOb.Cols().FactorEdbSeriesId)
+	pars1 := make([]interface{}, 0)
+	pars1 = append(pars1, factorEdbSeriesId)
+	sql = fmt.Sprintf(`DELETE FROM %s WHERE %s`, mappingOb.TableName(), cond1)
+	_, e = tx.Raw(sql, pars1).Exec()
+	if e != nil {
+		err = fmt.Errorf("remove mapping err: %v", e)
+		return
+	}
+	dataOb := new(FactorEdbSeriesCalculateDataQjjs)
+	//删除原指标数据
+	cond2 := fmt.Sprintf("%s = ?", dataOb.Cols().FactorEdbSeriesId)
+	pars2 := make([]interface{}, 0)
+	pars2 = append(pars2, factorEdbSeriesId)
+	sql = fmt.Sprintf(`DELETE FROM %s WHERE %s`, dataOb.TableName(), cond2)
+	_, e = tx.Raw(sql, pars2).Exec()
+	if e != nil {
+		err = fmt.Errorf("remove mapping err: %v", e)
+		return
+	}
+	return
+}

+ 210 - 0
models/data_manage/factor_edb_series_calculate_data_qjjs.go

@@ -0,0 +1,210 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// FactorEdbSeriesCalculateDataQjjs 因子指标系列-区间计算数据表
+type FactorEdbSeriesCalculateDataQjjs struct {
+	FactorEdbSeriesCalculateDataId int       `orm:"column(factor_edb_series_calculate_data_id);pk"`
+	FactorEdbSeriesId              int       `description:"因子指标系列ID"`
+	EdbInfoId                      int       `description:"指标ID"`
+	EdbCode                        string    `description:"指标编码"`
+	DataTime                       time.Time `description:"数据日期"`
+	Value                          float64   `description:"数据值"`
+	CreateTime                     time.Time `description:"创建时间"`
+	ModifyTime                     time.Time `description:"修改时间"`
+	DataTimestamp                  int64     `description:"数据日期时间戳"`
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) TableName() string {
+	return "factor_edb_series_calculate_data_qjjs"
+}
+
+type FactorEdbSeriesCalculateDataQjjsCols struct {
+	PrimaryId         string
+	FactorEdbSeriesId string
+	EdbInfoId         string
+	EdbCode           string
+	DataTime          string
+	Value             string
+	CreateTime        string
+	ModifyTime        string
+	DataTimestamp     string
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Cols() FactorEdbSeriesCalculateDataCols {
+	return FactorEdbSeriesCalculateDataCols{
+		PrimaryId:         "factor_edb_series_calculate_data_id",
+		FactorEdbSeriesId: "factor_edb_series_id",
+		EdbInfoId:         "edb_info_id",
+		EdbCode:           "edb_code",
+		DataTime:          "data_time",
+		Value:             "value",
+		CreateTime:        "create_time",
+		ModifyTime:        "modify_time",
+		DataTimestamp:     "data_timestamp",
+	}
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.FactorEdbSeriesCalculateDataId = int(id)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) CreateMulti(items []*FactorEdbSeriesCalculateDataQjjs) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(500, items)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) 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.FactorEdbSeriesCalculateDataId).Exec()
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) 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 *FactorEdbSeriesCalculateDataQjjs) 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 *FactorEdbSeriesCalculateDataQjjs) GetItemById(id int) (item *FactorEdbSeriesCalculateDataQjjs, 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 *FactorEdbSeriesCalculateDataQjjs) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *FactorEdbSeriesCalculateDataQjjs, 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 *FactorEdbSeriesCalculateDataQjjs) 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 *FactorEdbSeriesCalculateDataQjjs) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*FactorEdbSeriesCalculateDataQjjs, 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 *FactorEdbSeriesCalculateDataQjjs) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*FactorEdbSeriesCalculateDataQjjs, 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
+}
+
+// FactorEdbSeriesCalculateDataQjjsItem 因子指标系列-计算数据信息
+type FactorEdbSeriesCalculateDataQjjsItem struct {
+	DataId            int     `description:"数据ID"`
+	FactorEdbSeriesId int     `description:"因子指标系列ID"`
+	EdbInfoId         int     `description:"指标ID"`
+	EdbCode           string  `description:"指标编码"`
+	DataTime          string  `description:"数据日期"`
+	Value             float64 `description:"数据值"`
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) Format2Item() (item *FactorEdbSeriesCalculateDataQjjsItem) {
+	item = new(FactorEdbSeriesCalculateDataQjjsItem)
+	item.DataId = m.FactorEdbSeriesCalculateDataId
+	item.FactorEdbSeriesId = m.FactorEdbSeriesId
+	item.EdbInfoId = m.EdbInfoId
+	item.EdbCode = m.EdbCode
+	return
+}
+
+// TransEdbSeriesCalculateDataQjjs2EdbDataList 转换数据格式
+func TransEdbSeriesCalculateDataQjjs2EdbDataList(items []*FactorEdbSeriesCalculateDataQjjs) (list []*EdbDataList) {
+	list = make([]*EdbDataList, 0)
+	for _, v := range items {
+		list = append(list, &EdbDataList{
+			DataTime: v.DataTime.Format(utils.FormatDate),
+			Value:    v.Value,
+		})
+	}
+	return
+}
+
+func (m *FactorEdbSeriesCalculateDataQjjs) GetEdbDataList(seriesId int, edbInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
+	var pars []interface{}
+	sql := `SELECT factor_edb_series_calculate_data_id as edb_data_id  ,edb_info_id,data_time,value,data_timestamp FROM %s WHERE edb_info_id=? and factor_edb_series_id=? `
+	pars = append(pars, edbInfoId, seriesId)
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+	if endDate != "" {
+		sql += ` AND data_time<=? `
+		pars = append(pars, endDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	sql = fmt.Sprintf(sql, m.TableName())
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	return
+}

+ 8 - 0
models/data_manage/factor_edb_series_chart_mapping.go

@@ -10,6 +10,7 @@ import (
 
 const (
 	FactorEdbSeriesChartCalculateTypeCorrelation = 1 // 相关性计算
+	FactorEdbSeriesChartCalculateTypeRange       = 2 // 	区间计算
 )
 
 // FactorEdbSeriesChartMapping 因子指标系列-图表关联
@@ -120,6 +121,13 @@ func (m *FactorEdbSeriesChartMapping) GetItemById(id int) (item *FactorEdbSeries
 	return
 }
 
+func (m *FactorEdbSeriesChartMapping) GetItemByChartInfoId(id int) (item *FactorEdbSeriesChartMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().ChartInfoId)
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
 func (m *FactorEdbSeriesChartMapping) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *FactorEdbSeriesChartMapping, err error) {
 	o := orm.NewOrmUsingDB("data")
 	order := ``

+ 7 - 0
models/data_manage/factor_edb_series_mapping.go

@@ -165,3 +165,10 @@ func (m *FactorEdbSeriesMapping) Format2Item() (item *FactorEdbSeriesMappingItem
 	item.EdbCode = m.EdbCode
 	return
 }
+
+func (m *FactorEdbSeriesMapping) GetItemBySeriesId(seriesId int) (items []*FactorEdbSeriesMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().FactorEdbSeriesId)
+	_, err = o.Raw(sql, seriesId).QueryRows(&items)
+	return
+}

+ 8 - 8
models/data_manage/my_chart.go

@@ -914,14 +914,6 @@ func GetChartInfoViewByIdList(chartInfoIdList []int) (items []*ChartInfoView, er
 	return
 }
 
-// GetMyChartClassifyByClassifyId 主键获取分类
-func GetMyChartClassifyByClassifyId(classifyId int) (item *MyChartClassify, err error) {
-	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT * FROM my_chart_classify WHERE my_chart_classify_id = ? `
-	err = o.Raw(sql, classifyId).QueryRow(&item)
-	return
-}
-
 // MyChartClassifyItem 我的图表分类信息
 type MyChartClassifyItem struct {
 	MyChartClassifyId   int    `description:"分类ID"`
@@ -971,3 +963,11 @@ func GetMyChartClassifyIdAndNum(cond string, pars []interface{}) (items []*MyCha
 	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
 }
+
+// GetMyChartClassifyByClassifyId 主键获取分类
+func GetMyChartClassifyByClassifyId(classifyId int) (item *MyChartClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM my_chart_classify WHERE my_chart_classify_id = ? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}

+ 1 - 0
models/data_manage/predict_edb_info_calculate.go

@@ -37,6 +37,7 @@ type PredictEdbInfoCalculateBatchSaveReq struct {
 	MoveType      int    `description:"移动方式:1:领先(默认),2:滞后"`
 	MoveFrequency string `description:"移动频度:天/周/月/季/年"`
 	Calendar      string `description:"公历/农历"`
+	EmptyType     int    `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
 }
 
 // BatchPredictEdbInfoCalculateBatchSaveReq 批量添加 计算指标

+ 1 - 1
models/data_manage/trade_analysis/trade_analysis.go

@@ -152,7 +152,7 @@ type GetPositionTopListItem struct {
 
 func GetTradePositionTop(exchange string, classifyName, classifyType, dataTime string) (list []TradePositionTop, err error) {
 	tableName := "trade_position_" + exchange + "_top"
-	sql := `SELECT * FROM ` + tableName + " WHERE classify_name=? and classify_type=? and data_time=? and `rank` <=20 and `rank` > 0 ORDER BY deal_value desc"
+	sql := "SELECT * FROM " + tableName + " WHERE classify_name=? and classify_type=? and data_time=? and `rank` <=20 and `rank` > 0 ORDER BY deal_value desc"
 
 	o := orm.NewOrmUsingDB("data")
 	_, err = o.Raw(sql, classifyName, classifyType, dataTime).QueryRows(&list)

+ 15 - 9
models/db.go

@@ -181,15 +181,15 @@ func init() {
 	// 初始化跨品种分析表
 	initCrossVariety()
 
+	// 初始化图表主题
+	initChartTheme()
+
 	//初始化AI
 	initAi()
 
 	// 报告审批
 	initReportApprove()
 
-	// 初始化图表主题
-	initChartTheme()
-
 	// 初始化指标刷新
 	initEdbRefresh()
 
@@ -300,6 +300,7 @@ func initShEdbData() {
 		new(Edbinfo),                         //edb库的edbinfo表
 		new(data_manage.ExcelStyle),          //在线excel样式表
 		new(data_manage.ExcelEdbdataMapping), //excel样式和指标映射表
+		new(EdbinfoOpRecord),                 // 手工数据的操作日志
 	)
 }
 
@@ -321,6 +322,7 @@ func initEdbData() {
 		new(data_manage.BaseFromSmmData),
 		new(data_manage.BaseFromSmmClassify),
 		new(data_manage.EdbInfoLog),
+		new(data_manage.EdbInfoRecord),
 		new(data_manage.EdbInfoCalculateMapping),
 		new(data_manage.PredictEdbConf),                  //预测指标配置
 		new(data_manage.BaseFromMysteelChemicalClassify), //预测指标配置
@@ -346,11 +348,11 @@ func initEdbData() {
 		new(data_manage.EdbDataInsertConfig),      // 指标数据插入配置表
 		new(data_manage.EdbInfoNoPermissionAdmin), //指标不可见用户配置表
 		new(data_manage.EdbTerminal),              //指标终端
+		new(data_manage.EdbInfoRelation),          //指标关系表
 		new(data_manage.BaseFromThsHfIndex),
 		new(data_manage.BaseFromThsHfData),
 		new(data_manage.BaseFromThsHfClassify),
 		new(data_manage.BaseFromEdbMapping),
-		new(data_manage.EdbInfoRelation), //指标关系表
 	)
 }
 
@@ -366,6 +368,8 @@ func initChart() {
 		new(data_manage.MyChartClassifyMapping),
 		new(data_manage.ChartInfoLog),
 		new(data_manage.ChartInfoCorrelation),
+		new(data_manage.ChartSeries),
+		new(data_manage.ChartSeriesEdbMapping),
 	)
 }
 
@@ -469,7 +473,8 @@ func initBusinessConf() {
 
 func initOutLink() {
 	orm.RegisterModel(
-		new(OutLink)) // 外部链接表
+		new(OutLink), // 外部链接表
+	)
 }
 
 // initEtaTrial 试用平台相关表
@@ -621,10 +626,11 @@ func initFeCalendar() {
 // initFactorEdbSeries 因子指标系列数据表
 func initFactorEdbSeries() {
 	orm.RegisterModel(
-		new(data_manage.FactorEdbSeries),              // 因子指标系列
-		new(data_manage.FactorEdbSeriesChartMapping),  // 因子指标系列-图表关联
-		new(data_manage.FactorEdbSeriesMapping),       // 因子指标系列-指标计算数据
-		new(data_manage.FactorEdbSeriesCalculateData), // 因子指标系列-指标关联
+		new(data_manage.FactorEdbSeries),                  // 因子指标系列
+		new(data_manage.FactorEdbSeriesChartMapping),      // 因子指标系列-图表关联
+		new(data_manage.FactorEdbSeriesMapping),           // 因子指标系列-指标计算数据
+		new(data_manage.FactorEdbSeriesCalculateData),     // 因子指标系列-指标关联
+		new(data_manage.FactorEdbSeriesCalculateDataQjjs), // 因子指标系列-区间计算数据
 	)
 }
 

+ 304 - 0
models/manual_edb.go

@@ -0,0 +1,304 @@
+package models
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+// TargetDetailResp 指标数据结构体
+type TargetDetailResp struct {
+	Detail       *EdbInfoItem
+	ClassifyList []*EdbdataClassify
+}
+
+// GetTargetByTradeCode
+// @Description: 根据手工指标编码获取指标信息
+// @author: Roc
+// @datetime 2024-07-16 14:18:10
+// @param tradeCode string
+// @return item *Edbinfo
+// @return err error
+func GetTargetByTradeCode(tradeCode string) (item *EdbInfoItem, err error) {
+	sql := `SELECT * FROM edbinfo WHERE TRADE_CODE = ?  `
+	o := orm.NewOrmUsingDB("edb")
+	err = o.Raw(sql, tradeCode).QueryRow(&item)
+	return
+}
+
+// GetCountManualUserClassify
+// @Description: 根据用户ID和分类ID获取关系数量
+// @author: Roc
+// @datetime 2024-07-16 14:27:58
+// @param sysUserId int
+// @param classifyId int
+// @return total int
+// @return err error
+func GetCountManualUserClassify(sysUserId, classifyId int) (total int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT count(1) ct FROM manual_user_classify WHERE admin_id=? AND classify_id = ? `
+	err = o.Raw(sql, sysUserId, classifyId).QueryRow(&total)
+
+	return
+}
+func GetManualClassifyByClassifyId(classifyId int) (item *EdbdataClassify, err error) {
+	o := orm.NewOrmUsingDB("edb")
+	sql := ` SELECT classify_id,classify_name,parent_id FROM edbdata_classify WHERE classify_id = ?  `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+
+	return
+}
+
+// GetEdbDataListByCode
+// @Description: 通过指标ID获取所有数据
+// @author: Roc
+// @datetime 2024-07-16 15:32:41
+// @param tradeCode string
+// @return items []*Edbdata
+// @return err error
+func GetEdbDataListByCode(tradeCode string) (items []*Edbdata, err error) {
+	sql := ` SELECT  TRADE_CODE,DT,round(CLOSE,4) CLOSE,modify_time  FROM edbdata WHERE TRADE_CODE = ?  GROUP BY TRADE_CODE,DT ORDER BY DT DESC `
+	o := orm.NewOrmUsingDB("edb")
+	_, err = o.Raw(sql, tradeCode).QueryRows(&items)
+	return
+}
+
+// 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:"待更新日期"`
+	Status       string `description:"状态:未完成/完成"`
+	UniqueCode   string
+	IsJoinEdb    int8   `description:"指标库是否已添加:0-否;1-是"`
+	UserName     string `description:"录入用户名称"`
+}
+
+// EdbListResp 指标数据结构体
+type EdbListResp struct {
+	List   []*EdbInfoListItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// GetEdbInfoList
+// @Description: 根据条件获取指标列表
+// @author: Roc
+// @datetime 2024-07-17 10:34:05
+// @param condition string
+// @param pars []interface{}
+// @return items []*Edbinfo
+// @return err error
+func GetEdbInfoList(condition string, pars []interface{}, startSize, pageSize int) (items []*EdbInfoListItem, err error) {
+	o := orm.NewOrmUsingDB("edb")
+	sql := `SELECT DISTINCT a.* FROM edbinfo AS a  WHERE a.REMARK='手动' `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY a.create_date DESC `
+
+	if pageSize > 0 {
+		sql += ` LIMIT ?,? `
+		pars = append(pars, startSize, pageSize)
+	}
+
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+
+	return
+}
+
+// GetCountEdbInfoList
+// @Description: 根据条件获取指标数量
+// @author: Roc
+// @datetime 2024-07-17 14:51:00
+// @param condition string
+// @param pars []interface{}
+// @return total int
+// @return err error
+func GetCountEdbInfoList(condition string, pars []interface{}) (total int, err error) {
+	o := orm.NewOrmUsingDB("edb")
+	sql := `SELECT COUNT(1) ct FROM edbinfo AS a  WHERE a.REMARK='手动' `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY a.create_date DESC `
+
+	err = o.Raw(sql, pars).QueryRow(&total)
+
+	return
+}
+
+// OnlyMultiAddEdbdata
+// @Description: 批量添加指标数据
+// @author: Roc
+// @datetime 2024-07-18 16:48:55
+// @param items []*Edbdata
+// @return err error
+func OnlyMultiAddEdbdata(items []*Edbdata) (err error) {
+	o := orm.NewOrmUsingDB("edb")
+	_, err = o.InsertMulti(utils.MultiAddNum, items)
+	return
+}
+
+// DelEdbdataByCodeAndDateList
+// @Description: 根据指标编码和日期列表批量删除指标的明细数据
+// @author: Roc
+// @datetime 2024-07-18 16:54:27
+// @param tradeCode string
+// @param dateList []string
+// @return err error
+func DelEdbdataByCodeAndDateList(tradeCode string, dateList []string) (err error) {
+	num := len(dateList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrmUsingDB("edb")
+	sql := `delete from  edbdata AS a  WHERE a.TRADE_CODE=? and DT in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, tradeCode, dateList).Exec()
+
+	return
+}
+
+// UpdateManualIsJoinEdbStatus
+// @Description: 根据手工数据加入指标的状态
+// @author: Roc
+// @datetime 2024-07-22 11:29:13
+// @param edbCode string
+// @param isJoinEdb int 是否加入指标库
+// @return err error
+func UpdateManualIsJoinEdbStatus(edbCode string, isJoinEdb int8) (err error) {
+	o := orm.NewOrmUsingDB("edb")
+	sql := ` UPDATE edbinfo SET is_join_edb = ? WHERE TRADE_CODE =? `
+	_, err = o.Raw(sql, isJoinEdb, edbCode).Exec()
+	return
+}
+
+// BatchManualEdbReq 指标数据结构体
+type BatchManualEdbReq struct {
+	//IsJoinEdb      int      `form:"IsJoinEdb" description:"是否加到指标库,0:未加到指标库"`
+	FrequencyList  []string `description:"频度;枚举值:日度、周度、月度、季度、半年度、年度"`
+	Keyword        string   `description:"关键字"`
+	ClassifyIdList []int    `description:"所选品种id列表"`
+	UserIdList     []int    `description:"所选用户id列表"`
+	ListAll        bool     `form:"ListAll" json:"ListAll" description:"列表全选"`
+	TradeCodeList  []string `form:"TradeCodeList" json:"TradeCodeList" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
+	PageSize       int      `form:"PageSize" json:"PageSize"`
+	CurrentIndex   int      `form:"CurrentIndex" form:"CurrentIndex"`
+}
+
+// DelManualIndexByCodeList
+// @Description: 根据指标编码列表删除指标
+// @author: Roc
+// @datetime 2024-07-30 14:10:12
+// @param codeList []string
+// @return err error
+func DelManualIndexByCodeList(codeList []string) (err error) {
+	num := len(codeList)
+	if num <= 0 {
+		return
+	}
+	to, err := orm.NewOrmUsingDB("edb").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	// 删除指标
+	sql := `DELETE FROM edbinfo WHERE TRADE_CODE  in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = to.Raw(sql, codeList).Exec()
+	if err != nil {
+
+	}
+
+	// 删除指标数据
+	sql = `DELETE FROM edbdata WHERE TRADE_CODE  in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = to.Raw(sql, codeList).Exec()
+	if err != nil {
+
+	}
+
+	return
+}
+
+// EdbinfoOpRecord
+// @Description: 手工数据的操作日志
+type EdbinfoOpRecord struct {
+	TradeCode  string    `orm:"column(TRADE_CODE);pk" description:"指标编码"`
+	Remark     string    `orm:"column(remark)" description:"操作信息"`
+	UserId     int       `orm:"column(user_id)" description:"用户id"`
+	UserName   string    `orm:"column(user_name)" description:"用户姓名"`
+	CreateTime time.Time `orm:"column(create_time)" description:"创建时间"`
+}
+
+// Remark备注:
+// 1、创建指标
+// 2、编辑指标
+// 3、更新数据
+// 4、数据资产由<xxx>转移给<xxx>——在数据操作权限进行数据资产转移;
+
+// Create
+// @Description: 添加手工数据的操作日志
+// @author: Roc
+// @receiver m
+// @datetime 2024-07-30 16:25:55
+// @return err error
+func (m *EdbinfoOpRecord) Create() (err error) {
+	o := orm.NewOrmUsingDB("edb")
+	_, err = o.Insert(m)
+	return
+}
+
+// EdbinfoOpRecordItem
+// @Description: 手工数据的操作日志
+type EdbinfoOpRecordItem struct {
+	TradeCode  string `orm:"column(TRADE_CODE);pk" description:"指标编码"`
+	Remark     string `orm:"column(remark)" description:"操作信息"`
+	UserId     int    `orm:"column(user_id)" description:"用户id"`
+	UserName   string `orm:"column(user_name)" description:"用户姓名"`
+	CreateTime string `orm:"column(create_time)" description:"创建时间"`
+}
+
+// EdbinfoOpRecordListResp 指标数据结构体
+type EdbinfoOpRecordListResp struct {
+	List   []*EdbinfoOpRecordItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// GetEdbinfoOpRecordPageList
+// @Description: 根据指标编码获取手工数据的操作日志
+// @author: Roc
+// @datetime 2024-07-30 17:32:33
+// @param tradeCode string
+// @param startSize int
+// @param total int
+// @return pageSize int
+// @return items []*EdbinfoOpRecordItem
+// @return err error
+func GetEdbinfoOpRecordPageList(tradeCode string, startSize, pageSize int) (total int, items []*EdbinfoOpRecordItem, err error) {
+	o := orm.NewOrmUsingDB("edb")
+	sql := `SELECT  count(1) FROM edbinfo_op_record AS a  WHERE TRADE_CODE = ? `
+	err = o.Raw(sql, tradeCode).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	sql = `SELECT  a.* FROM edbinfo_op_record AS a  WHERE TRADE_CODE = ? ORDER BY a.create_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, tradeCode, startSize, pageSize).QueryRows(&items)
+
+	return
+}

+ 1 - 1
models/ppt_english/ppt_english_grant.go

@@ -99,7 +99,7 @@ type PptEnglishInfoGrantItem struct {
 	ReportCode    string    `description:"关联的报告code"`
 	IsShare       int8      `description:"是否分享,0:不分享,1:分享"`
 	PublishTime   time.Time `description:"发布时间"`
-	PptPage       int       `description:"PPT页数"`
+	PptPage       int       `description:"PPT页数"`
 	//GrantId      int64     `orm:"column(grant_id);pk;auto" description:"自增序号"`
 	//PptId        int64     `description:"ppt ID"`
 	//DepartmentId int64     `description:"授权部门id"`

+ 11 - 11
models/ppt_english/ppt_english_group_mapping.go

@@ -43,17 +43,6 @@ func GetPptMappingListByGroupId(groupId int64) (list []*PptEnglishGroupMapping,
 	return
 }
 
-// GetPptMappingListByGroupIds 根据分组ID查找
-func GetPptMappingListByGroupIds(groupIds []int64) (list []*PptEnglishGroupMapping, err error) {
-	_, err = orm.NewOrmUsingDB("rddp").
-		QueryTable("ppt_english_group_mapping").
-		Filter("group_id__in", groupIds).
-		OrderBy("-ppt_sort", "group_ppt_id").
-		All(&list)
-
-	return
-}
-
 // GetPptMappingByPptId 查询目录下, pptId对应的目录映射关系
 func GetPptMappingByPptId(pptId int64) (item *PptEnglishGroupMapping, err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -70,6 +59,17 @@ func GetPptMappingListByGroupIdDesc(groupId int64) (list []*PptEnglishGroupMappi
 	return
 }
 
+// GetPptMappingListByGroupIds 根据分组ID查找
+func GetPptMappingListByGroupIds(groupIds []int64) (list []*PptEnglishGroupMapping, err error) {
+	_, err = orm.NewOrmUsingDB("rddp").
+		QueryTable("ppt_english_group_mapping").
+		Filter("group_id__in", groupIds).
+		OrderBy("-ppt_sort", "group_ppt_id").
+		All(&list)
+
+	return
+}
+
 // GetPptMappingByGroupPptId 查询根据映射ID,查询单个映射关系
 func GetPptMappingByGroupPptId(groupPptId int64, adminId int) (item *PptEnglishGroupMapping, err error) {
 	o := orm.NewOrmUsingDB("rddp")

+ 2 - 2
models/ppt_v2_group_mapping.go

@@ -38,7 +38,7 @@ func GetPptMappingCountByGroupId(groupId int64) (total int64, err error) {
 // GetPptMappingListByGroupId 查询目录下,ppt列表
 func GetPptMappingListByGroupId(groupId int64) (list []*PptV2GroupMapping, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `select group_ppt_id, group_id, ppt_id, ppt_sort, admin_id, admin_real_name, create_time from ppt_v2_group_mapping where group_id=? order by ppt_sort asc, group_ppt_id asc `
+	sql := `select group_ppt_id, group_id, ppt_id, ppt_sort, admin_id, admin_real_name, create_time from ppt_v2_group_mapping where group_id=? order by ppt_sort desc, group_ppt_id desc `
 	_, err = o.Raw(sql, groupId).QueryRows(&list)
 	return
 }
@@ -48,7 +48,7 @@ func GetPptMappingListByGroupIds(groupIds []int64) (list []*PptV2GroupMapping, e
 	_, err = orm.NewOrmUsingDB("rddp").
 		QueryTable("ppt_v2_group_mapping").
 		Filter("group_id__in", groupIds).
-		OrderBy("-ppt_sort", "group_ppt_id").
+		OrderBy("-ppt_sort", "-group_ppt_id").
 		All(&list)
 
 	return

+ 1 - 1
models/report.go

@@ -903,6 +903,7 @@ type ElasticReportDetail struct {
 	ClassifyName       string `description:"最小单元的分类名称"`
 	Categories         string `description:"关联的品种名称(包括品种别名)"`
 	StageStr           string `description:"报告期数"`
+	BodyMd5            string `description:"MD5加密之后的内容"`
 }
 
 // GetLastPublishedDayWeekReport 获取上一篇已发布的晨周报
@@ -1488,7 +1489,6 @@ func GetReportLayoutByReportId(reportId int) (item ReportLayout, err error) {
 
 	sql := `SELECT id, report_layout  FROM report  WHERE id = ? `
 	err = o.Raw(sql, reportId).QueryRow(&item)
-
 	return
 }
 

+ 26 - 26
models/sandbox/sandbox.go

@@ -411,6 +411,32 @@ func GetFirstSandboxByClassifyId(classifyId int) (item *Sandbox, err error) {
 	return
 }
 
+// UpdateSandboxContent 更新沙盘内容
+func UpdateSandboxContent(list []Sandbox) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//循环更新沙盘内容
+	for _, sandbox := range list {
+		_, err = to.Update(&sandbox, "Content", "ModifyTime")
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
 // ContentDataStruct 沙盘内容结构体
 type ContentDataStruct struct {
 	Cells []struct {
@@ -438,29 +464,3 @@ type DetailParams struct {
 	Id         int    `json:"id"`
 	ClassifyId int    `json:"classifyId"`
 }
-
-// UpdateSandboxContent 更新沙盘内容
-func UpdateSandboxContent(list []Sandbox) (err error) {
-	o := orm.NewOrmUsingDB("data")
-	to, err := o.Begin()
-	if err != nil {
-		return
-	}
-	defer func() {
-		if err != nil {
-			_ = to.Rollback()
-		} else {
-			_ = to.Commit()
-		}
-	}()
-
-	//循环更新沙盘内容
-	for _, sandbox := range list {
-		_, err = to.Update(&sandbox, "Content", "ModifyTime")
-		if err != nil {
-			return
-		}
-	}
-
-	return
-}

+ 15 - 2
models/smart_report/smart_report.go

@@ -419,11 +419,24 @@ func UpdateSmartReportsStateBySecondIds(oldState, newState int, secondIds []int)
 	return
 }
 
-
 // UpdatePdfUrlSmartReportById 清空pdf相关字段
 func UpdatePdfUrlSmartReportById(reportId int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := `UPDATE smart_report SET detail_img_url = '',detail_pdf_url='',modify_time=NOW() WHERE smart_report_id = ? `
 	_, err = o.Raw(sql, reportId).Exec()
 	return
-}
+}
+
+func GetSmartReportFieldsByIds(ids []int, fields []string) (items []*SmartReport, err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("rddp")
+	field := " * "
+	if len(fields) > 0 {
+		field = fmt.Sprintf(" %s ", strings.Join(fields, ","))
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM smart_report WHERE smart_report_id IN (%s)`, field, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sql, ids).QueryRows(&items)
+	return
+}

+ 63 - 3
models/target.go

@@ -194,6 +194,8 @@ type Edbinfo struct {
 	NoticeTime   string `orm:"column(notice_time)" description:"通知时间"`
 	Mobile       string `orm:"column(mobile)" description:"录入者手机号"`
 	Sort         int    `orm:"column(sort)" description:"排序"`
+	ModifyTime   string `description:"最近一次更新时间"`
+	IsJoinEdb    int8   `description:"指标库是否已添加:0-否;1-是"`
 }
 
 func DeleteEdbinfoByTraceCodeList(tradeCodeList []string) (err error) {
@@ -244,7 +246,23 @@ func GetEdbinfoListCount(condition string, pars []interface{}, mobile string, ro
 	return
 }
 
-func GetEdbinfoList(condition string, pars []interface{}, startSize, pageSize int, mobile string, roleType int) (items []*Edbinfo, err error) {
+type EdbinfoItem 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"`
+	UserName     string `description:"录入用户名称"`
+	NoticeTime   string `description:"通知时间"`
+	Mobile       string `description:"录入者手机号"`
+	ModifyTime   string `description:"最近一次更新时间"`
+}
+
+func GetEdbinfoItemList(condition string, pars []interface{}, startSize, pageSize int, mobile string, roleType int) (items []*EdbinfoItem, err error) {
 	o := orm.NewOrmUsingDB("edb")
 	sql := ``
 	if mobile != "" && roleType == 1 {
@@ -299,7 +317,7 @@ join edbdata b on a.TRADE_CODE=b.TRADE_CODE
 }
 
 type TargetListResp struct {
-	List   []*Edbinfo
+	List   []*EdbinfoItem
 	Paging *paging.PagingItem `description:"分页数据"`
 }
 
@@ -427,6 +445,8 @@ type EdbdataClassify struct {
 	ClassifyName string
 	ParentId     int
 	EdbInfoTotal int
+	TradeCode    string
+	UniqueCode   string
 }
 
 func GetEdbdataClassifyByClassifyName(classifyName string) (item *EdbdataClassify, err error) {
@@ -441,6 +461,8 @@ type EdbdataClassifyList struct {
 	ClassifyName string
 	ParentId     int
 	Child        []*EdbdataClassify
+	TradeCode    string
+	UniqueCode   string
 }
 
 func GetEdbdataClassify(userId int64) (items []*EdbdataClassifyList, err error) {
@@ -1290,11 +1312,14 @@ type EdbInfoItem struct {
 	ClassifyName string     `description:"分类名称"`
 	CreateDate   string     `description:"创建时间"`
 	UserId       int        `description:"录入用户id"`
+	UserName     string     `description:"录入用户名称"`
 	NoticeTime   string     `description:"通知时间"`
 	Mobile       string     `description:"录入者手机号"`
 	ModifyDate   string     `description:"待更新日期"`
+	ModifyTime   string     `description:"最近一次更新时间"`
 	Status       string     `description:"状态:未完成/完成"`
-	Sort         int        `description:"排序"`
+	Sort         int        `orm:"column(sort)" description:"排序"`
+	IsJoinEdb    int8       `description:"指标库是否已添加:0-否;1-是"`
 	DataList     []*Edbdata `description:"指标数据列表"`
 }
 
@@ -1659,3 +1684,38 @@ func GetEdbInfoAdminList() (list []int, err error) {
 	_, err = o.Raw(sql).QueryRows(&list)
 	return
 }
+
+// GetAllChildManualEdbClassify
+// @Description: 获取手工数据中所有的子分类
+// @author: Roc
+// @datetime 2024-07-16 13:27:28
+// @return items []*EdbdataClassify
+// @return err error
+func GetAllChildManualEdbClassify() (items []*EdbdataClassify, err error) {
+	o := orm.NewOrmUsingDB("edb")
+
+	sql := ` SELECT classify_id,classify_name,parent_id FROM edbdata_classify WHERE parent_id > 0 `
+	_, err = o.Raw(sql).QueryRows(&items)
+
+	return
+}
+
+// GetChildManualEdbClassifyByIdList
+// @Description: 获取手工数据中所有的子分类
+// @author: Roc
+// @datetime 2024-07-16 13:33:57
+// @param idList []int
+// @return items []*EdbdataClassify
+// @return err error
+func GetChildManualEdbClassifyByIdList(idList []int) (items []*EdbdataClassify, err error) {
+	num := len(idList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("edb")
+
+	sql := ` SELECT classify_id,classify_name,parent_id FROM edbdata_classify WHERE classify_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, idList).QueryRows(&items)
+
+	return
+}

+ 347 - 23
routers/commentsRouter.go

@@ -871,6 +871,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisCommonController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisCommonController"],
+        beego.ControllerComments{
+            Method: "FixClassify",
+            Router: `/excel/fix_classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
         beego.ControllerComments{
             Method: "Add",
@@ -934,6 +943,33 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/excel/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "Share",
+            Router: `/excel/share`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "ShareDetail",
+            Router: `/excel/share_detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
         beego.ControllerComments{
             Method: "ExcelByName",
@@ -943,6 +979,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
+        beego.ControllerComments{
+            Method: "ClassifyList",
+            Router: `/excel_classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/excel:CustomAnalysisController"],
         beego.ControllerComments{
             Method: "Save",
@@ -2023,6 +2068,150 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "Add",
+            Router: `/chart_info/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "Copy",
+            Router: `/chart_info/copy`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/chart_info/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "DetailFromUniqueCode",
+            Router: `/chart_info/detail/from_unique_code`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/chart_info/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/chart_info/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "Preview",
+            Router: `/chart_info/preview`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartChartInfoController"],
+        beego.ControllerComments{
+            Method: "Refresh",
+            Router: `/chart_info/refresh`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "AddChartClassify",
+            Router: `/chart_classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "DeleteChartClassify",
+            Router: `/chart_classify/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "DeleteChartClassifyCheck",
+            Router: `/chart_classify/delete/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "EditChartClassify",
+            Router: `/chart_classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ChartClassifyItems",
+            Router: `/chart_classify/items`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ChartClassifyList",
+            Router: `/chart_classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ChartClassifyMove",
+            Router: `/chart_classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/range_analysis:RangeChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ClassifyTree",
+            Router: `/chart_classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/supply_analysis:VarietyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/supply_analysis:VarietyController"],
         beego.ControllerComments{
             Method: "Add",
@@ -2842,6 +3031,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "PreviewSectionCombineChartInfo",
+            Router: `/chart_info/preview/section_combine`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
         beego.ControllerComments{
             Method: "PreviewSectionScatterChartInfo",
@@ -2887,6 +3085,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "GetChartTypeList",
+            Router: `/chart_info/type_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
         beego.ControllerComments{
             Method: "SaveAdjustEdbInfo",
@@ -4975,6 +5182,132 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "ImportData",
+            Router: `/import/data`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "ClassifyEdbList",
+            Router: `/target/classify/edb/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchAdd2Edb",
+            Router: `/target/edb/batch/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchAddEdbCheck",
+            Router: `/target/edb/batch/add/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "NameCheck",
+            Router: `/target/edb/batch/add/name_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchDel",
+            Router: `/target/edb/batch/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchDelEdbCheck",
+            Router: `/target/edb/batch/del/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "BatchEdbList",
+            Router: `/target/edb/batch/list`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EdbDetail",
+            Router: `/target/edb/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EditExcelData",
+            Router: `/target/edb/excel_style/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EdbList",
+            Router: `/target/edb/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "RecordList",
+            Router: `/target/edb/op/record/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "EdbSearch",
+            Router: `/target/edb/search`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ManualEdbController"],
+        beego.ControllerComments{
+            Method: "TemplateDownload",
+            Router: `/template`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:MyChartController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:MyChartController"],
         beego.ControllerComments{
             Method: "MyChartAdd",
@@ -5481,8 +5814,8 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
         beego.ControllerComments{
-            Method: "IcpiClassifyList",
-            Router: `/icpi/classify/list`,
+            Method: "ExportIcpiDataList",
+            Router: `/icpi/export/icpiDataList`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -5490,8 +5823,8 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
         beego.ControllerComments{
-            Method: "ExportIcpiDataList",
-            Router: `/icpi/export/icpiDataList`,
+            Method: "Sci99ClassifyList",
+            Router: `/sci99/classify/list`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -5499,8 +5832,8 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
         beego.ControllerComments{
-            Method: "IcpiData",
-            Router: `/icpi/index/data`,
+            Method: "ExportSci99DataList",
+            Router: `/sci99/export/sci99DataList`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -5508,26 +5841,26 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceController"],
         beego.ControllerComments{
-            Method: "Sci99ClassifyList",
-            Router: `/sci99/classify/list`,
+            Method: "Sci99Data",
+            Router: `/sci99/index/data`,
             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.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceIcpiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceIcpiController"],
         beego.ControllerComments{
-            Method: "ExportSci99DataList",
-            Router: `/sci99/export/sci99DataList`,
+            Method: "IcpiClassifyList",
+            Router: `/icpi/classify/list`,
             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.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceIcpiController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_source:DataSourceIcpiController"],
         beego.ControllerComments{
-            Method: "Sci99Data",
-            Router: `/sci99/index/data`,
+            Method: "IcpiData",
+            Router: `/icpi/index/data`,
             AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
@@ -9682,15 +10015,6 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"],
-        beego.ControllerComments{
-            Method: "ImportData",
-            Router: `/import/data`,
-            AllowHTTPMethods: []string{"post"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
     beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:TargetController"],
         beego.ControllerComments{
             Method: "ImportFailListDownload",

+ 28 - 11
routers/router.go

@@ -18,6 +18,7 @@ import (
 	future_good2 "eta/eta_api/controllers/data_manage/future_good"
 	"eta/eta_api/controllers/data_manage/line_equation"
 	"eta/eta_api/controllers/data_manage/line_feature"
+	"eta/eta_api/controllers/data_manage/range_analysis"
 	"eta/eta_api/controllers/data_manage/supply_analysis"
 	"eta/eta_api/controllers/data_source"
 	"eta/eta_api/controllers/data_stat"
@@ -104,6 +105,7 @@ func init() {
 		web.NSNamespace("/entry",
 			web.NSInclude(
 				&controllers.TargetController{},
+				&data_manage.ManualEdbController{},
 			),
 			web.NSInclude(
 				&controllers.TargetCommonController{},
@@ -170,9 +172,9 @@ func init() {
 				&data_manage_permission.DataMangePermissionController{},
 				&data_manage.BloombergDataController{},
 				&data_manage.EdbBusinessController{},
-				&data_manage.BaseFromThsHfController{},
 				&data_manage.EdbInfoRelationController{},
 				&data_manage.FactorEdbSeriesController{},
+				&data_manage.BaseFromThsHfController{},
 			),
 		),
 		web.NSNamespace("/my_chart",
@@ -288,6 +290,7 @@ func init() {
 		web.NSNamespace("/custom_analysis",
 			web.NSInclude(
 				&excel.CustomAnalysisController{},
+				&excel.CustomAnalysisCommonController{},
 			),
 		),
 		web.NSNamespace("/out_link",
@@ -295,6 +298,12 @@ func init() {
 				&controllers.OutLinkController{},
 			),
 		),
+		web.NSNamespace("/user_login",
+			web.NSInclude(
+				&controllers.UserLoginController{},
+				&controllers.UserLoginAuthController{},
+			),
+		),
 		web.NSNamespace("/eta_trial",
 			web.NSInclude(
 				&eta_trial.EtaTrialController{},
@@ -317,21 +326,15 @@ func init() {
 				&data_stat.EdbTerminalController{},
 			),
 		),
-		web.NSNamespace("/chart_framework",
-			web.NSInclude(
-				&data_manage.ChartFrameworkController{},
-			),
-		),
 		web.NSNamespace("/smart_report",
 			web.NSInclude(
 				&smart_report.SmartReportController{},
 				&smart_report.SmartReportResourceController{},
 			),
 		),
-		web.NSNamespace("/ai",
+		web.NSNamespace("/chart_framework",
 			web.NSInclude(
-				&ai.AiController{},
-				&ai.AiFileController{},
+				&data_manage.ChartFrameworkController{},
 			),
 		),
 		web.NSNamespace("/cross_variety", //跨品种分析
@@ -348,9 +351,17 @@ func init() {
 				&report_approve.ReportApproveFlowController{},
 			),
 		),
+		web.NSNamespace("/ai",
+			web.NSInclude(
+				&ai.AiController{},
+				&ai.AiController{},
+				&ai.AiFileController{},
+			),
+		),
 		web.NSNamespace("/data_source",
 			web.NSInclude(
 				&data_source.DataSourceController{},
+				&data_source.DataSourceIcpiController{},
 			),
 		),
 		web.NSNamespace("/permission",
@@ -367,14 +378,20 @@ func init() {
 				&speech_recognition.SpeechRecognitionTagMenuController{},
 			),
 		),
+		web.NSNamespace("/fe_calendar",
+			web.NSInclude(
+				&fe_calendar.FeCalendarMatterController{},
+			),
+		),
 		web.NSNamespace("/message",
 			web.NSInclude(
 				&controllers.MessageController{},
 			),
 		),
-		web.NSNamespace("/fe_calendar",
+		web.NSNamespace("/range_analysis",
 			web.NSInclude(
-				&fe_calendar.FeCalendarMatterController{},
+				&range_analysis.RangeChartClassifyController{},
+				&range_analysis.RangeChartChartInfoController{},
 			),
 		),
 	)

+ 684 - 2
services/data/chart_extra_config.go

@@ -5,13 +5,16 @@ import (
 	"errors"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/services/google"
+	"eta/eta_api/utils"
+	"fmt"
+	"sort"
 	"strconv"
 	"strings"
+	"time"
 )
 
-func HandleExtraConfig(chartType int, extraConfigStr string) (newExtraConfigStr string, err error, errMsg string) {
+func HandleExtraConfig(chartType int, extraConfigStr string) (newExtraConfigStr string, newAllExtraConfigStr string, err error, errMsg string) {
 	newExtraConfigStr = extraConfigStr
-
 	switch chartType {
 	case 10: // 截面散点图
 		var tmpExtraConfig data_manage.SectionScatterReq
@@ -28,6 +31,48 @@ func HandleExtraConfig(chartType int, extraConfigStr string) (newExtraConfigStr
 		}
 
 		newExtraConfigStr, err, errMsg = handleSectionScatterChartData(tmpExtraConfig)
+	case utils.CHART_TYPE_SECTION_COMBINE:
+		//整理组合图的配置信息,将部分信息存入其他表
+		// 预览和详情都走这个接口
+		var sectionExtraConfig data_manage.ChartSectionAllExtraConf
+		if extraConfigStr == `` {
+			errMsg = "截面组合图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &sectionExtraConfig)
+		if err != nil {
+			errMsg = "截面组合图配置异常"
+			err = errors.New(errMsg)
+			return
+		}
+		newAllExtraConfig, e, msg := handleChartSectionCombineData(sectionExtraConfig)
+		if e != nil {
+			err = e
+			errMsg = msg
+			return
+		}
+		newAllExtraConfigByte, e := json.Marshal(newAllExtraConfig)
+		if e != nil {
+			err = e
+			return
+		}
+		newAllExtraConfigStr = string(newAllExtraConfigByte)
+
+		newExtraConfig := &data_manage.ChartSectionExtraConf{
+			DateConfList:        newAllExtraConfig.DateConfList,
+			IsHeap:              newAllExtraConfig.IsHeap,
+			XDataList:           newAllExtraConfig.XDataList,
+			UnitList:            newAllExtraConfig.UnitList,
+			BaseChartSeriesName: newAllExtraConfig.BaseChartSeriesName,
+			SortType:            newAllExtraConfig.SortType,
+		}
+		extraConfigByte, e := json.Marshal(newExtraConfig)
+		if e != nil {
+			err = e
+			return
+		}
+		newExtraConfigStr = string(extraConfigByte)
 	}
 	return
 }
@@ -197,3 +242,640 @@ func GetEnNameMapByCnNameList(cnNameList []string) (contentEnMap map[string]stri
 	}
 	return
 }
+
+// handleSectionScatterChartData 截面组合图的英文文案处理
+func handleChartSectionCombineData(extraConfig data_manage.ChartSectionAllExtraConf) (newExtraConfig data_manage.ChartSectionAllExtraConf, err error, errMsg string) {
+	for k, v := range extraConfig.DateConfList {
+		if v.DateConfNameEn == "" {
+			extraConfig.DateConfList[k].DateConfNameEn = v.DateConfName
+		}
+	}
+
+	if extraConfig.UnitList.LeftNameEn == "" {
+		extraConfig.UnitList.LeftNameEn = extraConfig.UnitList.LeftName
+	}
+	if extraConfig.UnitList.RightNameEn == "" {
+		extraConfig.UnitList.RightNameEn = extraConfig.UnitList.RightName
+	}
+	if extraConfig.UnitList.RightTwoNameEn == "" {
+		extraConfig.UnitList.RightTwoNameEn = extraConfig.UnitList.RightTwoName
+	}
+
+	for k, v := range extraConfig.XDataList {
+		if v.NameEn == "" {
+			extraConfig.XDataList[k].NameEn = v.Name
+		}
+	}
+	for k, v := range extraConfig.SeriesList {
+		if v.SeriesNameEn == `` {
+			extraConfig.SeriesList[k].SeriesNameEn = v.SeriesName
+		}
+	}
+
+	newExtraConfig = extraConfig
+	return
+}
+
+// GetChartSectionCombineData 截面组合图的数据处理
+func GetChartSectionCombineData(chartInfo *data_manage.ChartInfo, mappingList []*data_manage.ChartEdbInfoMapping, edbDataListMap map[int][]*data_manage.EdbDataList, extraConfig data_manage.ChartSectionAllExtraConf) (edbIdList []int, dataListResp data_manage.ChartSectionCombineDataResp, err error) {
+	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
+	edbDataMap := make(map[int]map[string]float64)
+	for edbInfoId, edbDataList := range edbDataListMap {
+		edbDateData := make(map[string]float64)
+		for _, edbData := range edbDataList {
+			edbDateData[edbData.DataTime] = edbData.Value
+		}
+		edbDataMap[edbInfoId] = edbDateData
+	}
+
+	// edbIdList 指标展示顺序;x轴的指标顺序
+	edbIdList = make([]int, 0)
+	edbMappingMap := make(map[int]*data_manage.ChartEdbInfoMapping)
+	for _, v := range mappingList {
+		edbIdList = append(edbIdList, v.EdbInfoId)
+		edbMappingMap[v.EdbInfoId] = v
+	}
+	// 确定好截面散点图返回的数据格式
+	// 获取所有的引用日期设置
+	dateConfListMap := make(map[string]*data_manage.ChartSectionDateConfItem)
+	dateConfEdbIds := make([]int, 0)
+	for _, v := range extraConfig.DateConfList {
+		if v.EdbInfoId > 0 {
+			dateConfEdbIds = append(dateConfEdbIds, v.EdbInfoId)
+		}
+		dateConfListMap[v.DateConfName] = v
+	}
+	// 遍历每个系列
+	// 遍历每个指标,根据选中的日期,进行日期变换得到最终的日期,根据最终的日期获取对应的值
+	// 组装数据
+	baseSeries := new(data_manage.ChartSectionSeriesItem) //y轴的系列
+	var firstUnit, leftUnit, rightUnit, right2Unit *data_manage.XData
+	var (
+		LeftMin   float64
+		LeftMax   float64
+		RightMin  float64
+		RightMax  float64
+		Right2Min float64
+		Right2Max float64
+	)
+	seriesDataListMap := make(map[string][]float64)
+	seriesNoDataIndexMap := make(map[string][]int)
+	for _, seriesItem := range extraConfig.SeriesList {
+		var maxDate time.Time
+		var minVal, maxVal float64
+		noDataEdbIndex := make([]int, 0)
+		dataList := make([]float64, len(seriesItem.EdbInfoList))
+		for index, edbConf := range seriesItem.EdbInfoList {
+			edbInfoId := edbConf.EdbInfoId //X轴的指标
+			edbMappingInfo, ok := edbMappingMap[edbInfoId]
+			if !ok {
+				continue
+			}
+			seriesItem.EdbInfoList[index].EdbName = edbMappingInfo.EdbName
+			seriesItem.EdbInfoList[index].EdbNameEn = edbMappingInfo.EdbNameEn
+			seriesItem.EdbInfoList[index].EdbInfoType = edbMappingInfo.EdbInfoCategoryType
+			seriesItem.EdbInfoList[index].Unit = edbMappingInfo.Unit
+			seriesItem.EdbInfoList[index].UnitEn = edbMappingInfo.UnitEn
+			if index == 0 {
+				firstUnit = &data_manage.XData{
+					Name:   edbMappingInfo.Unit,
+					NameEn: edbMappingInfo.UnitEn,
+				}
+			}
+			edbDataList, ok3 := edbDataListMap[edbInfoId]
+			if !ok3 {
+				err = fmt.Errorf("指标%d的日期数据不存在", edbInfoId)
+				return
+			}
+			//日期变换处理,判断用指标的最新日期还是,直接获取引用日期
+			var findDate string
+			if edbConf.DateConfType == 0 {
+				if edbInfoId == 0 {
+					err = fmt.Errorf("请选择指标")
+					return
+				}
+				findDate, err = GetChartSectionSeriesDateByDateChange(edbInfoId, edbDataList, edbConf.DateConf.DateChange, edbConf.DateConf.MoveForward)
+				if err != nil {
+					err = fmt.Errorf("指标%d的日期变换处理失败", edbInfoId)
+					return
+				}
+			} else {
+				// 获取日期配置
+				dateConfItem, ok1 := dateConfListMap[edbConf.DateConfName]
+				if !ok1 {
+					err = fmt.Errorf("引用日期配置不存在")
+					return
+				}
+				// todo 根据日期变换得到最终日期
+				edbDataListTmp := make([]*data_manage.EdbDataList, 0)
+				if dateConfItem.EdbInfoId > 0 {
+					edbDataListTmp, ok1 = edbDataListMap[dateConfItem.EdbInfoId]
+					if !ok1 {
+						err = fmt.Errorf("指标%d的日期数据不存在", dateConfItem.EdbInfoId)
+						return
+					}
+				}
+
+				findDate, err = GetChartSectionSeriesDateByDateChange(dateConfItem.EdbInfoId, edbDataListTmp, dateConfItem.DateChange, dateConfItem.MoveForward)
+				if err != nil {
+					err = fmt.Errorf("指标%d的日期变换处理失败", dateConfItem.EdbInfoId)
+					return
+				}
+			}
+			findDateTime, _ := time.ParseInLocation(utils.FormatDate, findDate, time.Local)
+			if maxDate.IsZero() {
+				maxDate = findDateTime
+			} else {
+				if findDateTime.After(maxDate) {
+					maxDate = findDateTime
+				}
+			}
+			if tmpValue, ok := edbDataMap[edbInfoId][findDate]; ok {
+				dataList[index] = tmpValue
+				if index == 0 {
+					minVal = tmpValue
+					maxVal = tmpValue
+				} else {
+					if tmpValue < minVal {
+						minVal = tmpValue
+					}
+					if tmpValue > maxVal {
+						maxVal = tmpValue
+					}
+				}
+			} else {
+				dataList[index] = 0
+				noDataEdbIndex = append(noDataEdbIndex, index)
+				continue
+			}
+		}
+		seriesDataListMap[seriesItem.SeriesName] = dataList
+		seriesNoDataIndexMap[seriesItem.SeriesName] = noDataEdbIndex
+		seriesItem.DataList = dataList
+		seriesItem.MinData = minVal
+		seriesItem.MaxData = maxVal
+		seriesItem.NoDataEdbIndex = noDataEdbIndex
+		if extraConfig.BaseChartSeriesName == seriesItem.SeriesName {
+			baseSeries = seriesItem
+		}
+		if seriesItem.IsAxis == 1 && leftUnit == nil { //左轴,右轴
+			leftUnit = firstUnit
+		} else if seriesItem.IsAxis == 0 && rightUnit == nil {
+			rightUnit = firstUnit
+		} else if seriesItem.IsAxis == 2 && right2Unit == nil {
+			right2Unit = firstUnit
+		}
+
+		//处理上下限
+		var minData, maxData float64
+		for _, d := range seriesItem.DataList {
+			if minData > d {
+				minData = d
+			}
+			if maxData < d {
+				maxData = d
+			}
+		}
+		if seriesItem.IsAxis == 1 {
+			if LeftMin > minData {
+				LeftMin = minData
+			}
+			if LeftMax < maxData {
+				LeftMax = maxData
+			}
+		} else if seriesItem.IsAxis == 0 {
+			if RightMin > minData {
+				RightMin = minData
+			}
+			if RightMax < maxData {
+				RightMax = maxData
+			}
+		} else {
+			if Right2Min > minData {
+				Right2Min = minData
+			}
+			if Right2Max < maxData {
+				Right2Max = maxData
+			}
+		}
+	}
+	// 处理横轴
+	// 遍历基准系列,判断有几个横轴名称
+	if baseSeries == nil {
+		err = fmt.Errorf("基准系列不存在")
+		return
+	}
+	// 处理系列排序
+	if extraConfig.SortType > 0 {
+		newSeriesDataListMap, newSeriesNoDataIndexMap := SortChartSeriesDataSet(baseSeries.SeriesName, baseSeries.DataList, baseSeries.NoDataEdbIndex, seriesDataListMap, seriesNoDataIndexMap, extraConfig.SortType)
+		for k, item := range extraConfig.SeriesList {
+			dataList, ok := newSeriesDataListMap[item.SeriesName]
+			if ok {
+				extraConfig.SeriesList[k].DataList = dataList
+			}
+			noIndex, ok := newSeriesNoDataIndexMap[item.SeriesName]
+			if ok {
+				extraConfig.SeriesList[k].NoDataEdbIndex = noIndex
+			}
+		}
+	}
+
+	xDataList := make([]data_manage.XData, 0)
+	for index, item := range baseSeries.EdbInfoList {
+		if index == 0 {
+			firstUnit = &data_manage.XData{
+				Name:   item.Unit,
+				NameEn: item.UnitEn,
+			}
+		}
+		tmp := data_manage.XData{
+			Name:   item.EdbName,
+			NameEn: item.EdbNameEn,
+		}
+		// 如果已经设置了横轴名称,则用设置的名称替换
+		if len(extraConfig.XDataList) > index {
+			newItem := extraConfig.XDataList[index]
+			if newItem.Name != "" {
+				tmp = newItem
+			}
+		}
+		xDataList = append(xDataList, tmp)
+	}
+	dataListResp.XDataList = xDataList
+
+	unitList := new(data_manage.ChartSectionCombineUnit)
+	if baseSeries.IsAxis == 1 { //左轴,右轴
+		leftUnit = firstUnit
+	} else if baseSeries.IsAxis == 2 {
+		rightUnit = firstUnit
+	} else {
+		right2Unit = firstUnit
+	}
+	if leftUnit != nil {
+		unitList.LeftName = leftUnit.Name
+		unitList.LeftNameEn = leftUnit.NameEn
+	}
+	if rightUnit != nil {
+		unitList.RightName = rightUnit.Name
+		unitList.RightNameEn = rightUnit.NameEn
+	}
+	if right2Unit != nil {
+		unitList.RightTwoName = right2Unit.Name
+		unitList.RightTwoNameEn = right2Unit.NameEn
+	}
+	if extraConfig.UnitList.LeftName != "" {
+		unitList.LeftName = extraConfig.UnitList.LeftName
+		unitList.LeftNameEn = extraConfig.UnitList.LeftNameEn
+	}
+	if extraConfig.UnitList.RightName != "" {
+		unitList.RightName = extraConfig.UnitList.RightName
+		unitList.RightNameEn = extraConfig.UnitList.RightNameEn
+	}
+
+	if extraConfig.UnitList.RightTwoName != "" {
+		unitList.RightTwoName = extraConfig.UnitList.RightTwoName
+		unitList.RightTwoNameEn = extraConfig.UnitList.RightTwoNameEn
+	}
+
+	if chartInfo != nil && chartInfo.MinMaxSave == 1 {
+		dataListResp.LeftMin = chartInfo.LeftMin
+		dataListResp.LeftMax = chartInfo.LeftMax
+		dataListResp.RightMin = chartInfo.RightMin
+		dataListResp.RightMax = chartInfo.RightMax
+		dataListResp.Right2Min = chartInfo.Right2Min
+		dataListResp.Right2Max = chartInfo.Right2Max
+	} else {
+		dataListResp.LeftMin = strconv.FormatFloat(LeftMin, 'f', -1, 64)
+		dataListResp.LeftMax = strconv.FormatFloat(LeftMax, 'f', -1, 64)
+		dataListResp.RightMin = strconv.FormatFloat(RightMin, 'f', -1, 64)
+		dataListResp.RightMax = strconv.FormatFloat(RightMax, 'f', -1, 64)
+		dataListResp.Right2Min = strconv.FormatFloat(Right2Min, 'f', -1, 64)
+		dataListResp.Right2Max = strconv.FormatFloat(Right2Max, 'f', -1, 64)
+	}
+
+	// 查询引用日期里的指标信息
+	if len(dateConfEdbIds) > 0 {
+		dateConfEdbList, e := data_manage.GetEdbInfoByIdList(dateConfEdbIds)
+		if e != nil {
+			err = fmt.Errorf("查询引用日期里的指标信息失败,错误信息:%s", e.Error())
+			return
+		}
+		dateConfEdbMap := make(map[int]*data_manage.EdbInfo)
+		for _, dateConfEdb := range dateConfEdbList {
+			dateConfEdbMap[dateConfEdb.EdbInfoId] = dateConfEdb
+		}
+		for i, dateConf := range extraConfig.DateConfList {
+			if dateConf.EdbInfoId > 0 {
+				edbItem, ok := dateConfEdbMap[dateConf.EdbInfoId]
+				if ok {
+					extraConfig.DateConfList[i].EdbName = edbItem.EdbName
+					extraConfig.DateConfList[i].EdbInfoId = edbItem.EdbInfoId
+					extraConfig.DateConfList[i].EdbInfoType = edbItem.EdbInfoType
+					extraConfig.DateConfList[i].Frequency = edbItem.Frequency
+					extraConfig.DateConfList[i].EndDate = edbItem.EndDate
+				}
+
+			}
+		}
+	}
+
+	dataListResp.SeriesList = extraConfig.SeriesList
+	dataListResp.DateConfList = extraConfig.DateConfList
+	dataListResp.BaseChartSeriesName = extraConfig.BaseChartSeriesName
+	dataListResp.UnitList = unitList
+	dataListResp.IsHeap = extraConfig.IsHeap
+	dataListResp.SortType = extraConfig.SortType
+	return
+}
+
+// GetChartSectionSeriesDateByDateChange 获取日期变换后的日期edbInfoId 1指标日期,2 系统日期
+func GetChartSectionSeriesDateByDateChange(edbInfoId int, dataList []*data_manage.EdbDataList, dateChange []*data_manage.ChartSectionDateChange, moveForward int) (newDate string, err error) {
+	if edbInfoId > 0 { //指标日期
+		newDate = GetEdbDateByMoveForward(moveForward, dataList)
+	} else {
+		//系统日期
+		newDate = time.Now().Format(utils.FormatDate)
+	}
+	if newDate != "" && len(dateChange) > 0 {
+		newDate, err = HandleChartSectionSeriesDateChange(newDate, dateChange)
+	}
+	return
+}
+
+func GetEdbDateByMoveForward(moveForward int, edbDataList []*data_manage.EdbDataList) (date string) {
+	dateList := make([]string, 0)
+	for _, v := range edbDataList {
+		dateList = append(dateList, v.DataTime)
+	}
+
+	date = GetEdbDateByMoveForwardByDateList(moveForward, dateList)
+	return
+}
+
+func GetEdbDateByMoveForwardByDateList(moveForward int, dateList []string) (date string) {
+	// 根据日期进行排序
+	index := len(dateList) - 1 - moveForward
+	for k, v := range dateList {
+		if k == index {
+			date = v
+			return
+		}
+	}
+	return
+}
+
+// HandleChartSectionSeriesDateChange 处理日期变换
+func HandleChartSectionSeriesDateChange(date string, dateChange []*data_manage.ChartSectionDateChange) (newDate string, err error) {
+	newDate = date
+	if newDate != "" {
+		if len(dateChange) > 0 {
+			var dateTime time.Time
+			dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+			if err != nil {
+				err = fmt.Errorf("日期解析失败: %s", err.Error())
+				return
+			}
+			for _, v := range dateChange {
+				if v.ChangeType == 1 {
+					dateTime = dateTime.AddDate(v.Year, v.Month, v.Day)
+					newDate = dateTime.Format(utils.FormatDate)
+				} else if v.ChangeType == 2 {
+					newDate, err, _ = handleSystemAppointDateT(dateTime, v.FrequencyDay, v.Frequency)
+					if err != nil {
+						return
+					}
+					dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+					if err != nil {
+						err = fmt.Errorf("日期解析失败: %s", err.Error())
+						return
+					}
+				}
+			}
+		}
+	}
+
+	return
+}
+
+// handleSystemAppointDateT
+// @Description: 处理系统日期相关的指定频率(所在周/旬/月/季/半年/年的最后/最早一天)
+// @author: Roc
+// @datetime2023-10-27 09:31:35
+// @param Frequency string
+// @param Day string
+// @return date string
+// @return err error
+// @return errMsg string
+func handleSystemAppointDateT(currDate time.Time, appointDay, frequency string) (date string, err error, errMsg string) {
+	//currDate := time.Now()
+	switch frequency {
+	case "本周":
+		day := int(currDate.Weekday())
+		if day == 0 { // 周日
+			day = 7
+		}
+		num := 0
+		switch appointDay {
+		case "周一":
+			num = 1
+		case "周二":
+			num = 2
+		case "周三":
+			num = 3
+		case "周四":
+			num = 4
+		case "周五":
+			num = 5
+		case "周六":
+			num = 6
+		case "周日":
+			num = 7
+		}
+		day = num - day
+		date = currDate.AddDate(0, 0, day).Format(utils.FormatDate)
+	case "本旬":
+		day := currDate.Day()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 11, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 21, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if day <= 10 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, currDate.Location())
+			} else if day <= 20 {
+				tmpDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+			}
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	case "本月":
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month(), 1, 0, 0, 0, 0, currDate.Location())
+		case "最后一天":
+			tmpDate = time.Date(currDate.Year(), currDate.Month()+1, 1, 0, 0, 0, 0, currDate.Location()).AddDate(0, 0, -1)
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	case "本季":
+		month := currDate.Month()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 4, 1, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 10, 1, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if month <= 3 {
+				tmpDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location())
+			} else if month <= 9 {
+				tmpDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+			}
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	case "本半年":
+		month := currDate.Month()
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 7, 1, 0, 0, 0, 0, currDate.Location())
+			}
+		case "最后一天":
+			if month <= 6 {
+				tmpDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, currDate.Location())
+			} else {
+				tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+			}
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	case "本年":
+		var tmpDate time.Time
+		switch appointDay {
+		case "第一天":
+			tmpDate = time.Date(currDate.Year(), 1, 1, 0, 0, 0, 0, currDate.Location())
+		case "最后一天":
+			tmpDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, currDate.Location())
+		}
+		date = tmpDate.Format(utils.FormatDate)
+	default:
+		errMsg = "错误的日期频度:" + frequency
+		err = errors.New(errMsg)
+		return
+	}
+
+	return
+}
+
+// sortTripleDataSet 以第一组数据为基准,排序之后,空数组的位置也要同步变更
+func SortChartSeriesDataSet(baseName string, baseDataList []float64, baseSeriesNoDataIndexList []int, dataListMap map[string][]float64, noDataListIndexMap map[string][]int, asc int) (newDataListMap map[string][]float64, newNoDataListIndexMap map[string][]int) {
+	newDataListMap = make(map[string][]float64)
+	newNoDataListIndexMap = make(map[string][]int)
+
+	indices := make([]int, len(baseDataList))
+	newIndices := make([]int, len(baseDataList)-len(baseSeriesNoDataIndexList))
+	// 初始化indices
+	for i := range indices {
+		indices[i] = i
+	}
+	if len(baseSeriesNoDataIndexList) > 0 { //把空值移动到最右边
+		j := 0
+		for i := range indices {
+			isEmpty := false
+			for _, v := range baseSeriesNoDataIndexList {
+				if i == v {
+					isEmpty = true
+					break
+				}
+			}
+			if isEmpty {
+				continue
+			}
+			newIndices[j] = i
+			j += 1
+		}
+		newIndices = append(newIndices, baseSeriesNoDataIndexList...)
+		// 根据排序后的indices重新排列所有组的数据
+		for i, idx := range newIndices {
+			for k, _ := range dataListMap {
+				if _, ok := newDataListMap[k]; !ok {
+					newDataListMap[k] = make([]float64, len(baseDataList))
+				}
+				if utils.InArrayByInt(noDataListIndexMap[k], idx) { //如果i位置上的数据为空,那么
+					newNoDataListIndexMap[k] = append(newNoDataListIndexMap[k], i)
+				}
+				newDataListMap[k][i] = dataListMap[k][idx]
+			}
+		}
+		dataListMap = newDataListMap
+		noDataListIndexMap = newNoDataListIndexMap
+		newDataListMap = make(map[string][]float64)
+		newNoDataListIndexMap = make(map[string][]int)
+		baseDataList, _ = dataListMap[baseName]
+		//先把空的数据移动到最后面
+		indices = make([]int, len(baseDataList)-len(baseSeriesNoDataIndexList)) //空值不参与排序
+		newIndices = make([]int, len(baseDataList))                             //空值不参与排序
+		// 初始化indices
+		for i := range indices {
+			indices[i] = i
+		}
+
+	}
+	length := len(indices)
+	baseDataSortList := make([]data_manage.ChartSectionSeriesValSort, length)
+	for i, value := range baseDataList {
+		if i < length {
+			baseDataSortList[i] = data_manage.ChartSectionSeriesValSort{Index: i, Value: value}
+		}
+	}
+	if asc == 1 {
+		// 使用sort.Sort进行排序
+		sort.Sort(data_manage.ChartSectionSeriesValSortAsc(baseDataSortList))
+	} else {
+		sort.Sort(data_manage.ChartSectionSeriesValSortDesc(baseDataSortList))
+	}
+
+	for k, v := range baseDataSortList {
+		indices[k] = v.Index
+	}
+
+	for i := range newIndices {
+		if i < length {
+			newIndices[i] = indices[i]
+		} else {
+			newIndices[i] = i
+		}
+	}
+	// 根据排序后的indices重新排列所有组的数据
+	for i, idx := range newIndices {
+		for k, _ := range dataListMap {
+			if _, ok := newDataListMap[k]; !ok {
+				newDataListMap[k] = make([]float64, len(baseDataList))
+			}
+			if utils.InArrayByInt(noDataListIndexMap[k], idx) { //如果i位置上的数据为空,那么
+				newNoDataListIndexMap[k] = append(newNoDataListIndexMap[k], i)
+			}
+			newDataListMap[k][i] = dataListMap[k][idx]
+		}
+	}
+	return
+}

+ 494 - 3
services/data/chart_info.go

@@ -301,6 +301,18 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 
 	var extraConfig interface{}
 	switch chartType {
+	case 6:
+		var tmpConfig data_manage.ChartTimeCombineExtraConf
+		if extraConfigStr != `` {
+			err = json.Unmarshal([]byte(extraConfigStr), &tmpConfig)
+			if err != nil {
+				errMsg = "雷达图配置异常"
+				err = errors.New(errMsg)
+				return
+			}
+		}
+
+		extraConfig = tmpConfig
 	case 7: // 柱形图
 		var barConfig data_manage.BarChartInfoReq
 		if extraConfigStr == `` {
@@ -344,6 +356,159 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 			return
 		}
 		extraConfig = barConfig
+	case utils.CHART_TYPE_SECTION_COMBINE:
+		// 预览和详情都走这个接口
+		var sectionExtraConfig data_manage.ChartSectionAllExtraConf
+		if extraConfigStr == `` {
+			errMsg = "截面组合图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		if chartInfoId > 0 {
+			var sectionExtraConfigTmp data_manage.ChartSectionExtraConf
+			err = json.Unmarshal([]byte(extraConfigStr), &sectionExtraConfigTmp)
+			if err != nil {
+				errMsg = "截面组合图配置异常"
+				err = errors.New(errMsg)
+				return
+			}
+			sectionExtraConfig.XDataList = sectionExtraConfigTmp.XDataList
+			sectionExtraConfig.BaseChartSeriesName = sectionExtraConfigTmp.BaseChartSeriesName
+			sectionExtraConfig.UnitList = sectionExtraConfigTmp.UnitList
+			sectionExtraConfig.DateConfList = sectionExtraConfigTmp.DateConfList
+			sectionExtraConfig.IsHeap = sectionExtraConfigTmp.IsHeap
+			sectionExtraConfig.SortType = sectionExtraConfigTmp.SortType
+			// 查询所有的seriesEdb
+			seriesEdbList, e := data_manage.GetChartSeriesEdbByChartInfoId(chartInfoId)
+			if e != nil {
+				errMsg = "查询seriesEdb失败"
+				err = errors.New(errMsg + e.Error())
+				return
+			}
+			// todo 是否有必要返回单位信息
+			// 组装成map
+			seriesEdbMap := make(map[int][]*data_manage.ChartSectionSeriesEdbConf)
+			for _, v := range seriesEdbList {
+				var dateConf *data_manage.ChartSectionSeriesDateConfItem
+				if v.DateConf != "" {
+					err = json.Unmarshal([]byte(v.DateConf), &dateConf)
+					if err != nil {
+						errMsg = "截面组合图配置异常"
+						err = errors.New(errMsg + e.Error())
+						return
+					}
+				}
+				tmp := &data_manage.ChartSectionSeriesEdbConf{
+					ChartSeriesEdbMappingId: v.ChartSeriesEdbMappingId,
+					ChartSeriesId:           v.ChartSeriesId,
+					//ChartInfoId:             v.ChartInfoId,
+					EdbInfoId:    v.EdbInfoId,
+					DateConf:     dateConf,
+					EdbName:      "",
+					EdbNameEn:    "",
+					Unit:         "",
+					UnitEn:       "",
+					DateConfName: v.DateConfName,
+					DateConfType: v.DateConfType,
+				}
+				seriesEdbMap[v.ChartSeriesId] = append(seriesEdbMap[v.ChartSeriesId], tmp)
+			}
+			//查询series
+			seriesListTmp, e := data_manage.GetChartSeriesByChartInfoId(chartInfoId)
+			if e != nil {
+				errMsg = "查询series失败"
+				err = errors.New(errMsg + e.Error())
+				return
+			}
+			seriesList := make([]*data_manage.ChartSectionSeriesItem, 0)
+			for _, v := range seriesListTmp {
+
+				tmpSeries := &data_manage.ChartSectionSeriesItem{
+					ChartSeriesId: v.ChartSeriesId,
+					SeriesName:    v.SeriesName,
+					SeriesNameEn:  v.SeriesNameEn,
+					ChartStyle:    v.ChartStyle,
+					ChartColor:    v.ChartColor,
+					ChartWidth:    v.ChartWidth,
+					IsPoint:       v.IsPoint,
+					IsNumber:      v.IsNumber,
+					IsAxis:        v.IsAxis,
+					MaxData:       v.MaxData,
+					MinData:       v.MinData,
+					//IsOrder:         false,
+					EdbInfoList: nil,
+					DataList:    nil,
+				}
+				edbInfoList, ok := seriesEdbMap[v.ChartSeriesId]
+				if ok {
+					tmpSeries.EdbInfoList = edbInfoList
+				}
+				seriesList = append(seriesList, tmpSeries)
+			}
+			sectionExtraConfig.SeriesList = seriesList
+
+		} else {
+			err = json.Unmarshal([]byte(extraConfigStr), &sectionExtraConfig)
+			if err != nil {
+				errMsg = "截面组合图配置异常"
+				err = errors.New(errMsg)
+				return
+			}
+		}
+
+		//校验引用日期名称是否重复
+		dateNameMap := make(map[string]int)
+		dateNameEnMap := make(map[string]int)
+		for _, v := range sectionExtraConfig.DateConfList {
+			if _, ok := dateNameMap[v.DateConfName]; ok {
+				errMsg = "截面组合图引用日期名称设置重复"
+				err = errors.New(errMsg + v.DateConfName)
+				return
+			}
+			if _, ok := dateNameEnMap[v.DateConfNameEn]; ok {
+				errMsg = "截面组合图引用日期名称设置重复"
+				err = errors.New(errMsg + v.DateConfNameEn)
+				return
+			}
+			if v.DateConfName != "" {
+				dateNameMap[v.DateConfName] = 1
+			}
+			if v.DateConfNameEn != "" {
+				dateNameEnMap[v.DateConfNameEn] = 1
+			}
+
+		}
+
+		//检查系列名称是否重复
+		seriesNameMap := make(map[string]int)
+		seriesNameEnMap := make(map[string]int)
+		for _, v := range sectionExtraConfig.SeriesList {
+			if _, ok := seriesNameMap[v.SeriesName]; ok {
+				errMsg = "截面组合图系列名称设置重复"
+				err = errors.New(errMsg + v.SeriesName)
+				return
+			}
+			if _, ok := seriesNameEnMap[v.SeriesNameEn]; ok {
+				errMsg = "截面组合图系列名称设置重复"
+				err = errors.New(errMsg + v.SeriesNameEn)
+				return
+			}
+			if v.SeriesName != "" {
+				seriesNameMap[v.SeriesName] = 1
+			}
+			if v.SeriesNameEn != "" {
+				seriesNameEnMap[v.SeriesNameEn] = 1
+			}
+		}
+
+		// 检查基准系列是否设置
+		if sectionExtraConfig.BaseChartSeriesName == "" {
+			errMsg = "截面组合图基准系列名称未设置"
+			err = errors.New(errMsg)
+			return
+		}
+		//todo 如果是详情接口,应该要从其他表里查询配置
+		extraConfig = sectionExtraConfig
 	default:
 		xEdbIdValue = make([]int, 0)
 		yDataList = make([]data_manage.YData, 0)
@@ -370,6 +535,15 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 			// 兼容无配置的老图
 			dataResp = new(data_manage.SeasonChartResp)
 		}
+	case 6: //时序组合图
+		//判断是否堆积
+		timeConf := extraConfig.(data_manage.ChartTimeCombineExtraConf)
+		if extraConfigStr == "" { //历史数据,默认开启堆积
+			timeConf = data_manage.ChartTimeCombineExtraConf{
+				IsHeap: 1,
+			}
+		}
+		dataResp = data_manage.ChartTimeCombineDataResp{IsHeap: timeConf.IsHeap}
 	case 7: // 柱形图
 		barChartConf := extraConfig.(data_manage.BarChartInfoReq)
 		xEdbIdValue, yDataList, err = BarChartData(mappingList, edbDataListMap, barChartConf.DateList, barChartConf.Sort)
@@ -406,6 +580,23 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 			return
 		}
 
+		// 这个数据没有必要返回给前端
+		for _, v := range edbList {
+			v.DataList = nil
+		}
+	case utils.CHART_TYPE_SECTION_COMBINE: // 截面组合图
+		sectionConf := extraConfig.(data_manage.ChartSectionAllExtraConf)
+		var chartInfo *data_manage.ChartInfo
+		if chartInfoId > 0 {
+			chartInfo, err = data_manage.GetChartInfoById(chartInfoId)
+			if err != nil {
+				errMsg = "获取图表信息失败"
+				return
+			}
+		}
+
+		xEdbIdValue, dataResp, err = GetChartSectionCombineData(chartInfo, mappingList, edbDataListMap, sectionConf)
+
 		// 这个数据没有必要返回给前端
 		for _, v := range edbList {
 			v.DataList = nil
@@ -1647,6 +1838,96 @@ func CheckChartExtraConfig(chartType int, extraConfigStr string) (edbIdList []in
 			err = errors.New(errMsg)
 			return
 		}
+	case utils.CHART_TYPE_SECTION_COMBINE:
+		var extraConfig data_manage.ChartSectionAllExtraConf
+		if extraConfigStr == `` {
+			errMsg = "截面组合图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &extraConfig)
+		if err != nil {
+			errMsg = "截面组合图配置异常"
+			err = errors.New(errMsg + err.Error())
+			return
+		}
+		//校验引用日期名称是否重复
+		dateNameMap := make(map[string]int)
+		dateNameEnMap := make(map[string]int)
+		for _, v := range extraConfig.DateConfList {
+			if _, ok := dateNameMap[v.DateConfName]; ok {
+				errMsg = "截面组合图引用日期名称设置重复"
+				err = errors.New(errMsg + v.DateConfName)
+				return
+			}
+			if _, ok := dateNameEnMap[v.DateConfNameEn]; ok {
+				errMsg = "截面组合图引用日期名称设置重复"
+				err = errors.New(errMsg + v.DateConfNameEn)
+				return
+			}
+			if v.DateConfName != "" {
+				dateNameMap[v.DateConfName] = 1
+			}
+			if v.DateConfNameEn != "" {
+				dateNameEnMap[v.DateConfNameEn] = 1
+			}
+
+		}
+
+		//检查系列名称是否重复
+		seriesNameMap := make(map[string]int)
+		seriesNameEnMap := make(map[string]int)
+		for _, v := range extraConfig.SeriesList {
+			if _, ok := seriesNameMap[v.SeriesName]; ok {
+				errMsg = "截面组合图系列名称设置重复"
+				err = errors.New(errMsg + v.SeriesName)
+				return
+			}
+			if _, ok := seriesNameEnMap[v.SeriesNameEn]; ok {
+				errMsg = "截面组合图系列名称设置重复"
+				err = errors.New(errMsg + v.SeriesNameEn)
+				return
+			}
+			if v.SeriesName != "" {
+				seriesNameMap[v.SeriesName] = 1
+			}
+			if v.SeriesNameEn != "" {
+				seriesNameEnMap[v.SeriesNameEn] = 1
+			}
+		}
+
+		// 检查基准系列是否设置
+		if extraConfig.BaseChartSeriesName == "" {
+			errMsg = "截面组合图基准系列名称未设置"
+			err = errors.New(errMsg)
+			return
+		}
+		//todo 如果是详情接口,应该要从其他表里查询配置
+
+		// 遍历指标列表获取指标id
+		edbIdMap := make(map[int]int)
+		hasUsed := make(map[string]struct{})
+		for _, v := range extraConfig.SeriesList {
+			for _, item := range v.EdbInfoList {
+				if item.DateConfName != "" && item.DateConfType == 1 {
+					hasUsed[item.DateConfName] = struct{}{}
+				}
+				if _, ok := edbIdMap[item.EdbInfoId]; !ok {
+					edbIdMap[item.EdbInfoId] = item.EdbInfoId
+					edbIdList = append(edbIdList, item.EdbInfoId)
+				}
+			}
+		}
+		// todo 遍历引用日期获取引用日期里的指标ID, 如果引用日期里的指标没有被引用,是否加入映射
+		for _, v := range extraConfig.DateConfList {
+			if _, ok := hasUsed[v.DateConfName]; !ok {
+				continue
+			}
+			if _, ok := edbIdMap[v.EdbInfoId]; !ok && v.EdbInfoId > 0 {
+				edbIdMap[v.EdbInfoId] = v.EdbInfoId
+				edbIdList = append(edbIdList, v.EdbInfoId)
+			}
+		}
 	}
 	return
 }
@@ -2152,7 +2433,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 	}
 
 	// 图表额外配置
-	extraConfig, err, errMsg = HandleExtraConfig(chartType, extraConfig)
+	var allExtraConfig string
+	extraConfig, allExtraConfig, err, errMsg = HandleExtraConfig(chartType, extraConfig)
 	if err != nil {
 		if errMsg == `` {
 			errMsg = "指标异常!"
@@ -2291,6 +2573,7 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 	chartInfo.MarkersAreas = req.MarkersAreas
 	chartInfo.Unit = req.Unit
 	chartInfo.UnitEn = req.UnitEn
+	chartInfo.ChartAlias = req.ChartAlias
 	newId, err := data_manage.AddChartInfo(chartInfo)
 	if err != nil {
 		errMsg = `保存失败`
@@ -2334,8 +2617,18 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 		err = errors.New("保存失败,Err:" + err.Error())
 		return
 	}
+
 	// 添加指标引用记录
 	_ = SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
+
+	if chartType == utils.CHART_TYPE_SECTION_COMBINE {
+		err = data_manage.AddChartSeriesAndEdbMapping(allExtraConfig, int(newId))
+		if err != nil {
+			errMsg = `保存失败`
+			err = errors.New("保存失败,Err:" + err.Error())
+			return
+		}
+	}
 	//添加es数据
 	go EsAddOrEditChartInfo(chartInfo.ChartInfoId)
 
@@ -2421,7 +2714,7 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang
 	// 关联指标
 	var edbInfoIdArr []int
 	// 非散点截面图的直接通过前端获取,散点截面图需要额外从配置中获取
-	if chartItem.ChartType != 10 {
+	if chartItem.ChartType != 10 && chartItem.ChartType != utils.CHART_TYPE_SECTION_COMBINE {
 		//edbList := make([]*data_manage.EdbInfo, 0)
 		for _, v := range req.ChartEdbInfoList {
 			edbInfoId := v.EdbInfoId
@@ -2537,7 +2830,7 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang
 	}
 
 	// 图表额外配置
-	extraConfig, err, errMsg := HandleExtraConfig(req.ChartType, req.ExtraConfig)
+	extraConfig, allExtraConfig, err, errMsg := HandleExtraConfig(req.ChartType, req.ExtraConfig)
 	if err != nil {
 		if errMsg == `` {
 			errMsg = "指标异常!"
@@ -2689,6 +2982,14 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang
 
 	// todo 添加指标引用记录
 	_ = SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
+	if chartItem.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+		err = data_manage.EditChartSeriesAndEdbMapping(allExtraConfig, chartItem.ChartInfoId)
+		if err != nil {
+			errMsg = "保存失败"
+			err = errors.New("保存失败,Err:" + err.Error())
+			return
+		}
+	}
 	//添加es数据
 	go EsAddOrEditChartInfo(chartItem.ChartInfoId)
 	//修改my eta es数据
@@ -3235,6 +3536,18 @@ func GetChartEdbDataV2(chartInfoId, chartType int, calendar, startDate, endDate
 
 	var extraConfig interface{}
 	switch chartType {
+	case 6:
+		var tmpConfig data_manage.ChartTimeCombineExtraConf
+		if extraConfigStr != `` {
+			err = json.Unmarshal([]byte(extraConfigStr), &tmpConfig)
+			if err != nil {
+				errMsg = "雷达图配置异常"
+				err = errors.New(errMsg)
+				return
+			}
+		}
+
+		extraConfig = tmpConfig
 	case 7: // 柱形图
 		var barConfig data_manage.BarChartInfoReq
 		if extraConfigStr == `` {
@@ -3278,6 +3591,159 @@ func GetChartEdbDataV2(chartInfoId, chartType int, calendar, startDate, endDate
 			return
 		}
 		extraConfig = barConfig
+	case utils.CHART_TYPE_SECTION_COMBINE:
+		// 预览和详情都走这个接口
+		var sectionExtraConfig data_manage.ChartSectionAllExtraConf
+		if extraConfigStr == `` {
+			errMsg = "截面组合图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		if chartInfoId > 0 {
+			var sectionExtraConfigTmp data_manage.ChartSectionExtraConf
+			err = json.Unmarshal([]byte(extraConfigStr), &sectionExtraConfigTmp)
+			if err != nil {
+				errMsg = "截面组合图配置异常"
+				err = errors.New(errMsg)
+				return
+			}
+			sectionExtraConfig.XDataList = sectionExtraConfigTmp.XDataList
+			sectionExtraConfig.BaseChartSeriesName = sectionExtraConfigTmp.BaseChartSeriesName
+			sectionExtraConfig.UnitList = sectionExtraConfigTmp.UnitList
+			sectionExtraConfig.DateConfList = sectionExtraConfigTmp.DateConfList
+			sectionExtraConfig.IsHeap = sectionExtraConfigTmp.IsHeap
+			sectionExtraConfig.SortType = sectionExtraConfigTmp.SortType
+			// 查询所有的seriesEdb
+			seriesEdbList, e := data_manage.GetChartSeriesEdbByChartInfoId(chartInfoId)
+			if e != nil {
+				errMsg = "查询seriesEdb失败"
+				err = errors.New(errMsg + e.Error())
+				return
+			}
+			// todo 是否有必要返回单位信息
+			// 组装成map
+			seriesEdbMap := make(map[int][]*data_manage.ChartSectionSeriesEdbConf)
+			for _, v := range seriesEdbList {
+				var dateConf *data_manage.ChartSectionSeriesDateConfItem
+				if v.DateConf != "" {
+					err = json.Unmarshal([]byte(v.DateConf), &dateConf)
+					if err != nil {
+						errMsg = "截面组合图配置异常"
+						err = errors.New(errMsg + e.Error())
+						return
+					}
+				}
+				tmp := &data_manage.ChartSectionSeriesEdbConf{
+					ChartSeriesEdbMappingId: v.ChartSeriesEdbMappingId,
+					ChartSeriesId:           v.ChartSeriesId,
+					//ChartInfoId:             v.ChartInfoId,
+					EdbInfoId:    v.EdbInfoId,
+					DateConf:     dateConf,
+					EdbName:      "",
+					EdbNameEn:    "",
+					Unit:         "",
+					UnitEn:       "",
+					DateConfName: v.DateConfName,
+					DateConfType: v.DateConfType,
+				}
+				seriesEdbMap[v.ChartSeriesId] = append(seriesEdbMap[v.ChartSeriesId], tmp)
+			}
+			//查询series
+			seriesListTmp, e := data_manage.GetChartSeriesByChartInfoId(chartInfoId)
+			if e != nil {
+				errMsg = "查询series失败"
+				err = errors.New(errMsg + e.Error())
+				return
+			}
+			seriesList := make([]*data_manage.ChartSectionSeriesItem, 0)
+			for _, v := range seriesListTmp {
+
+				tmpSeries := &data_manage.ChartSectionSeriesItem{
+					ChartSeriesId: v.ChartSeriesId,
+					SeriesName:    v.SeriesName,
+					SeriesNameEn:  v.SeriesNameEn,
+					ChartStyle:    v.ChartStyle,
+					ChartColor:    v.ChartColor,
+					ChartWidth:    v.ChartWidth,
+					IsPoint:       v.IsPoint,
+					IsNumber:      v.IsNumber,
+					IsAxis:        v.IsAxis,
+					MaxData:       v.MaxData,
+					MinData:       v.MinData,
+					//IsOrder:         false,
+					EdbInfoList: nil,
+					DataList:    nil,
+				}
+				edbInfoList, ok := seriesEdbMap[v.ChartSeriesId]
+				if ok {
+					tmpSeries.EdbInfoList = edbInfoList
+				}
+				seriesList = append(seriesList, tmpSeries)
+			}
+			sectionExtraConfig.SeriesList = seriesList
+
+		} else {
+			err = json.Unmarshal([]byte(extraConfigStr), &sectionExtraConfig)
+			if err != nil {
+				errMsg = "截面组合图配置异常"
+				err = errors.New(errMsg)
+				return
+			}
+		}
+
+		//校验引用日期名称是否重复
+		dateNameMap := make(map[string]int)
+		dateNameEnMap := make(map[string]int)
+		for _, v := range sectionExtraConfig.DateConfList {
+			if _, ok := dateNameMap[v.DateConfName]; ok {
+				errMsg = "截面组合图引用日期名称设置重复"
+				err = errors.New(errMsg + v.DateConfName)
+				return
+			}
+			if _, ok := dateNameEnMap[v.DateConfNameEn]; ok {
+				errMsg = "截面组合图引用日期名称设置重复"
+				err = errors.New(errMsg + v.DateConfNameEn)
+				return
+			}
+			if v.DateConfName != "" {
+				dateNameMap[v.DateConfName] = 1
+			}
+			if v.DateConfNameEn != "" {
+				dateNameEnMap[v.DateConfNameEn] = 1
+			}
+
+		}
+
+		//检查系列名称是否重复
+		seriesNameMap := make(map[string]int)
+		seriesNameEnMap := make(map[string]int)
+		for _, v := range sectionExtraConfig.SeriesList {
+			if _, ok := seriesNameMap[v.SeriesName]; ok {
+				errMsg = "截面组合图系列名称设置重复"
+				err = errors.New(errMsg + v.SeriesName)
+				return
+			}
+			if _, ok := seriesNameEnMap[v.SeriesNameEn]; ok {
+				errMsg = "截面组合图系列名称设置重复"
+				err = errors.New(errMsg + v.SeriesNameEn)
+				return
+			}
+			if v.SeriesName != "" {
+				seriesNameMap[v.SeriesName] = 1
+			}
+			if v.SeriesNameEn != "" {
+				seriesNameEnMap[v.SeriesNameEn] = 1
+			}
+		}
+
+		// 检查基准系列是否设置
+		if sectionExtraConfig.BaseChartSeriesName == "" {
+			errMsg = "截面组合图基准系列名称未设置"
+			err = errors.New(errMsg)
+			return
+		}
+		//todo 如果是详情接口,应该要从其他表里查询配置
+		extraConfig = sectionExtraConfig
 	default:
 		xEdbIdValue = make([]int, 0)
 		yDataList = make([]data_manage.YData, 0)
@@ -3304,6 +3770,15 @@ func GetChartEdbDataV2(chartInfoId, chartType int, calendar, startDate, endDate
 			// 兼容无配置的老图
 			dataResp = new(data_manage.SeasonChartResp)
 		}
+	case 6: //时序组合图
+		//判断是否堆积
+		timeConf := extraConfig.(data_manage.ChartTimeCombineExtraConf)
+		if extraConfigStr == "" { //历史数据,默认开启堆积
+			timeConf = data_manage.ChartTimeCombineExtraConf{
+				IsHeap: 1,
+			}
+		}
+		dataResp = data_manage.ChartTimeCombineDataResp{IsHeap: timeConf.IsHeap}
 	case 7: // 柱形图
 		barChartConf := extraConfig.(data_manage.BarChartInfoReq)
 		xEdbIdValue, yDataList, err = BarChartData(mappingList, edbDataListMap, barChartConf.DateList, barChartConf.Sort)
@@ -3340,6 +3815,22 @@ func GetChartEdbDataV2(chartInfoId, chartType int, calendar, startDate, endDate
 			return
 		}
 
+		// 这个数据没有必要返回给前端
+		for _, v := range edbList {
+			v.DataList = nil
+		}
+	case utils.CHART_TYPE_SECTION_COMBINE: // 截面组合图
+		sectionConf := extraConfig.(data_manage.ChartSectionAllExtraConf)
+		var chartInfo *data_manage.ChartInfo
+		if chartInfoId > 0 {
+			chartInfo, err = data_manage.GetChartInfoById(chartInfoId)
+			if err != nil {
+				errMsg = "获取图表信息失败"
+				return
+			}
+		}
+		xEdbIdValue, dataResp, err = GetChartSectionCombineData(chartInfo, mappingList, edbDataListMap, sectionConf)
+
 		// 这个数据没有必要返回给前端
 		for _, v := range edbList {
 			v.DataList = nil

+ 2 - 2
services/data/chart_info_excel_balance.go

@@ -177,7 +177,7 @@ func addBalanceExcelChart(req request.AddBalanceTableChartReq, sysUserId int, sy
 	}
 
 	// 图表额外配置
-	extraConfig, err, errMsg = HandleExtraConfig(chartType, extraConfig)
+	extraConfig, _, err, errMsg = HandleExtraConfig(chartType, extraConfig)
 	if err != nil {
 		if errMsg == `` {
 			errMsg = "指标异常!"
@@ -480,7 +480,7 @@ func editBalanceExcelChart(req request.AddBalanceTableChartReq) (chartInfo *data
 	}*/
 
 	// 图表额外配置
-	extraConfig, err, errMsg = HandleExtraConfig(chartType, extraConfig)
+	extraConfig, _, err, errMsg = HandleExtraConfig(chartType, extraConfig)
 	if err != nil {
 		if errMsg == `` {
 			errMsg = "指标异常!"

+ 37 - 0
services/data/chart_theme.go

@@ -128,6 +128,43 @@ func GetThemePreviewChartEdbData(chartType int, calendar, startDate, endDate str
 	case utils.CHART_TYPE_RADAR: //雷达图
 		radarConf := extraConfig.(data_manage.RadarChartInfoReq)
 		xEdbIdValue, dataResp, err = RadarChartData(mappingList, edbDataListMap, radarConf)
+	case utils.CHART_TYPE_SECTION_COMBINE: // 截面组合图
+		var sectionConf data_manage.ChartSectionAllExtraConf
+		if extraConfigStr == `` {
+			errMsg = "截面散点图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &sectionConf)
+		if err != nil {
+			errMsg = "截面散点配置异常"
+			err = errors.New(errMsg + err.Error())
+			return
+		}
+
+		// edbIdList 指标展示顺序;x轴的指标顺序
+		xEdbIdValue = make([]int, 0)
+		for _, v := range mappingList {
+			xEdbIdValue = append(xEdbIdValue, v.EdbInfoId)
+		}
+		dataResp = &data_manage.ChartSectionCombineDataResp{
+			DateConfList:        sectionConf.DateConfList,
+			IsHeap:              sectionConf.IsHeap,
+			XDataList:           sectionConf.XDataList,
+			UnitList:            sectionConf.UnitList,
+			BaseChartSeriesName: sectionConf.BaseChartSeriesName,
+			SortType:            sectionConf.SortType,
+			SeriesList:          sectionConf.SeriesList,
+			LeftMin:             "0",
+			LeftMax:             "4000",
+			RightMin:            "10",
+			RightMax:            "15",
+		}
+
+		// 这个数据没有必要返回给前端
+		for _, v := range edbList {
+			v.DataList = nil
+		}
 	}
 	return
 }

+ 1 - 1
services/data/data_manage_permission/data_move.go

@@ -230,7 +230,7 @@ func GetMoveEdbChartList(source, subSource, userId int, keyword, classify string
 		if err != nil {
 			return
 		}
-		tmpList, tmpErr := models.GetEdbinfoList(condition, pars, startSize, pageSize, "", 0)
+		tmpList, tmpErr := models.GetEdbinfoItemList(condition, pars, startSize, pageSize, "", 0)
 		if tmpErr != nil {
 			err = tmpErr
 			return

+ 2 - 8
services/data/edb_classify.go

@@ -7,7 +7,6 @@ import (
 	"eta/eta_api/models/data_manage/cross_variety"
 	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/models/system"
-	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/data_stat"
 	"eta/eta_api/utils"
@@ -919,13 +918,8 @@ func Delete(classifyId, edbInfoId int, sysUser *system.Admin, requestBody, reque
 
 		go data_stat.AddEdbDeleteLog(edbInfo, sysUser)
 
-		// 如果删除的指标是自定义分析的来源,那么还需要删除指标与excel的关系
-		if edbInfo.Source == utils.DATA_SOURCE_CALCULATE_ZDYFX {
-			tmpErr = excel.DeleteCustomAnalysisExcelEdbMappingByEdbInfoId(edbInfo.EdbInfoId)
-			if tmpErr != nil {
-				alarm_msg.SendAlarmMsg(fmt.Sprintf("删除指标时,需要删除与自定义分析的关系失败,指标ID:%d,Err:%s", edbInfo.EdbInfoId, tmpErr.Error()), 3)
-			}
-		}
+		// 删除指标后的操作
+		go handleByDelEdbInfo(edbInfo)
 
 		// 返回下一个表格的信息
 		{

+ 41 - 5
services/data/edb_info.go

@@ -6,6 +6,7 @@ import (
 	"eta/eta_api/cache"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/data_manage/excel"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/services/elastic"
@@ -1987,11 +1988,7 @@ func EdbInfoAdd(source, subSource, classifyId int, edbCode, edbName, frequency,
 	//添加es
 	AddOrEditEdbInfoToEs(int(edbInfoId))
 
-	// 更新钢联化工状态为启用
-	if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
-		// 启动钢联的刷新
-		_ = data_manage.UpdateMysteelChemicalRefreshStatus(edbCode, 0)
-	}
+	go handleByAddEdbInfo(edbInfo)
 
 	return
 }
@@ -3005,3 +3002,42 @@ func GetEdbTerminalCodeBySource(source int, edbCode, stockCode string) (terminal
 	}
 	return
 }
+
+// handleByAddEdbInfo
+// @Description: 添加指标后的处理操作
+// @author: Roc
+// @datetime 2024-07-22 13:06:36
+// @param edbInfo *data_manage.EdbInfo
+func handleByAddEdbInfo(edbInfo *data_manage.EdbInfo) {
+	// 更新钢联化工状态为启用
+	if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+		// 启动钢联的刷新
+		_ = data_manage.UpdateMysteelChemicalRefreshStatus(edbInfo.EdbCode, 0)
+	}
+
+	// 如果是手工数据,那么需要标记手工数据为已加入指标库
+	if edbInfo.Source == utils.DATA_SOURCE_MANUAL {
+		_ = models.UpdateManualIsJoinEdbStatus(edbInfo.EdbCode, 1)
+	}
+
+}
+
+// handleByDelEdbInfo
+// @Description: 删除指标后的处理操作
+// @author: Roc
+// @datetime 2024-07-22 13:06:36
+// @param edbInfo *data_manage.EdbInfo
+func handleByDelEdbInfo(edbInfo *data_manage.EdbInfo) {
+	// 如果删除的指标是自定义分析的来源,那么还需要删除指标与excel的关系
+	if edbInfo.Source == utils.DATA_SOURCE_CALCULATE_ZDYFX {
+		err := excel.DeleteCustomAnalysisExcelEdbMappingByEdbInfoId(edbInfo.EdbInfoId)
+		if err != nil {
+			utils.FileLog.Error(fmt.Sprintf("删除指标时,需要删除与自定义分析的关系失败,指标ID:%d,Err:%s", edbInfo.EdbInfoId, err.Error()), 3)
+		}
+	}
+
+	// 如果是手工数据,那么需要标记手工数据为未加入指标库
+	if edbInfo.Source == utils.DATA_SOURCE_MANUAL {
+		_ = models.UpdateManualIsJoinEdbStatus(edbInfo.EdbCode, 0)
+	}
+}

+ 0 - 287
services/data/edb_info_calculate.go

@@ -63,293 +63,6 @@ type CalculateItems struct {
 	DataMap   map[string]float64
 }
 
-func ReplaceFormula(edbInfoIdArr []*data_manage.EdbInfo, valArr map[int]float64, formulaMap map[string]string, formulaStr string, edbInfoIdBytes []string) string {
-	// todo 处理min和max
-	funMap := GetFormulaMap()
-	for k, v := range funMap {
-		formulaStr = strings.Replace(formulaStr, k, v, -1)
-	}
-	replaceCount := 0
-	for dk, dv := range edbInfoIdArr {
-		if dk == 0 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				} else {
-					fmt.Println("valArr not found:", valArr, valOk)
-				}
-			} else {
-				fmt.Println("formulaMap not found:", dKey, dk)
-			}
-		}
-		if dk == 1 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				} else {
-					fmt.Println("valArr not found:", valArr, valOk)
-				}
-			} else {
-				fmt.Println("formulaMap not found:", dKey, dk)
-			}
-		}
-		if dk == 2 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 3 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 4 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 5 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 6 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 7 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 8 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 9 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 10 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 11 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 12 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 13 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 14 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 15 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 16 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 17 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 18 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 19 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 20 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 21 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 22 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 23 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 24 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-		if dk == 25 {
-			dKey := edbInfoIdBytes[dk]
-			if _, ok := formulaMap[dKey]; ok { //公式中存在
-				if val, valOk := valArr[dv.EdbInfoId]; valOk { //值存在
-					dvStr := fmt.Sprintf("%v", val)
-					formulaStr = strings.Replace(formulaStr, dKey, dvStr, -1)
-					replaceCount++
-				}
-			}
-		}
-	}
-	for k, v := range funMap {
-		formulaStr = strings.Replace(formulaStr, v, k, -1)
-	}
-	if replaceCount == len(formulaMap) {
-		return formulaStr
-	} else {
-		return ""
-	}
-}
-
 func GetFormulaMap() map[string]string {
 	funMap := make(map[string]string)
 	funMap["MAX"] = "[@@]"

+ 30 - 0
services/data/edb_info_record.go

@@ -0,0 +1,30 @@
+package data
+
+import (
+	"eta/eta_api/models/data_manage"
+	"time"
+)
+
+func AddEditEdbInfoRcord(oldEdbInfo *data_manage.EdbInfo, newEdbInfo *data_manage.EdbInfoEditRecord) (err error) {
+	if oldEdbInfo.EdbName != newEdbInfo.EdbName || oldEdbInfo.Frequency != newEdbInfo.Frequency || oldEdbInfo.Unit != newEdbInfo.Unit {
+		ctime := time.Now()
+		edbRecord := new(data_manage.EdbInfoRecord)
+		edbRecord.EdbInfoId = oldEdbInfo.EdbInfoId
+		edbRecord.OldEdbName = oldEdbInfo.EdbName
+		edbRecord.OldFrequency = oldEdbInfo.Frequency
+		edbRecord.OldUnit = oldEdbInfo.Unit
+		edbRecord.NewEdbName = newEdbInfo.EdbName
+		edbRecord.NewFrequency = newEdbInfo.Frequency
+		edbRecord.NewUnit = newEdbInfo.Unit
+		edbRecord.OperateUserId = newEdbInfo.OperateUserId
+		edbRecord.OperateUserRealName = newEdbInfo.OperateUserRealName
+		edbRecord.CreateTime = ctime
+		edbRecord.Timestamp = ctime.Unix()
+		err = data_manage.AddEditEdbInfoRcord(edbRecord)
+		if err != nil {
+			return
+		}
+		err = data_manage.ModifyEdbInfoBaseTimeById(edbRecord.EdbInfoId, ctime)
+	}
+	return
+}

+ 4 - 3
services/data/excel/balance_table.go

@@ -119,8 +119,9 @@ func GetBalanceExcelEdbData(excelEdbMappingItem *excelModel.ExcelChartEdb, newMi
 	var dateList, dataList []string
 	// 日期序列
 	{
-		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DateSequence)
+		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr, tmpErrMsg := GetSheetStr(excelEdbMappingItem.DateSequence)
 		if tmpErr != nil {
+			errMsg = tmpErrMsg
 			err = tmpErr
 			return
 		}
@@ -218,8 +219,9 @@ func GetBalanceExcelEdbData(excelEdbMappingItem *excelModel.ExcelChartEdb, newMi
 
 	// 数据序列
 	{
-		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DataSequence)
+		_, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr, tmpErrMsg := GetSheetStr(excelEdbMappingItem.DataSequence)
 		if tmpErr != nil {
+			errMsg = tmpErrMsg
 			err = tmpErr
 			return
 		}
@@ -475,7 +477,6 @@ func TransferBalanceExcelContentToStatic(excelDetail *excelModel.ExcelInfo, lang
 		return
 	}
 	cellRelationConf = string(cellRelationConfByte)
-
 	newData, tmpErr, tmpErrMsg := GetMixedTableCellData(mixedTableReq, lang)
 	if tmpErr != nil {
 		err = errors.New(tmpErrMsg + "获取最新的数据失败 ,Err:" + tmpErr.Error())

+ 30 - 11
services/data/excel/custom_analysis_edb.go

@@ -265,8 +265,18 @@ func Refresh(excelInfo *excel.ExcelInfo, lang string) (err error, errMsg string,
 		// 获取对应的日期和数据列表
 		relDateList, relDataList, tmpErr, tmpErrMsg := getDateAndDataList(v, xlsxFile)
 		if tmpErr != nil {
-			err = errors.New(fmt.Sprintf("获取对应的日期和数据列表,Err:%s", tmpErr.Error()))
-			errMsg = tmpErrMsg
+			err = errors.New(fmt.Sprintf("《%s》获取对应的日期和数据列表,Err:%s", v.EdbName, tmpErr.Error()))
+			errMsg = fmt.Sprintf("《%s》%s", v.EdbName, tmpErrMsg)
+			return
+		}
+		if len(relDateList) <= 0 {
+			errMsg = fmt.Sprintf("《%s》的日期序列为空", v.EdbName)
+			err = errors.New(errMsg)
+			return
+		}
+		if len(relDataList) <= 0 {
+			errMsg = fmt.Sprintf("《%s》的数据序列为空", v.EdbName)
+			err = errors.New(errMsg)
 			return
 		}
 		//fmt.Println(v)
@@ -318,8 +328,9 @@ func getDateAndDataList(excelEdbMappingItem *excel.ExcelEdbMappingItem, xlsxFile
 
 	// 日期序列
 	{
-		sheetName, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DateSequenceStr)
+		sheetName, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr, tmpErrMsg := GetSheetStr(excelEdbMappingItem.DateSequenceStr)
 		if tmpErr != nil {
+			errMsg = tmpErrMsg
 			err = tmpErr
 			return
 		}
@@ -423,8 +434,9 @@ func getDateAndDataList(excelEdbMappingItem *excel.ExcelEdbMappingItem, xlsxFile
 
 	// 数据序列
 	{
-		sheetName, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr := GetSheetStr(excelEdbMappingItem.DataSequenceStr)
+		sheetName, startColumnName, endColumnName, startNum, endNum, isAll, isRow, isColumn, tmpErr, tmpErrMsg := GetSheetStr(excelEdbMappingItem.DataSequenceStr)
 		if tmpErr != nil {
+			errMsg = tmpErrMsg
 			err = tmpErr
 			return
 		}
@@ -541,11 +553,14 @@ func getDateAndDataList(excelEdbMappingItem *excel.ExcelEdbMappingItem, xlsxFile
 // @return isAll bool 是否选择整行/列数据
 // @return isRow bool 是否选择行数据
 // @return isColumn bool 是否选择列数据
-func GetSheetStr(sequenceStr string) (sheetName, startColumnName, endColumnName string, startNum, endNum int, isAll, isRow, isColumn bool, err error) {
+// @return err error 错误信息
+// @return errMsg string 错误信息
+func GetSheetStr(sequenceStr string) (sheetName, startColumnName, endColumnName string, startNum, endNum int, isAll, isRow, isColumn bool, err error, errMsg string) {
 	// 找出sheetName
 	tmpList := strings.Split(sequenceStr, "!")
 	if len(tmpList) != 2 {
-		err = errors.New("错误的公式,查找sheet异常:" + sequenceStr)
+		errMsg = "错误的公式,查找sheet异常:" + sequenceStr
+		err = errors.New(errMsg)
 		return
 	}
 
@@ -554,7 +569,8 @@ func GetSheetStr(sequenceStr string) (sheetName, startColumnName, endColumnName
 	// 分离开始/结束单元格
 	tmpList = strings.Split(tmpList[1], ":")
 	if len(tmpList) != 2 {
-		err = errors.New("错误的公式,查找开始/结束单元格异常:" + sequenceStr)
+		errMsg = "错误的公式,查找开始/结束单元格异常:" + sequenceStr
+		err = errors.New(errMsg)
 		return
 	}
 
@@ -563,12 +579,14 @@ func GetSheetStr(sequenceStr string) (sheetName, startColumnName, endColumnName
 
 	lenList := len(startList)
 	if lenList != len(endList) {
-		err = errors.New("错误的公式,开始与结束单元格异常:" + sequenceStr)
+		errMsg = "错误的公式,开始与结束单元格异常:" + sequenceStr
+		err = errors.New(errMsg)
 		return
 	}
 
 	if lenList != 3 && lenList != 2 {
-		err = errors.New("错误的公式:" + sequenceStr)
+		errMsg = "错误的公式:" + sequenceStr
+		err = errors.New(errMsg)
 		return
 	}
 
@@ -606,7 +624,7 @@ func GetSheetStr(sequenceStr string) (sheetName, startColumnName, endColumnName
 	// 确定行
 	startDeci, tmpErr1 := decimal.NewFromString(startList[2])
 	endDeci, tmpErr2 := decimal.NewFromString(endList[2])
-	if tmpErr1 != nil && tmpErr1 != tmpErr2 {
+	if tmpErr1 != nil && errors.Is(tmpErr1, tmpErr2) {
 		err = errors.New("错误的公式3:" + sequenceStr)
 		return
 	}
@@ -615,7 +633,8 @@ func GetSheetStr(sequenceStr string) (sheetName, startColumnName, endColumnName
 	endNum = int(endDeci.IntPart())
 
 	if startColumnName != endColumnName && startNum != endNum {
-		err = errors.New("选区不允许跨行或者跨列")
+		errMsg = `选区不允许跨行或者跨列`
+		err = errors.New(errMsg)
 	}
 
 	if startColumnName == endColumnName {

+ 33 - 2
services/data/excel/excel_info.go

@@ -531,7 +531,7 @@ func GetExcelEdbBatchRefreshKey(source string, primaryId, subId int) string {
 }
 
 // GetEdbSourceByEdbInfoIdList 获取关联指标的来源
-func GetEdbSourceByEdbInfoIdList(edbInfoIdList []int) (sourceNameList, sourceNameEnList []string,err error) {
+func GetEdbSourceByEdbInfoIdList(edbInfoIdList []int) (sourceNameList, sourceNameEnList []string, err error) {
 	sourceNameList = make([]string, 0)
 	sourceNameEnList = make([]string, 0)
 	sourceMap := make(map[int]string)
@@ -580,4 +580,35 @@ func GetEdbSourceByEdbInfoIdList(edbInfoIdList []int) (sourceNameList, sourceNam
 		sourceNameEnList = append(sourceNameEnList, conf[models.BusinessConfCompanyName])
 	}
 	return
-}
+}
+
+// GetCustomAnalysisOpButton 获取自定义分析按钮权限
+func GetCustomAnalysisOpButton(sysUser *system.Admin, belongUserId int, permissionType []int) (button excel.ExcelInfoDetailButton) {
+	// 管理员/所属人所有权限
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN || sysUser.AdminId == belongUserId {
+		button.RefreshButton = true
+		button.CopyButton = true
+		button.DownloadButton = true
+
+		button.OpEdbButton = true
+		button.RefreshEdbButton = true
+		button.OpButton = true
+		button.DeleteButton = true
+		return
+	}
+
+	// 非管理员-被分享人
+	for _, v := range permissionType {
+		if v == 1 {
+			button.RefreshButton = true
+			button.CopyButton = true
+			button.DownloadButton = true
+		}
+		if v == 2 {
+			button.OpEdbButton = true
+			button.RefreshEdbButton = true
+			button.OpButton = true
+		}
+	}
+	return
+}

+ 60 - 45
services/data/excel/mixed_table.go

@@ -8,8 +8,8 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/dengsgo/math-engine/engine"
 	"github.com/shopspring/decimal"
-	"github.com/yidane/formula"
 	"sort"
 	"strconv"
 	"strings"
@@ -1011,31 +1011,38 @@ func calculateByCellList(calculateFormula string, tagList []utils.CellPosition)
 		return
 	}
 
-	expression := formula.NewExpression(formulaFormStr)
-	calResult, err := expression.Evaluate()
+	//expression := formula.NewExpression(formulaFormStr)
+	//calResult, err := expression.Evaluate()
+	//if err != nil {
+	//	errMsg = "计算失败"
+	//	err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+	//	// 分母为0的报错
+	//	if strings.Contains(err.Error(), "divide by zero") {
+	//		errMsg = "分母不能为0"
+	//		err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
+	//	}
+	//	return
+	//}
+	//// 如果计算结果是NAN,那么就提示报错
+	//if calResult.IsNan() {
+	//	errMsg = "计算失败"
+	//	err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
+	//	return
+	//}
+	//calVal, err = calResult.Float64()
+	//if err != nil {
+	//	return
+	//}
+
+	calValFloat, err := engine.ParseAndExec(formulaFormStr)
+	//calVal, err := calResult.Float64()
 	if err != nil {
-		errMsg = "计算失败"
-		err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
-		// 分母为0的报错
-		if strings.Contains(err.Error(), "divide by zero") {
-			errMsg = "分母不能为0"
-			err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
-		}
-		return
-	}
-	// 如果计算结果是NAN,那么就提示报错
-	if calResult.IsNan() {
-		errMsg = "计算失败"
-		err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
-		return
+		err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+		fmt.Println(err)
+		return 0, "", err
 	}
-	calVal, err = calResult.Float64()
-	if err != nil {
-		return
-	}
-
 	// 转Decimal然后四舍五入
-	calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
+	calVal, _ = decimal.NewFromFloat(calValFloat).Round(4).Float64()
 
 	return
 }
@@ -1138,32 +1145,40 @@ func DateCalculateFormula(valTagMap map[string]int, calculateFormula string) (ca
 		err = errors.New(errMsg)
 		return
 	}
-	fmt.Println("公式:" + formulaFormStr)
-	expression := formula.NewExpression(formulaFormStr)
-	calResult, err := expression.Evaluate()
+	//fmt.Println("公式:" + formulaFormStr)
+	//expression := formula.NewExpression(formulaFormStr)
+	//calResult, err := expression.Evaluate()
+	//if err != nil {
+	//	errMsg = "公式错误,请重新填写"
+	//	err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+	//	// 分母为0的报错
+	//	if strings.Contains(err.Error(), "divide by zero") {
+	//		errMsg = "分母不能为0"
+	//		err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
+	//	}
+	//	return
+	//}
+	//// 如果计算结果是NAN,那么就提示报错
+	//if calResult.IsNan() {
+	//	errMsg = "公式错误,请重新填写"
+	//	err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
+	//	return
+	//}
+	//calVal, err = calResult.Float64()
+	//if err != nil {
+	//	return
+	//}
+
+	calValFloat, err := engine.ParseAndExec(formulaFormStr)
+	//calVal, err := calResult.Float64()
 	if err != nil {
-		errMsg = "公式错误,请重新填写"
-		err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
-		// 分母为0的报错
-		if strings.Contains(err.Error(), "divide by zero") {
-			errMsg = "分母不能为0"
-			err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
-		}
-		return
-	}
-	// 如果计算结果是NAN,那么就提示报错
-	if calResult.IsNan() {
-		errMsg = "公式错误,请重新填写"
-		err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
-		return
-	}
-	calVal, err = calResult.Float64()
-	if err != nil {
-		return
+		err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+		fmt.Println(err)
+		return 0, "", err
 	}
 
 	// 转Decimal然后四舍五入
-	calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
+	calVal, _ = decimal.NewFromFloat(calValFloat).Round(4).Float64()
 
 	return
 }

+ 29 - 18
services/data/excel/time_table.go

@@ -8,11 +8,12 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/shopspring/decimal"
-	"github.com/yidane/formula"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/dengsgo/math-engine/engine"
 )
 
 // TableDataConfig
@@ -899,25 +900,35 @@ func calculate(calculateFormula string, TagMap map[string]float64) (calVal, errM
 		return
 	}
 
-	expression := formula.NewExpression(formulaFormStr)
-	calResult, err := expression.Evaluate()
+	//expression := formula.NewExpression(formulaFormStr)
+	//calResult, err := expression.Evaluate()
+	//if err != nil {
+	//	errMsg = "计算失败"
+	//	err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+	//	// 分母为0的报错
+	//	if strings.Contains(err.Error(), "divide by zero") {
+	//		errMsg = "分母不能为0"
+	//		err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
+	//	}
+	//	return
+	//}
+	//// 如果计算结果是NAN,那么就提示报错
+	//if calResult.IsNan() {
+	//	errMsg = "计算失败"
+	//	err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
+	//	return
+	//}
+	//calVal = calResult.String()
+
+	calValFloat, err := engine.ParseAndExec(formulaFormStr)
+	//calVal, err := calResult.Float64()
 	if err != nil {
-		errMsg = "计算失败"
-		err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
-		// 分母为0的报错
-		if strings.Contains(err.Error(), "divide by zero") {
-			errMsg = "分母不能为0"
-			err = errors.New("分母不能为空,计算公式:" + formulaFormStr)
-		}
-		return
-	}
-	// 如果计算结果是NAN,那么就提示报错
-	if calResult.IsNan() {
-		errMsg = "计算失败"
-		err = errors.New("计算失败:计算结果是:NAN;formulaStr:" + formulaFormStr)
-		return
+		err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
+		fmt.Println(err)
+		return "", "", err
 	}
-	calVal = calResult.String()
+	calVal = utils.SubFloatToString(calValFloat, 2)
+	//nanCheck := fmt.Sprintf("%0.f", calVal)
 
 	// 转Decimal然后四舍五入
 	valDecimal, err := decimal.NewFromString(calVal)

+ 4 - 3
services/data/future_good/chart_info.go

@@ -300,9 +300,8 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, baseEdbInfoMapp
 		dataList := make([]*data_manage.EdbDataList, 0)
 		tmpDataList, ok := tmpDataListMap[v.EdbInfoId]
 		if !ok {
-			/*err = fmt.Errorf("期货数据不存在 FutureGoodEdbInfoId: %d", v.EdbInfoId)
-			return*/
-			continue
+			err = fmt.Errorf("期货数据不存在 FutureGoodEdbInfoId: %d", v.EdbInfoId)
+			return
 		}
 		for _, tmpData := range tmpDataList {
 			dataList = append(dataList, &data_manage.EdbDataList{
@@ -539,6 +538,7 @@ func BarChartData(baseEdbInfoMapping *data_manage.ChartEdbInfoMapping, edbInfoMa
 				xEdbInfoIdList = append(xEdbInfoIdList, v.EdbInfoId)
 				continue
 			}
+
 			findDataValueTmp, isFindTmp := baseEdbDataMap[v.EdbInfoId][realDateTime.Format(utils.FormatDate)]
 			findDataList = append(findDataList, findDataValueTmp)
 			yDataMap[v.EdbInfoId] = findDataValueTmp
@@ -556,6 +556,7 @@ func BarChartData(baseEdbInfoMapping *data_manage.ChartEdbInfoMapping, edbInfoMa
 		currYear := realDateTime.Year()   // 当前年份
 
 		mList := make([]int, 0) // 间隔月份
+
 		indexList := make([]int, 0)
 		if regionType == `国内` {
 			for i := currMonth + 1; i <= 12; i++ {

+ 33 - 22
services/data/future_good/profit_chart_info.go

@@ -9,11 +9,12 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/shopspring/decimal"
-	"github.com/yidane/formula"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/dengsgo/math-engine/engine"
 )
 
 // GetProfitChartEdbData 获取利润图表的指标数据
@@ -301,6 +302,7 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 				err = fmt.Errorf("指标id: %d 没有数据", edbInfoId)
 				return
 			}
+
 			findDataValueTmp, isFindTmp := baseEdbDateDataTmp[realDateTime.Format(utils.FormatDate)]
 			if !isFindTmp {
 				noDataIdList = append(noDataIdList, edbInfoId)
@@ -308,7 +310,6 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 			}
 			findDataList = append(findDataList, findDataValueTmp)
 			yDataMap[index] = findDataValueTmp
-
 			xEdbInfoIdList = append(xEdbInfoIdList, edbInfoId)
 			edbIdList = append(edbIdList, edbInfoId)
 			index += 1
@@ -327,6 +328,8 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 			//findDateTime
 
 			// 获取当前日期相对开始日期的期数
+			//tmpN := (currDate.Year()-findDateTime.Year())*12 + int(currDate.Month()-findDateTime.Month())
+			// 用实际日期的月份作为基准,往前推12个月(2024-5-13 16:26:43修改)
 			tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month())
 			if tmpN <= 0 {
 				continue
@@ -386,30 +389,38 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 				continue
 			}
 
-			expression := formula.NewExpression(formulaFormStr)
-			calResult, evaluateErr := expression.Evaluate()
-			if evaluateErr != nil {
-				// 分母为0的报错
-				if strings.Contains(evaluateErr.Error(), "divide by zero") {
-					//removeDateList = append(removeDateList, sk)
-					continue
-				}
-				err = errors.New("计算失败:Err:" + evaluateErr.Error() + ";formulaStr:" + formulaFormStr)
-				fmt.Println(err)
-				continue
-			}
-			// 如果计算结果是NAN,那么就退出当前循环
-			if calResult.IsNan() {
-				continue
-			}
-			calVal, tmpErr := calResult.Float64()
-			if tmpErr != nil {
-				err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+			//expression := formula.NewExpression(formulaFormStr)
+			//calResult, evaluateErr := expression.Evaluate()
+			//if evaluateErr != nil {
+			//	// 分母为0的报错
+			//	if strings.Contains(evaluateErr.Error(), "divide by zero") {
+			//		//removeDateList = append(removeDateList, sk)
+			//		continue
+			//	}
+			//	err = errors.New("计算失败:Err:" + evaluateErr.Error() + ";formulaStr:" + formulaFormStr)
+			//	fmt.Println(err)
+			//	continue
+			//}
+			//// 如果计算结果是NAN,那么就退出当前循环
+			//if calResult.IsNan() {
+			//	continue
+			//}
+			//calVal, tmpErr := calResult.Float64()
+			//if tmpErr != nil {
+			//	err = errors.New("计算失败:获取计算值失败 Err:" + tmpErr.Error() + ";formulaStr:" + formulaFormStr)
+			//	fmt.Println(err)
+			//	continue
+			//}
+			calVal, err := engine.ParseAndExec(formulaFormStr)
+			//calVal, err := calResult.Float64()
+			if err != nil {
+				err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
 				fmt.Println(err)
-				continue
+				return nil, nil, err
 			}
 			//yDataMap[n] = calVal
 			//xEdbInfoIdList = append(xEdbInfoIdList, n)
+
 			calVal, _ = decimal.NewFromFloat(calVal).Round(4).Float64()
 			yDataMap[tmpN] = calVal
 			xEdbInfoIdList = append(xEdbInfoIdList, tmpN)

+ 10 - 6
services/data/line_feature/chart_info.go

@@ -158,21 +158,25 @@ func GetPercentileData(chartInfoId int, startDate, endDate string, mappingInfo *
 	// 百分位数据个数算法
 	// 数据区间第一个和最后一个数据点的时间和数据分别为(T1,S1)(T2,S2); N=T1到T2指标数据个数, n=小于等于S2的数据个数
 	// 个数百分位=(n-1)/(N-1)
-	maxDay := len(dataList) // 往前找数据的边界
+	var firstDate time.Time
+	if len(dataList) > 0 {
+		d, _ := time.ParseInLocation(utils.FormatDate, dataList[0].DataTime, time.Local)
+		firstDate = d
+	}
 	if percentType == utils.PercentCalculateTypeNum {
 		for i, d := range dataList {
 			// T2为当前日期
 			s2 := decimal.NewFromFloat(d.Value)
 			t2, _ := time.ParseInLocation(utils.FormatDate, d.DataTime, time.Local)
 
-			// 计算N和n
+			// 往前找(时间长度)个有数据的
 			var bigN, tinyN int
-			for k := 0; k < maxDay; k++ {
-				// 往前找(时间长度)个有数据的, N理论上只有最前面几个日期<calculateDay, 后面的N=calculateDay
-				if bigN >= calculateDay {
+			for k := 0; k < calculateDay; k++ {
+				tp := t2.AddDate(0, 0, -k)
+				if tp.Before(firstDate) {
 					break
 				}
-				preVal, preOk := dataMap[t2.AddDate(0, 0, -k)]
+				preVal, preOk := dataMap[tp]
 				if !preOk {
 					continue
 				}

+ 983 - 0
services/data/manual.go

@@ -1,10 +1,17 @@
 package data
 
 import (
+	"errors"
+	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/shopspring/decimal"
+	"github.com/tealeg/xlsx"
+	"strconv"
+	"strings"
+	"time"
 )
 
 func GetManualSysUser(keyWord string) (list []*data_manage.ManualSysUser, err error) {
@@ -82,3 +89,979 @@ func GetManualSysUser(keyWord string) (list []*data_manage.ManualSysUser, err er
 	}
 	return
 }
+
+// GetManualEdbClassifyListByAdminId
+// @Description: 根据账户类型获取手工指标分类ID集合
+// @author: Roc
+// @datetime 2024-07-16 13:18:39
+// @param adminId int64
+// @return classifyIdList []int
+// @return err error
+func GetManualEdbClassifyListByAdminId(adminId int64) (classifyIdList []int, err error) {
+	var list []*models.EdbdataClassify
+	if adminId <= 0 {
+		list, err = models.GetAllChildManualEdbClassify()
+	} else {
+		userClassifyList, _ := models.GetManualUserClassify(int(adminId))
+		var userClassifyIdList []int
+		for _, v := range userClassifyList {
+			userClassifyIdList = append(userClassifyIdList, v.ClassifyId)
+		}
+		list, err = models.GetChildManualEdbClassifyByIdList(userClassifyIdList)
+	}
+	if err != nil {
+		return
+	}
+	for _, classify := range list {
+		classifyIdList = append(classifyIdList, classify.ClassifyId)
+	}
+
+	return
+}
+
+type ManualIndexSource2EdbReq struct {
+	EdbCode       string
+	EdbName       string
+	Frequency     string
+	Unit          string
+	ClassifyId    int
+	AdminId       int
+	AdminRealName string
+}
+
+// ManualIndexSource2Edb
+// @Description: 新增手工数据源到指标库
+// @author: Roc
+// @datetime 2024-07-26 13:23:19
+// @param req ManualIndexSource2EdbReq
+// @param lang string
+// @return edb *data_manage.EdbInfo
+// @return err error
+// @return errMsg string
+// @return skip bool
+func ManualIndexSource2Edb(req ManualIndexSource2EdbReq, lang string) (edb *data_manage.EdbInfo, err error, errMsg string, skip bool) {
+	if req.EdbCode == "" {
+		err = fmt.Errorf("指标ID为空")
+		return
+	}
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("ManualIndexSource2Edb新增失败, Err: %s", err.Error())
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+		}
+	}()
+	source := utils.DATA_SOURCE_MANUAL
+
+	// 是否已有指标数据
+	dataList, e := data_manage.GetEdbDataAllByEdbCode(req.EdbCode, source, utils.DATA_SUB_SOURCE_EDB, utils.EDB_DATA_LIMIT)
+	if e != nil {
+		err = fmt.Errorf("获取指标数据失败, Err: %s", e.Error())
+		return
+	}
+
+	// 新增指标数据
+	if len(dataList) == 0 {
+		res, e := AddEdbData(source, req.EdbCode, req.Frequency)
+		if e != nil {
+			err = fmt.Errorf("index_lib: 新增指标数据失败, Err: %s", e.Error())
+			return
+		}
+		if res == nil {
+			err = fmt.Errorf("index_lib: 新增指标数据失败, res nil")
+			return
+		}
+		if res.Ret != 200 {
+			err = fmt.Errorf("index_lib: 新增指标数据失败, Ret: %d", res.Ret)
+			return
+		}
+	}
+
+	// 是否新增过指标
+	exist, e := data_manage.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("获取指标是否存在失败, err: %s", e.Error())
+		return
+	}
+	if exist != nil {
+		skip = true
+		return
+	}
+
+	// 开始结束时间
+	var startDate, endDate string
+	minMax, e := data_manage.GetEdbInfoMaxAndMinInfo(source, utils.DATA_SUB_SOURCE_EDB, req.EdbCode)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = fmt.Errorf("MinMax: 获取指标极值失败, err: %s", e.Error())
+		return
+	}
+	if minMax != nil {
+		startDate = minMax.MinDate
+		endDate = minMax.MaxDate
+	}
+
+	// 新增指标库
+	edbInfo, e, msg, _ := EdbInfoAdd(source, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, startDate, endDate, req.AdminId, req.AdminRealName, lang)
+	if e != nil {
+		errMsg = msg
+		err = fmt.Errorf("EdbInfo: 新增指标失败, err: %s", e.Error())
+		return
+	}
+	edb = edbInfo
+
+	// 新增es
+	go AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+
+	return
+}
+
+//func ImportManualData(path string, sysUser *system.Admin) (err error, errMsg string) {
+//	errMsg = `导入失败`
+//	xlFile, err := xlsx.OpenFile(path)
+//	if err != nil {
+//		fmt.Println(err.Error())
+//		return
+//	}
+//	if len(xlFile.Sheets) <= 0 {
+//		errMsg = "导入模板异常"
+//		err = errors.New(errMsg)
+//		return
+//	}
+//
+//	//导入成功数量
+//	successCount := 0
+//	//导入失败数据
+//	failDataList := make([]*models.EdbdataImportFail, 0)
+//	var indexDataList []ImportManualIndexData
+//
+//	// 模板校验,然后处理成标准化格式
+//	for _, sheet := range xlFile.Sheets {
+//		var tmpIndexDataList []ImportManualIndexData
+//		var tmpFailDataList []*models.EdbdataImportFail
+//		rowList := sheet.Rows
+//		if len(rowList) <= 0 {
+//			errMsg = sheet.Name + "页异常"
+//			err = errors.New(errMsg)
+//			return
+//		}
+//		headerCell := rowList[0].Cells
+//		if len(headerCell) < 7 {
+//			errMsg = sheet.Name + "页模板异常"
+//			err = errors.New(errMsg)
+//			return
+//		}
+//
+//		templateName := headerCell[6].String()
+//		switch templateName {
+//		case "导入模板2/Import Template 2":
+//			// 模板2需要走对应的取数逻辑
+//			tmpIndexDataList, tmpFailDataList, err, errMsg = getDataByTemplate2(sheet, sysUser.AdminId)
+//		default:
+//			// 模板1需要走对应的取数逻辑
+//			tmpIndexDataList, tmpFailDataList, err, errMsg = getDataByTemplate1(sheet, sysUser.AdminId)
+//		}
+//		indexDataList = append(indexDataList, tmpIndexDataList...)
+//		failDataList = append(failDataList, tmpFailDataList...)
+//	}
+//
+//	var indexDataListMap map[string]ImportManualIndexData
+//	for _, v := range indexDataList {
+//		indexData, ok := indexDataListMap[v.IndexName]
+//		if !ok {
+//			// 没有就赋值
+//			indexDataListMap[v.IndexName] = v
+//			continue
+//		}
+//
+//		indexData.Unit = v.Unit
+//		indexData.ClassName = v.ClassName
+//		indexData.Frequency = v.Frequency
+//
+//		indexData.DataMap = v.DataMap
+//	}
+//
+//	//超管账号可以查看分类下的所有频度数据
+//	userId := sysUser.AdminId
+//	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
+//		userId = 0
+//	}
+//	//获取账户所拥有权限的分类id集合
+//	classifyNameStrList, edbDataClassifyMap, err := GetEdbClassifyNameListByAdminId(int64(userId))
+//	if err != nil {
+//		errMsg = "获取分类数据失败"
+//		return
+//	}
+//
+//	////指标map
+//	//targetMap := make(map[string]*models.Edbinfo)
+//	//defer func() {
+//	//	for _, target := range targetMap {
+//	//		//结束后,清除掉对应的缓存
+//	//		key := "import:edbinfo:data:" + target.TradeCode
+//	//		utils.Rc.Delete(key)
+//	//
+//	//		//将该指标的code加入到 “手工数据导入后刷新” 缓存
+//	//		if utils.Re == nil {
+//	//			err := utils.Rc.LPush(utils.CACHE_IMPORT_MANUAL_DATA, target.TradeCode)
+//	//			if err != nil {
+//	//				fmt.Println("CACHE_IMPORT_MANUAL_DATA LPush Err:" + err.Error())
+//	//			}
+//	//		}
+//	//	}
+//	//}()
+//	//
+//	//// 所有的指标数据map
+//	//edbCodeDataMap := make(map[string]map[string]string)
+//
+//	// 遍历sheet页读取
+//	//for _, sheet := range xlFile.Sheets {
+//	//	fmt.Println("sheet name: ", sheet.Name)
+//	//	//遍历行读取
+//	//	maxRow := sheet.MaxRow
+//	//	fmt.Println("maxRow:", maxRow)
+//	//	fmt.Println("maxRow")
+//	//	for i := 0; i < maxRow; i++ {
+//	//		if i == 1 {
+//	//			row := sheet.Row(i)
+//	//			cells := row.Cells
+//	//			if len(cells) < 6 {
+//	//				br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
+//	//				br.Msg = "导入文件异常,请下载最新导入模板文件"
+//	//				return
+//	//			}
+//	//			templateFail := false
+//	//			if cells[0].Value != "品种分类" && cells[0].Value != "Species Category" {
+//	//				templateFail = true
+//	//			}
+//	//			if cells[1].Value != "录入日期" && cells[1].Value != "Input Date" {
+//	//				templateFail = true
+//	//			}
+//	//			if cells[2].Value != "指标名称" && cells[2].Value != "Indicator Name Indicator Name" {
+//	//				templateFail = true
+//	//			}
+//	//			if cells[3].Value != "值" && cells[3].Value != "Value" {
+//	//				templateFail = true
+//	//			}
+//	//			if cells[4].Value != "频度" && cells[4].Value != "Frequency" {
+//	//				templateFail = true
+//	//			}
+//	//			if cells[5].Value != "单位" && cells[5].Value != "Unit" {
+//	//				templateFail = true
+//	//			}
+//	//			if templateFail {
+//	//				br.ErrMsg = "导入文件异常,请下载最新导入模板文件"
+//	//				br.Msg = "导入文件异常,请下载最新导入模板文件"
+//	//				return
+//	//			}
+//	//		}
+//	//		if i > 1 {
+//	//			row := sheet.Row(i)
+//	//			cells := row.Cells
+//	//			if len(cells) >= 6 {
+//	//				classifyName := cells[0].Value //分类
+//	//				if classifyName == "" {        //过滤空白行
+//	//					continue
+//	//				}
+//	//				cell1 := cells[1].Value
+//	//				//createDate := utils.ConvertToFormatDay(cell1) //录入日期
+//	//				createDate := cell1
+//	//				cell2 := cells[2].Value //指标名称
+//	//				secName := utils.TrimStr(cell2)
+//	//				frequency := cells[4].Value //频度
+//	//				unit := cells[5].Value      //单位
+//	//
+//	//				closeVal := cells[3].Value //值
+//	//				if strings.Contains(closeVal, "#N/A") {
+//	//					continue
+//	//				}
+//	//
+//	//				//校验表格中的日期格式
+//	//				if strings.Contains(createDate, "-") {
+//	//					//如果是带有 - 的普通日期格式文本
+//	//					_, timeErr := time.Parse("2006-1-2", createDate)
+//	//					if timeErr != nil {
+//	//						failItem := new(models.EdbdataImportFail)
+//	//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//						failItem.ClassifyName = classifyName
+//	//						failItem.CreateDate = createDate
+//	//						failItem.SecName = secName
+//	//						failItem.Close = closeVal
+//	//						failItem.Remark = "日期格式异常"
+//	//						failItem.Frequency = frequency
+//	//						failItem.Unit = unit
+//	//						failDataList = append(failDataList, failItem)
+//	//						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//	//						continue
+//	//					}
+//	//				} else if strings.Contains(createDate, "/") {
+//	//					//如果是带有 / 的普通日期格式文本
+//	//					createDateTime, timeErr := time.Parse("2006/1/2", createDate)
+//	//					if timeErr != nil {
+//	//						failItem := new(models.EdbdataImportFail)
+//	//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//						failItem.ClassifyName = classifyName
+//	//						failItem.CreateDate = createDate
+//	//						failItem.SecName = secName
+//	//						failItem.Close = closeVal
+//	//						failItem.Remark = "日期格式异常"
+//	//						failItem.Frequency = frequency
+//	//						failItem.Unit = unit
+//	//						failDataList = append(failDataList, failItem)
+//	//						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//	//						continue
+//	//					}
+//	//					createDate = createDateTime.Format("2006-01-02")
+//	//				} else {
+//	//					//可能是excel的日期格式
+//	//					_, tmpErr := strconv.Atoi(createDate)
+//	//					if tmpErr != nil {
+//	//						failItem := new(models.EdbdataImportFail)
+//	//						failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//						failItem.ClassifyName = classifyName
+//	//						failItem.CreateDate = createDate
+//	//						failItem.SecName = secName
+//	//						failItem.Close = closeVal
+//	//						failItem.Remark = "日期格式异常"
+//	//						failItem.Frequency = frequency
+//	//						failItem.Unit = unit
+//	//						failDataList = append(failDataList, failItem)
+//	//						//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//	//						continue
+//	//					}
+//	//					createDate = utils.ConvertToFormatDay(createDate) //录入日期
+//	//				}
+//	//
+//	//				closeValFloat, err := cells[3].Float() //值
+//	//
+//	//				if err != nil {
+//	//					failItem := new(models.EdbdataImportFail)
+//	//					failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//					failItem.ClassifyName = classifyName
+//	//					failItem.CreateDate = createDate
+//	//					failItem.SecName = secName
+//	//					failItem.Close = cells[3].Value
+//	//					failItem.Remark = "值类型异常"
+//	//					failItem.Frequency = frequency
+//	//					failItem.Unit = unit
+//	//					failDataList = append(failDataList, failItem)
+//	//					continue
+//	//				}
+//	//				newDecimal := decimal.NewFromFloat(closeValFloat)
+//	//				newDecimal.Round(4)
+//	//				closeVal = newDecimal.String()
+//	//				if strings.Contains(closeVal, "#N/A") {
+//	//					continue
+//	//				}
+//	//				//closeVal := closeValFloat.
+//	//
+//	//				//判断指标,类型,等数据是否正常
+//	//				classifyName = strings.Trim(classifyName, " ")
+//	//				frequency = utils.TrimStr(frequency)
+//	//				unit = utils.TrimStr(unit)
+//	//
+//	//				// 成功数量超过20个指标,那就不导入了
+//	//				if len(targetMap) >= 150 {
+//	//					failItem := new(models.EdbdataImportFail)
+//	//					failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//					failItem.ClassifyName = classifyName
+//	//					failItem.CreateDate = createDate
+//	//					failItem.SecName = secName
+//	//					failItem.Close = closeVal
+//	//					failItem.Remark = "导入指标数量过多"
+//	//					failItem.Frequency = frequency
+//	//					failItem.Unit = unit
+//	//					failDataList = append(failDataList, failItem)
+//	//					//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//	//					continue
+//	//				}
+//	//
+//	//				if !strings.Contains(strings.Join(classifyNameStrList, ","), classifyName) {
+//	//					failItem := new(models.EdbdataImportFail)
+//	//					failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//					failItem.ClassifyName = classifyName
+//	//					failItem.CreateDate = createDate
+//	//					failItem.SecName = secName
+//	//					failItem.Close = closeVal
+//	//					failItem.Remark = "没有该品种分类权限"
+//	//					failItem.Frequency = frequency
+//	//					failItem.Unit = unit
+//	//					failDataList = append(failDataList, failItem)
+//	//					continue
+//	//				}
+//	//				//fmt.Println(classifyName, createDate, secName, closeVal)
+//	//
+//	//				//获取指标分类信息
+//	//				classify, ok := edbDataClassifyMap[classifyName]
+//	//				if !ok {
+//	//					failItem := new(models.EdbdataImportFail)
+//	//					failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//					failItem.ClassifyName = classifyName
+//	//					failItem.CreateDate = createDate
+//	//					failItem.SecName = secName
+//	//					failItem.Close = closeVal
+//	//					failItem.Remark = "指标分类不存在"
+//	//					failItem.Frequency = frequency
+//	//					failItem.Unit = unit
+//	//					failDataList = append(failDataList, failItem)
+//	//					//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+//	//					continue
+//	//				}
+//	//
+//	//				//判断指标是否存在
+//	//				target, ok := targetMap[secName]
+//	//				if !ok {
+//	//					tmpTarget, err := models.GetTargetBySecName(secName)
+//	//					if err != nil {
+//	//						//如果是找不到该指标,那么新增指标
+//	//						if err.Error() == utils.ErrNoRow() {
+//	//							if frequency == "" {
+//	//								failItem := new(models.EdbdataImportFail)
+//	//								failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//								failItem.ClassifyName = classifyName
+//	//								failItem.CreateDate = createDate
+//	//								failItem.SecName = secName
+//	//								failItem.Close = closeVal
+//	//								failItem.Remark = "新增指标失败,频度字段为空"
+//	//								failItem.Frequency = frequency
+//	//								failItem.Unit = unit
+//	//								failDataList = append(failDataList, failItem)
+//	//								continue
+//	//							}
+//	//							if unit == "" {
+//	//								failItem := new(models.EdbdataImportFail)
+//	//								failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//								failItem.ClassifyName = classifyName
+//	//								failItem.CreateDate = createDate
+//	//								failItem.SecName = secName
+//	//								failItem.Close = closeVal
+//	//								failItem.Remark = "新增指标失败,单位字段为空"
+//	//								failItem.Frequency = frequency
+//	//								failItem.Unit = unit
+//	//								failDataList = append(failDataList, failItem)
+//	//								continue
+//	//							}
+//	//							tmpErr := data.AddEdbInfo(secName, unit, frequency, "", sysUser.Mobile, classify.ClassifyId, sysUser.AdminId)
+//	//							if tmpErr != nil {
+//	//								fmt.Println("line 158")
+//	//								failItem := new(models.EdbdataImportFail)
+//	//								failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//								failItem.ClassifyName = classifyName
+//	//								failItem.CreateDate = createDate
+//	//								failItem.SecName = secName
+//	//								failItem.Close = closeVal
+//	//								failItem.Remark = "新增指标失败"
+//	//								failItem.Frequency = frequency
+//	//								failItem.Unit = unit
+//	//								failDataList = append(failDataList, failItem)
+//	//								continue
+//	//							}
+//	//							tmpTarget, tmpErr := models.GetTargetBySecName(secName)
+//	//							target = tmpTarget
+//	//							targetMap[secName] = target
+//	//						} else {
+//	//							fmt.Println("导入数据 获取指标:Err:" + err.Error())
+//	//						}
+//	//					} else {
+//	//						target = tmpTarget
+//	//						targetMap[secName] = target
+//	//					}
+//	//
+//	//					//设置10分钟缓存,不允许其他地方删除
+//	//					key := "import:edbinfo:data:" + target.TradeCode
+//	//					utils.Rc.SetNX(key, 1, time.Second*600)
+//	//				}
+//	//
+//	//				if target == nil {
+//	//					fmt.Println("指标不存在")
+//	//					failItem := new(models.EdbdataImportFail)
+//	//					failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+//	//					failItem.ClassifyName = classifyName
+//	//					failItem.CreateDate = createDate
+//	//					failItem.SecName = secName
+//	//					failItem.Close = closeVal
+//	//					failItem.Remark = "指标不存在"
+//	//					failItem.Frequency = frequency
+//	//					failItem.Unit = unit
+//	//					failDataList = append(failDataList, failItem)
+//	//					continue
+//	//				}
+//	//
+//	//				//更新指标信息
+//	//				updateCols := make([]string, 0)
+//	//				//更新指标分类
+//	//				if target.ClassifyId <= 0 && classify.ClassifyId > 0 {
+//	//					target.ClassifyId = classify.ClassifyId
+//	//					updateCols = append(updateCols, "ClassifyId")
+//	//				}
+//	//				if target.Frequency != frequency {
+//	//					target.Frequency = frequency
+//	//					target.NoticeTime = ""
+//	//					updateCols = append(updateCols, "Frequency", "NoticeTime")
+//	//				}
+//	//				if target.Unit != unit {
+//	//					target.Unit = unit
+//	//					updateCols = append(updateCols, "Unit")
+//	//				}
+//	//				if len(updateCols) > 0 {
+//	//					_ = target.Update(updateCols)
+//	//				}
+//	//
+//	//				// 判断指标数据列表是否已经存在
+//	//				tmpDataMap, ok := edbCodeDataMap[target.TradeCode]
+//	//				if !ok {
+//	//					tmpDataMap = make(map[string]string)
+//	//					dataList, tmpErr := models.GetTargetsDataList(target.TradeCode)
+//	//					if tmpErr != nil {
+//	//						go alarm_msg.SendAlarmMsg("导入数据"+target.TradeCode+" 获取指标的数据失败,Err:"+err.Error(), 3)
+//	//					}
+//	//					for _, tmpData := range dataList {
+//	//						tmpDataMap[tmpData.Dt] = tmpData.Close
+//	//					}
+//	//					edbCodeDataMap[target.TradeCode] = tmpDataMap
+//	//				}
+//	//
+//	//				//判断数据是否已经存在
+//	//				tmpVal, ok := tmpDataMap[createDate]
+//	//				//数据已存在,进行更新操作
+//	//				if ok {
+//	//					if tmpVal != closeVal {
+//	//						err = models.ModifyTargetsDataByImport(target.TradeCode, createDate, closeVal)
+//	//						if err != nil {
+//	//							go alarm_msg.SendAlarmMsg("导入数据 修改数据失败,Err:"+err.Error(), 3)
+//	//							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 修改数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+//	//						}
+//	//					}
+//	//				} else { //数据不存在,进行新增操作
+//	//					if target.TradeCode != "" && createDate != "" && closeVal != "" {
+//	//						models.AddTargetsDataByImport(target.TradeCode, createDate, closeVal)
+//	//						if err != nil {
+//	//							go alarm_msg.SendAlarmMsg("导入数据 新增数据失败,Err:"+err.Error(), 3)
+//	//							//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 新增数据失败:Err:"+err.Error(), utils.EmailSendToUsers)
+//	//						}
+//	//						tmpDataMap[createDate] = closeVal
+//	//						edbCodeDataMap[target.TradeCode] = tmpDataMap
+//	//					}
+//	//				}
+//	//				successCount++
+//	//			} else {
+//	//				//br.ErrMsg = "请填写导入内容"
+//	//				//br.Msg = "请填写导入内容"
+//	//				//return
+//	//			}
+//	//		}
+//	//	}
+//	//}
+//
+//	return
+//}
+
+// ImportManualIndexData
+// @Description: excel模板后的内容
+type ImportManualIndexData struct {
+	IndexName string            `description:"指标名称"`
+	Unit      string            `description:"单位"`
+	ClassName string            `description:"所属品种"`
+	Frequency string            `description:"频度"`
+	DataMap   map[string]string `description:"时间数据"`
+}
+
+// TODO 模板数据获取异常的结果记录
+
+// getDataByTemplate1
+// @Description: 根据模板1获取数据
+// @author: Roc
+// @datetime 2024-07-24 16:17:45
+// @param sheet *xlsx.Sheet
+// @return indexDataList []ImportManualIndexData
+// @return err error
+// @return errMsg string
+func getDataByTemplate1(sheet *xlsx.Sheet, sysUserId int) (indexDataList []ImportManualIndexData, failDataList []*models.EdbdataImportFail, err error, errMsg string) {
+	fmt.Println("sheet name: ", sheet.Name)
+	indexDataList = make([]ImportManualIndexData, 0)
+	indexDataListMap := make(map[string]ImportManualIndexData, 0)
+	failDataList = make([]*models.EdbdataImportFail, 0)
+
+	//遍历行读取
+	maxRow := sheet.MaxRow
+	fmt.Println("maxRow:", maxRow)
+
+	// 表头信息
+	if maxRow <= 2 {
+		errMsg = "模板异常1"
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 表头校验
+	{
+		headerRow := sheet.Row(1)
+		cells := headerRow.Cells
+		if len(cells) < 6 {
+			errMsg = "导入文件异常,请下载最新导入模板文件"
+			err = errors.New(errMsg)
+			return
+		}
+
+		templateFail := false
+		if cells[0].Value != "品种/Variety" {
+			templateFail = true
+		}
+		if cells[1].Value != "指标名称/Indicator Name" {
+			templateFail = true
+		}
+		if cells[2].Value != "指标日期/Indicator Date" {
+			templateFail = true
+		}
+		if cells[3].Value != "值/Value" {
+			templateFail = true
+		}
+		if cells[4].Value != "频度/Frequency" {
+			templateFail = true
+		}
+		if cells[5].Value != "单位/Unit" {
+			templateFail = true
+		}
+		if templateFail {
+			errMsg = "导入文件异常,请下载最新导入模板文件"
+			err = errors.New(errMsg)
+			return
+		}
+	}
+
+	for i := 2; i < maxRow; i++ {
+		row := sheet.Row(i)
+		cells := row.Cells
+		if len(cells) < 6 {
+			errMsg = "导入文件异常,请下载最新导入模板文件"
+			err = errors.New(errMsg)
+			return
+		}
+
+		classifyName := strings.TrimSpace(cells[0].Value) //分类
+		if classifyName == "" {                           //过滤空白行
+			continue
+		}
+		// 指标名称
+		cell1 := cells[1].Value
+		indexName := strings.TrimSpace(cell1)
+		if indexName == "" { //过滤空白行
+			continue
+		}
+		//createDate := utils.ConvertToFormatDay(cell1) //录入日期
+		createDate := cells[2].Value                      //指标日期
+		frequency := strings.TrimSpace(cells[4].String()) //频度
+		unit := strings.TrimSpace(cells[5].String())      //单位
+
+		closeVal := cells[3].Value //值
+		if strings.Contains(closeVal, "#N/A") {
+			continue
+		}
+
+		currDate, tmpErr := getExcelDate(createDate)
+		if tmpErr != nil {
+			failDataList = append(failDataList, &models.EdbdataImportFail{
+				//Id:           0,
+				ClassifyName: classifyName,
+				CreateDate:   createDate,
+				SecName:      indexName,
+				Close:        closeVal,
+				Remark:       "日期格式异常",
+				SysUserId:    strconv.Itoa(sysUserId),
+				Frequency:    frequency,
+				Unit:         unit,
+			})
+			continue
+		}
+
+		closeValFloat, tmpErr := cells[3].Float() //值
+		if tmpErr != nil {
+			failDataList = append(failDataList, &models.EdbdataImportFail{
+				//Id:           0,
+				ClassifyName: classifyName,
+				CreateDate:   currDate,
+				SecName:      indexName,
+				Close:        cells[3].Value,
+				Remark:       "值类型异常",
+				SysUserId:    strconv.Itoa(sysUserId),
+				Frequency:    frequency,
+				Unit:         unit,
+			})
+			continue
+		}
+		newDecimal := decimal.NewFromFloat(closeValFloat)
+		newDecimal.Round(4)
+		closeVal = newDecimal.String()
+		if strings.Contains(closeVal, "#N/A") {
+			continue
+		}
+
+		_, ok := indexDataListMap[indexName]
+		if !ok {
+			indexDataListMap[indexName] = ImportManualIndexData{
+				IndexName: indexName,
+				Unit:      unit,
+				ClassName: classifyName,
+				Frequency: frequency,
+				DataMap:   make(map[string]string),
+			}
+		}
+
+		indexDataListMap[indexName].DataMap[currDate] = closeVal
+	}
+
+	for _, v := range indexDataListMap {
+		indexDataList = append(indexDataList, v)
+	}
+
+	return
+}
+
+// getDataByTemplate2
+// @Description: 根据模板2获取数据
+// @author: Roc
+// @datetime 2024-07-24 16:17:56
+// @param sheet *xlsx.Sheet
+// @return indexDataList []ImportManualIndexData
+// @return err error
+// @return errMsg string
+func getDataByTemplate2(sheet *xlsx.Sheet, sysUserId int) (indexDataList []ImportManualIndexData, failDataList []*models.EdbdataImportFail, err error, errMsg string) {
+	fmt.Println("sheet name: ", sheet.Name)
+	indexDataList = make([]ImportManualIndexData, 0)
+	failDataList = make([]*models.EdbdataImportFail, 0)
+
+	//遍历行读取
+	maxRow := sheet.MaxRow
+	fmt.Println("maxRow:", maxRow)
+
+	varietyList := make([]string, 0)
+	indexNameList := make([]string, 0)
+	unitList := make([]string, 0)
+	frequencyList := make([]string, 0)
+
+	// make(map[指标下标]map[日期]值)
+	indexDateValueMap := make(map[int]map[string]string)
+
+	// 表头信息
+	if maxRow <= 4 {
+		errMsg = "模板异常1"
+		err = errors.New(errMsg)
+		return
+	}
+
+	// 表头处理
+	for i := 1; i <= 4; i++ {
+		row := sheet.Row(i)
+		cells := row.Cells
+		//if len(cells) < 1 {
+		//	errMsg = "模板异常2"
+		//	err = errors.New(errMsg)
+		//	return
+		//}
+
+		switch i {
+		case 1:
+			for k, v := range cells {
+				if k == 0 {
+					continue
+				}
+				varietyList = append(varietyList, strings.TrimSpace(v.String()))
+			}
+		case 2:
+			for k, v := range cells {
+				if k == 0 {
+					continue
+				}
+				indexNameList = append(indexNameList, strings.TrimSpace(v.String()))
+			}
+		case 3:
+			for k, v := range cells {
+				if k == 0 {
+					continue
+				}
+				unitList = append(unitList, strings.TrimSpace(v.String()))
+			}
+		case 4:
+			for k, v := range cells {
+				if k == 0 {
+					continue
+				}
+				frequencyList = append(frequencyList, strings.TrimSpace(v.String()))
+			}
+		}
+	}
+
+	maxNameIndex := len(indexNameList) - 1
+	maxUnitIndex := len(unitList) - 1
+	maxClassNameIndex := len(varietyList) - 1
+	maxFrequencyIndex := len(frequencyList) - 1
+
+	// 数据处理
+	for i := 5; i < maxRow; i++ {
+		row := sheet.Row(i)
+		cells := row.Cells
+		//if len(cells) < 1 {
+		//	errMsg = "模板异常2"
+		//	err = errors.New(errMsg)
+		//	return
+		//}
+		// 当前日期
+		var currDate string
+
+		// 日期是否异常,异常的话,就跳过,进入下一行数据处理
+		var dateErr bool
+
+		// 数据处理
+		for k, v := range cells {
+			if k == 0 {
+				tmpCurrDate, tmpErr := getExcelDate(v.Value)
+				if tmpErr != nil {
+					// TODO 错误数据记录
+					//failItem := new(models.EdbdataImportFail)
+					//failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+					//failItem.ClassifyName = classifyName
+					//failItem.CreateDate = createDate
+					//failItem.SecName = secName
+					//failItem.Close = closeVal
+					//failItem.Remark = "日期格式异常"
+					//failItem.Frequency = frequency
+					//failItem.Unit = unit
+					//failDataList = append(failDataList, failItem)
+					//go utils.SendEmail(utils.APPNAME+"失败提醒", "导入数据 获取分类:Err:"+err.Error(), utils.EmailSendToUsers)
+					dateErr = true
+				}
+
+				currDate = tmpCurrDate
+				continue
+			}
+			key := k - 1
+
+			// 日期异常,所以不处理该行了
+			if dateErr {
+				var indexName, unit, classifyName, frequency string
+				if key <= maxNameIndex {
+					indexName = indexNameList[key]
+				}
+				if key <= maxUnitIndex {
+					unit = unitList[key]
+				}
+				if key <= maxClassNameIndex {
+					classifyName = varietyList[key]
+				}
+				if key <= maxFrequencyIndex {
+					frequency = frequencyList[key]
+				}
+
+				failDataList = append(failDataList, &models.EdbdataImportFail{
+					//Id:           0,
+					ClassifyName: classifyName,
+					CreateDate:   currDate,
+					SecName:      indexName,
+					Close:        cells[3].Value,
+					Remark:       "日期格式异常",
+					SysUserId:    strconv.Itoa(sysUserId),
+					Frequency:    frequency,
+					Unit:         unit,
+				})
+
+				continue
+			}
+
+			_, ok := indexDateValueMap[key]
+			if !ok {
+				indexDateValueMap[key] = make(map[string]string)
+			}
+
+			closeVal := v.Value //值
+			// 没有数据,说明是空串
+			if strings.Contains(closeVal, "#N/A") {
+				indexDateValueMap[key][currDate] = ""
+				continue
+			}
+
+			closeValFloat, tmpErr := v.Float() //值
+			if tmpErr != nil {
+				// TODO 错误数据记录
+				//failItem := new(models.EdbdataImportFail)
+				//failItem.SysUserId = strconv.Itoa(sysUser.AdminId)
+				//failItem.ClassifyName = classifyName
+				//failItem.CreateDate = createDate
+				//failItem.SecName = secName
+				//failItem.Close = cells[3].Value
+				//failItem.Remark = "值类型异常"
+				//failItem.Frequency = frequency
+				//failItem.Unit = unit
+				//failDataList = append(failDataList, failItem)
+				indexDateValueMap[key][currDate] = ""
+				continue
+			}
+
+			newDecimal := decimal.NewFromFloat(closeValFloat)
+			newDecimal.Round(4)
+			closeVal = newDecimal.String()
+			if strings.Contains(closeVal, "#N/A") {
+				indexDateValueMap[key][currDate] = ""
+				continue
+			}
+			indexDateValueMap[key][currDate] = closeVal
+		}
+
+	}
+
+	for i, indexName := range indexNameList {
+		var unit, classifyName, frequency string
+		if i <= maxUnitIndex {
+			unit = unitList[i]
+		}
+		if i <= maxClassNameIndex {
+			classifyName = varietyList[i]
+		}
+		if i <= maxFrequencyIndex {
+			frequency = frequencyList[i]
+		}
+		indexData := ImportManualIndexData{
+			IndexName: indexName,
+			Unit:      unit,
+			ClassName: classifyName,
+			Frequency: frequency,
+			DataMap:   indexDateValueMap[i],
+		}
+		indexDataList = append(indexDataList, indexData)
+	}
+
+	return
+}
+
+// getExcelDate
+// @Description: 获取excel的日期
+// @author: Roc
+// @datetime 2024-07-23 17:26:12
+// @param createDate string
+// @return newCreateDate string
+// @return err error
+func getExcelDate(createDate string) (newCreateDate string, err error) {
+	if strings.Contains(createDate, "-") {
+		//如果是带有 - 的普通日期格式文本
+		_, err = time.Parse("2006-1-2", createDate)
+		if err == nil {
+			newCreateDate = createDate
+		}
+	} else if strings.Contains(createDate, "/") {
+		//如果是带有 / 的普通日期格式文本
+		createDateTime, timeErr := time.Parse("2006/1/2", createDate)
+		if timeErr != nil {
+			err = timeErr
+		} else {
+			newCreateDate = createDateTime.Format("2006-01-02")
+		}
+	} else {
+		//可能是excel的日期格式
+		_, tmpErr := strconv.Atoi(createDate)
+		if tmpErr != nil {
+			err = tmpErr
+		} else {
+			newCreateDate = utils.ConvertToFormatDay(createDate) //录入日期
+		}
+	}
+
+	return
+}

+ 1651 - 0
services/data/range_analysis/chart_info.go

@@ -0,0 +1,1651 @@
+package range_analysis
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/services/data"
+	"eta/eta_api/utils"
+	"fmt"
+	"math"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// HandleDataByCalculateType 根据计算公式处理数据
+func HandleDataByCalculateType(originList []*data_manage.ChartRangeAnalysisDateDataItem, calculateType int) (newList []*data_manage.EdbDataList, err error) {
+	if len(originList) == 0 {
+		return
+	}
+	switch calculateType {
+	case 0: //均值
+		var sum float64
+		for _, item := range originList {
+			sum = 0
+			for k, v := range item.DataList {
+				sum += v.Value
+				val := sum / float64(k+1)
+				newList = append(newList, &data_manage.EdbDataList{
+					DataTime:      v.DataTime,
+					Value:         val,
+					DataTimestamp: v.DataTimestamp,
+				})
+			}
+		}
+	case 1: //累计值
+		var sum float64
+		for _, item := range originList {
+			sum = 0
+			for _, v := range item.DataList {
+				sum += v.Value
+				val := sum
+				newList = append(newList, &data_manage.EdbDataList{
+					DataTime:      v.DataTime,
+					Value:         val,
+					DataTimestamp: v.DataTimestamp,
+				})
+			}
+		}
+	case 2: //涨幅
+		for _, item := range originList {
+			var baseVal float64
+			for k, v := range item.DataList {
+				if k == 0 {
+					baseVal = v.Value
+					if baseVal == 0 {
+						break
+					}
+				}
+				val := (v.Value - baseVal) / baseVal
+				newList = append(newList, &data_manage.EdbDataList{
+					DataTime:      v.DataTime,
+					Value:         val,
+					DataTimestamp: v.DataTimestamp,
+				})
+			}
+		}
+	case 3: //复合增长率
+		for _, item := range originList {
+			var baseVal float64
+			var baseDate string
+			for k, v := range item.DataList {
+				if k == 0 {
+					baseVal = v.Value
+					baseDate = v.DataTime
+					if baseVal == 0 {
+						break
+					}
+					continue
+				}
+				baseDateT, e := time.ParseInLocation(utils.FormatDate, baseDate, time.Local)
+				if e != nil {
+					err = fmt.Errorf("time.ParseInLocation err: %v", e)
+					return
+				}
+				tmpT, e := time.ParseInLocation(utils.FormatDate, baseDate, time.Local)
+				if e != nil {
+					err = fmt.Errorf("time.ParseInLocation err: %v", e)
+					return
+				}
+				// 计算两个日期相差的天数
+				diff := tmpT.Sub(baseDateT).Hours() / 24
+				val := v.Value / baseVal
+				val = math.Pow(val, diff) - 1
+				newList = append(newList, &data_manage.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+			}
+		}
+	case 4: //最大值
+		var maxVal float64
+		for _, item := range originList {
+			for k, v := range item.DataList {
+				if k == 0 {
+					maxVal = v.Value
+				}
+				if v.Value > maxVal {
+					maxVal = v.Value
+				}
+				val := maxVal
+				newList = append(newList, &data_manage.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+			}
+		}
+	case 5: //最小值
+		var minVal float64
+		for _, item := range originList {
+			for k, v := range item.DataList {
+				if k == 0 {
+					minVal = v.Value
+				}
+				if v.Value < minVal {
+					minVal = v.Value
+				}
+				val := minVal
+				newList = append(newList, &data_manage.EdbDataList{DataTime: v.DataTime, Value: val, DataTimestamp: v.DataTimestamp})
+			}
+		}
+	}
+
+	return
+}
+
+// MoveDataDaysToNewDataList 平移指标数据生成新的数据序列
+func MoveDataDaysToNewDataList(dataList []*data_manage.EdbDataList, moveDay int) (newDataList []data_manage.EdbDataList, dateDataMap map[string]float64) {
+	dateMap := make(map[time.Time]float64)
+	var minDate, maxDate time.Time
+	dateDataMap = make(map[string]float64)
+
+	for _, v := range dataList {
+		currDate, _ := time.ParseInLocation(utils.FormatDate, v.DataTime, time.Local)
+		if minDate.IsZero() || currDate.Before(minDate) {
+			minDate = currDate
+		}
+		if maxDate.IsZero() || currDate.After(maxDate) {
+			maxDate = currDate
+		}
+		dateMap[currDate] = v.Value
+	}
+
+	// 处理领先、滞后数据
+	newDateMap := make(map[time.Time]float64)
+	for currDate, value := range dateMap {
+		newDate := currDate.AddDate(0, 0, moveDay)
+		newDateMap[newDate] = value
+	}
+	minDate = minDate.AddDate(0, 0, moveDay)
+	maxDate = maxDate.AddDate(0, 0, moveDay)
+
+	// 获取日期相差日
+	dayNum := utils.GetTimeSubDay(minDate, maxDate)
+
+	for i := 0; i <= dayNum; i++ {
+		currDate := minDate.AddDate(0, 0, i)
+		tmpValue, ok := newDateMap[currDate]
+		if !ok {
+			//找不到数据,那么就用前面的数据吧
+			if len(newDataList)-1 < 0 {
+				tmpValue = 0
+			} else {
+				tmpValue = newDataList[len(newDataList)-1].Value
+			}
+		}
+		tmpData := data_manage.EdbDataList{
+			DataTime: currDate.Format(utils.FormatDate),
+			Value:    tmpValue,
+		}
+		dateDataMap[tmpData.DataTime] = tmpData.Value
+		newDataList = append(newDataList, tmpData)
+	}
+	return
+}
+
+// GetChartEdbInfoFormat 区间计算图表-获取指标信息
+func GetChartEdbInfoFormat(chartInfoId int, edbInfoMappingList []*data_manage.ChartEdbInfoMapping) (edbList []*data_manage.ChartEdbInfoMapping, err error) {
+	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
+	for _, edbInfoMapping := range edbInfoMappingList {
+		if edbInfoMapping == nil {
+			err = fmt.Errorf("指标信息有误")
+			return
+		}
+
+		edbInfoMapping.FrequencyEn = data.GetFrequencyEn(edbInfoMapping.Frequency)
+		if edbInfoMapping.Unit == `无` {
+			edbInfoMapping.Unit = ``
+		}
+		if chartInfoId <= 0 {
+			edbInfoMapping.IsAxis = 1
+			edbInfoMapping.LeadValue = 0
+			edbInfoMapping.LeadUnit = ""
+			edbInfoMapping.ChartEdbMappingId = 0
+			edbInfoMapping.ChartInfoId = 0
+			edbInfoMapping.IsOrder = false
+			edbInfoMapping.EdbInfoType = 1
+			edbInfoMapping.ChartStyle = ""
+			edbInfoMapping.ChartColor = ""
+			edbInfoMapping.ChartWidth = 0
+		} else {
+			edbInfoMapping.LeadUnitEn = data.GetLeadUnitEn(edbInfoMapping.LeadUnit)
+		}
+		edbList = append(edbList, edbInfoMapping)
+	}
+	return
+}
+
+// GetChartDataByEdbInfoList 区间计算图表-根据指标信息获取x轴和y轴
+func GetChartDataByEdbInfoList(chartInfoId int, startDate, endDate string, edbInfoMappingList []*data_manage.ChartEdbInfoMapping, req *data_manage.ChartRangeAnalysisExtraConf) (edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, dataResp data_manage.ChartRangeAnalysisDataResp, err error) {
+	if chartInfoId > 0 && req.EdbInfoMode == 1 {
+		edbList, xEdbIdValue, dataResp, err = GetChartDataByEdbInfoListBySeries(chartInfoId, startDate, endDate, edbInfoMappingList, req)
+		return
+	}
+	for _, edbInfoMapping := range edbInfoMappingList {
+		edbInfoMapping, err = getChartDataByEdbInfo(edbInfoMapping, req)
+		if err != nil {
+			return
+		}
+		xEdbIdValue = append(xEdbIdValue, edbInfoMapping.EdbInfoId)
+	}
+
+	dataResp = data_manage.ChartRangeAnalysisDataResp{ChartRangeAnalysisExtraConf: req}
+	edbList, err = GetChartEdbInfoFormat(chartInfoId, edbInfoMappingList)
+	if err != nil {
+		err = fmt.Errorf("获取区间计算图表, 完善指标信息失败, Err:" + err.Error())
+		return
+	}
+	return
+}
+
+func GetChartDataByEdbInfoListBySeries(chartInfoId int, startDate, endDate string, edbInfoMappingList []*data_manage.ChartEdbInfoMapping, req *data_manage.ChartRangeAnalysisExtraConf) (edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, dataResp data_manage.ChartRangeAnalysisDataResp, err error) {
+	//查询seriesId
+	chartSeriesOb := new(data_manage.FactorEdbSeriesChartMapping)
+	seriesMappingItem, err := chartSeriesOb.GetItemByChartInfoId(chartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("图表关联关系不存在")
+			return
+		} else {
+			err = fmt.Errorf("获取图表关联失败, Err: " + err.Error())
+			return
+		}
+	}
+	//根据seriesId查询数据
+	//并把数据放到dataList中
+	for _, edbInfoMapping := range edbInfoMappingList {
+		dataOb := new(data_manage.FactorEdbSeriesCalculateDataQjjs)
+		dataList, e := dataOb.GetEdbDataList(seriesMappingItem.FactorEdbSeriesId, edbInfoMapping.EdbInfoId, startDate, endDate)
+		if e != nil {
+			err = e
+			return
+		}
+		edbInfoMapping.DataList = dataList
+		xEdbIdValue = append(xEdbIdValue, edbInfoMapping.EdbInfoId)
+	}
+
+	dataResp = data_manage.ChartRangeAnalysisDataResp{ChartRangeAnalysisExtraConf: req}
+	edbList, err = GetChartEdbInfoFormat(chartInfoId, edbInfoMappingList)
+	if err != nil {
+		err = fmt.Errorf("获取区间计算图表, 完善指标信息失败, Err:" + err.Error())
+		return
+	}
+	return
+}
+
+// getChartDataByEdbInfo 区间计算图表-根据指标信息获取x轴和y轴
+func getChartDataByEdbInfo(edbInfoMapping *data_manage.ChartEdbInfoMapping, req *data_manage.ChartRangeAnalysisExtraConf) (newEdbInfoMapping *data_manage.ChartEdbInfoMapping, err error) {
+	newEdbInfoMapping = edbInfoMapping
+	// 指标的开始日期和结束日期
+	edbStartDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfoMapping.StartDate, time.Local)
+	//edbStartDate := edbStartDateTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+	edbEndDateTime, _ := time.ParseInLocation(utils.FormatDate, edbInfoMapping.EndDate, time.Local)
+	edbEndDate := edbEndDateTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+
+	// 获取时间基准指标在时间区间内的值
+	dataList := make([]*data_manage.EdbDataList, 0)
+	switch edbInfoMapping.EdbInfoCategoryType {
+	case 0:
+		dataList, err = data_manage.GetEdbDataList(edbInfoMapping.Source, edbInfoMapping.SubSource, edbInfoMapping.EdbInfoId, "", "")
+	case 1:
+		_, dataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(edbInfoMapping.EdbInfoId, "", "", false)
+	default:
+		err = errors.New("指标base类型异常")
+		return
+	}
+
+	dateList := make([]*data_manage.ChartRangeAnalysisDateDataItem, 0)
+	switch req.DateRangeType {
+	case 0:
+		// 智能划分得到一个开始日期,和结束日期
+		var startDateTime, endDateTime time.Time
+		if req.AutoDateConf.IsAutoStartDate == 0 { //固定设置
+			startDate := req.AutoDateConf.StartDate
+			if startDate == "" {
+				startDate = "2020-01-01"
+			}
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+			endDateTime = edbEndDateTime
+		} else {
+			startConf := req.AutoDateConf.StartDateConf
+			endConf := req.AutoDateConf.EndDateConf
+			startDate := ""
+			endDate := ""
+			if startConf.BaseDateType == 0 { //
+				startDate = edbEndDate
+			} else if startConf.BaseDateType == 1 {
+				startDate = time.Now().Format(utils.FormatDate)
+			}
+			if startConf.MoveForward > 0 {
+				startDate = GetEdbDateByMoveForward(startConf.MoveForward, dataList)
+			}
+			if len(startConf.DateChange) > 0 {
+				startDate, err = HandleEdbDateChange(startDate, startConf.DateChange)
+				if err != nil {
+					err = fmt.Errorf("智能划分开始日期处理失败:%s", err.Error())
+					return
+				}
+			}
+
+			if endConf.BaseDateType == 0 { //
+				endDate = edbEndDate
+			} else if endConf.BaseDateType == 2 {
+				endDate = "2020-01-01"
+			}
+			if endConf.MoveForward > 0 {
+				endDate = GetEdbDateByMoveForward(endConf.MoveForward, dataList)
+			}
+			if len(endConf.DateChange) > 0 {
+				endDate, err = HandleEdbDateChange(endDate, endConf.DateChange)
+				if err != nil {
+					err = fmt.Errorf("智能划分结束日期处理失败:%s", err.Error())
+					return
+				}
+			}
+
+			startDateTime, _ = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+			endDateTime, _ = time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+		}
+		dateList = append(dateList, &data_manage.ChartRangeAnalysisDateDataItem{
+			StartDate: startDateTime,
+			EndDate:   endDateTime})
+	case 1:
+		// 手工划分得到多个开始日期和结束日期(已排序)
+		for _, v := range req.ManualDateConf {
+			startDateT, _ := time.ParseInLocation(utils.FormatDate, v.StartDate, time.Local)
+			endDateT, _ := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+			tmp := &data_manage.ChartRangeAnalysisDateDataItem{
+				StartDate: startDateT,
+				EndDate:   endDateT,
+			}
+			dateList = append(dateList, tmp)
+		}
+	case 2:
+		// 跨年划分得到多个开始日期和结束日期
+		startYear := edbStartDateTime.Year()
+		endYear := edbEndDateTime.Year()
+		startDay := req.YearDateConf.StartDay
+		endDay := req.YearDateConf.EndDay
+		for year := startYear; year <= endYear; year++ {
+			startDate := fmt.Sprintf("%d-%s", year, startDay)
+			endDate := fmt.Sprintf("%d-%s", year+1, endDay)
+			startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+			endDateTime, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+			if startDateTime.Before(edbStartDateTime) {
+				break
+			}
+
+			tmp := &data_manage.ChartRangeAnalysisDateDataItem{
+				StartDate: startDateTime,
+				EndDate:   endDateTime,
+			}
+			dateList = append(dateList, tmp)
+		}
+	}
+	// 根据日期,获取数据
+	for _, v := range dateList {
+		for _, vv := range dataList {
+			dataTimeT, _ := time.ParseInLocation(utils.FormatDate, vv.DataTime, time.Local)
+			if dataTimeT.After(v.StartDate) && dataTimeT.Before(v.EndDate) ||
+				dataTimeT.Equal(v.StartDate) ||
+				dataTimeT.Equal(v.EndDate) {
+				v.DataList = append(v.DataList, vv)
+			}
+		}
+	}
+	// 根据时间区间类型来获取数据的计算窗口,然后再拼接成整段数据
+	newDataList, err := HandleDataByCalculateType(dateList, req.CalculateType)
+	if err != nil {
+		return
+	}
+	if req.UnNormalDataDealType > 0 {
+		// todo 异常数据处理
+	}
+
+	if req.DataConvertType > 0 {
+		// todo 数据转换
+	}
+	newEdbInfoMapping.DataList = newDataList
+	return
+}
+
+// RollingCorrelationChartDataResp 滚动区间计算图表数据
+type RollingCorrelationChartDataResp struct {
+	MaxData             float64
+	MinData             float64
+	LatestDate          string `description:"真实数据的最后日期"`
+	EdbInfoCategoryType int
+	ChartColor          string
+	ChartStyle          string
+	PredictChartColor   string
+	ChartType           int
+	ChartWidth          int
+	EdbName             string
+	EdbNameEn           string
+	Unit                string
+	UnitEn              string
+	IsAxis              int
+	DataList            []data_manage.EdbDataList
+}
+
+// ChartInfoRefresh 图表刷新
+func ChartInfoRefresh(chartInfoId int, uniqueCode string) (isAsync bool, err error) {
+	var errMsg string
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("CorrelationChartInfoRefresh: %s", errMsg)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+	}()
+	var chartInfo *data_manage.ChartInfo
+	if chartInfoId > 0 {
+		chartInfo, err = data_manage.GetChartInfoById(chartInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "图表已被删除,请刷新页面"
+				err = errors.New(errMsg)
+				return
+			}
+			errMsg = "获取图表信息失败"
+			err = errors.New("获取图表信息失败,Err:" + err.Error())
+			return
+		}
+	} else {
+		chartInfo, err = data_manage.GetChartInfoByUniqueCode(uniqueCode)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "图表已被删除,请刷新页面"
+				err = errors.New(errMsg)
+				return
+			}
+			errMsg = "获取图表信息失败"
+			err = errors.New("获取图表信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	// 1.刷新图表关联的指标
+	mappings, e := data_manage.GetChartEdbMappingList(chartInfoId)
+	if e != nil {
+		utils.FileLog.Info(fmt.Sprintf("获取图表关联指标失败, err: %v", e))
+		return
+	}
+	if len(mappings) == 0 {
+		utils.FileLog.Info("图表无关联指标")
+		return
+	}
+	var edbIds []int
+	for _, v := range mappings {
+		edbIds = append(edbIds, v.EdbInfoId)
+	}
+	if e, _ = data.EdbInfoRefreshAllFromBaseV3(edbIds, false, true, false); e != nil {
+		utils.FileLog.Info(fmt.Sprintf("批量刷新指标失败, err: %v", e))
+		return
+	}
+
+	// todo 重新计算
+	// 区间计算图表配置校验
+	var extraConfig data_manage.ChartRangeAnalysisExtraConf
+	err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConfig)
+	if err != nil {
+		errMsg = "配置信息错误"
+		err = errors.New(errMsg + ", Err: " + err.Error())
+		return
+	}
+	chartSeriesOb := new(data_manage.FactorEdbSeriesChartMapping)
+	seriesMappingItem, err := chartSeriesOb.GetItemByChartInfoId(chartInfo.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		} else {
+			err = fmt.Errorf("获取图表关联失败, Err: " + err.Error())
+			return
+		}
+	} else {
+		_, e = FactorEdbStepCalculateRange(seriesMappingItem.FactorEdbSeriesId, edbIds, extraConfig, true)
+		if e != nil {
+			err = fmt.Errorf("计算因子指标失败, Err: " + e.Error())
+			return
+		}
+	}
+	// 4.清除图表缓存
+	key := utils.HZ_CHART_LIB_DETAIL + uniqueCode
+	_ = utils.Rc.Delete(key)
+
+	return
+}
+
+// AddChartInfo 添加图表
+func AddChartInfo(req data_manage.AddChartInfoReq, source int, sysUser *system.Admin, lang string) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+
+	req.ChartName = strings.Trim(req.ChartName, " ")
+	if req.ChartName == "" {
+		errMsg = "请填写图表名称!"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	if req.ChartClassifyId <= 0 {
+		errMsg = "分类参数错误!"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	// 区间计算图表配置校验
+	var extraConfig data_manage.ChartRangeAnalysisExtraConf
+	if req.ExtraConfig == `` {
+		errMsg = "配置信息错误"
+		err = errors.New(errMsg)
+		return
+	}
+	err = json.Unmarshal([]byte(req.ExtraConfig), &extraConfig)
+	if err != nil {
+		errMsg = "配置信息错误"
+		err = errors.New(errMsg + ", Err: " + err.Error())
+		return
+	}
+
+	if len(req.ChartEdbInfoList) <= 0 {
+		errMsg = "请选择指标"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	extraConfig.SeriesName = strings.TrimSpace(extraConfig.SeriesName)
+	if extraConfig.SeriesName == "" && extraConfig.EdbInfoMode == 1 {
+		errMsg = "请输入指标系列名称"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	if len(req.ChartEdbInfoList) > 100 {
+		errMsg = "添加指标总数量不得超过100"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	if extraConfig.CalculateType > 5 || extraConfig.CalculateType < 0 {
+		errMsg = "计算方式参数错误"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	//todo 校验时间区间设置
+	chartClassify, err := data_manage.GetChartClassifyById(req.ChartClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "分类不存在"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+		errMsg = "获取分类信息失败"
+		err = errors.New("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	if chartClassify == nil {
+		errMsg = "分类不存在"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	var edbInfoIdArr []int
+
+	for _, v := range req.ChartEdbInfoList {
+		edbInfoId := v.EdbInfoId
+		edbInfo, tmpErr := data_manage.GetEdbInfoById(edbInfoId)
+		if tmpErr != nil {
+			if tmpErr.Error() == utils.ErrNoRow() {
+				errMsg = "指标不存在!"
+				err = errors.New("指标不存在,edbInfoId:" + strconv.Itoa(edbInfoId))
+				return
+			} else {
+				errMsg = "获取指标信息失败!"
+				err = errors.New("获取图表的指标信息失败,Err:" + tmpErr.Error())
+				return
+			}
+		}
+
+		if edbInfo == nil {
+			errMsg = "指标已被删除,请重新选择!"
+			err = errors.New("指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoId))
+			return
+		} else {
+			if edbInfo.EdbInfoId <= 0 {
+				errMsg = "指标已被删除,请重新选择!"
+				err = errors.New("指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoId))
+				return
+			}
+		}
+
+		edbInfoIdArr = append(edbInfoIdArr, edbInfoId)
+		edbInfo.EdbNameSource = edbInfo.EdbName
+	}
+
+	sort.Ints(edbInfoIdArr)
+	var edbInfoIdArrStr []string
+	for _, v := range edbInfoIdArr {
+		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v))
+	}
+	edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")
+	var chartInfoId int
+
+	// 判断图表是否存在
+	{
+		var condition string
+		var pars []interface{}
+
+		switch lang {
+		case utils.EnLangVersion:
+			condition += " AND chart_name_en = ? AND source = ? "
+		default:
+			condition += " AND chart_name=? AND source = ? "
+		}
+		pars = append(pars, req.ChartName, source)
+		count, tmpErr := data_manage.GetChartInfoCountByCondition(condition, pars)
+		if tmpErr != nil {
+			errMsg = "判断图表名称是否存在失败"
+			err = errors.New("判断图表名称是否存在失败,Err:" + tmpErr.Error())
+			return
+		}
+
+		if count > 0 {
+			errMsg = "图表已存在,请重新填写"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+	}
+
+	disableVal := data.CheckIsDisableChart(edbInfoIdArr)
+
+	chartInfo = new(data_manage.ChartInfo)
+	chartInfo.ChartName = req.ChartName
+	chartInfo.ChartNameEn = req.ChartName
+	chartInfo.EdbInfoIds = edbInfoIdStr
+	chartInfo.ChartClassifyId = req.ChartClassifyId
+	chartInfo.SysUserId = sysUser.AdminId
+	chartInfo.SysUserRealName = sysUser.RealName
+	chartInfo.CreateTime = time.Now()
+	chartInfo.ModifyTime = time.Now()
+	chartInfo.IsSetName = 0
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
+	chartInfo.ChartType = 9 // 区间计算图
+	chartInfo.Calendar = "公历"
+	chartInfo.DateType = 6
+	chartInfo.StartDate = req.StartDate
+	chartInfo.EndDate = req.EndDate
+	chartInfo.SeasonStartDate = req.StartDate
+	chartInfo.SeasonEndDate = req.EndDate
+	chartInfo.LeftMin = req.LeftMin
+	chartInfo.LeftMax = req.LeftMax
+	chartInfo.RightMin = req.RightMin
+	chartInfo.RightMax = req.RightMax
+	chartInfo.Disabled = disableVal
+	chartInfo.Source = source
+	chartInfo.ChartThemeId = req.ChartThemeId
+	chartInfo.SourcesFrom = req.SourcesFrom
+	chartInfo.Instructions = req.Instructions
+	chartInfo.MarkersLines = req.MarkersLines
+	chartInfo.MarkersAreas = req.MarkersAreas
+	if req.ExtraConfig != "" {
+		chartInfo.ExtraConfig = req.ExtraConfig
+	}
+
+	// 指标信息
+	mapList := make([]*data_manage.ChartEdbMapping, 0)
+	for _, v := range req.ChartEdbInfoList {
+		// todo 指标名称修改
+		mapItem := new(data_manage.ChartEdbMapping)
+		mapItem.EdbInfoId = v.EdbInfoId
+		mapItem.CreateTime = time.Now()
+		mapItem.ModifyTime = time.Now()
+		edbTimestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+		mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + edbTimestamp + "_" + strconv.Itoa(v.EdbInfoId))
+		mapItem.IsOrder = true
+		mapItem.IsAxis = 1
+		mapItem.EdbInfoType = 1
+		mapItem.Source = utils.CHART_SOURCE_RANGE_ANALYSIS
+		mapList = append(mapList, mapItem)
+
+	}
+
+	// 新增图表和指标mapping
+	chartInfoId, e := data_manage.CreateRangeChartAndEdb(chartInfo, mapList)
+	if e != nil {
+		errMsg = "操作失败"
+		err = errors.New("新增区间计算图表失败, Err: " + e.Error())
+		return
+	}
+	// 把系列和图表绑定
+	if extraConfig.EdbInfoMode == 1 {
+		// 新增指标系列
+		err = AddSeries(edbInfoIdArr, chartInfoId, utils.CHART_SOURCE_RANGE_ANALYSIS, extraConfig, req.ExtraConfig)
+		if err != nil {
+			errMsg = "操作失败"
+			err = errors.New("新增区间计算图表失败, Err: " + err.Error())
+			return
+		}
+		//todo 如果保存失败是否要删除
+	}
+
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartInfo)
+	//添加es数据
+	go data.EsAddOrEditChartInfo(chartInfoId)
+
+	return
+}
+
+// EditChartInfo 编辑图表
+func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang string) (chartItem *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+
+	chartItem, err = data_manage.GetChartInfoById(req.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "图表已被删除,请刷新页面"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+		errMsg = "获取图表信息失败"
+		err = errors.New("获取图表信息失败,Err:" + err.Error())
+		return
+	}
+
+	if chartItem.Source != utils.CHART_SOURCE_RANGE_ANALYSIS {
+		errMsg = "该图不是区间计算图表!"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	req.ChartName = strings.Trim(req.ChartName, " ")
+	if req.ChartClassifyId <= 0 {
+		errMsg = "分类参数错误!"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	// 区间计算图表配置校验
+	var extraConfig data_manage.ChartRangeAnalysisExtraConf
+	if req.ExtraConfig == `` {
+		errMsg = "配置信息错误"
+		err = errors.New(errMsg)
+		return
+	}
+	err = json.Unmarshal([]byte(req.ExtraConfig), &extraConfig)
+	if err != nil {
+		errMsg = "配置信息错误"
+		err = errors.New(errMsg)
+		return
+	}
+
+	if len(req.ChartEdbInfoList) <= 0 {
+		errMsg = "请选择指标"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	extraConfig.SeriesName = strings.TrimSpace(extraConfig.SeriesName)
+	if extraConfig.SeriesName == "" && extraConfig.EdbInfoMode == 1 {
+		errMsg = "请输入指标系列名称"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+	if len(req.ChartEdbInfoList) > 100 {
+		errMsg = "添加指标总数量不得超过100"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	if extraConfig.CalculateType > 5 || extraConfig.CalculateType < 0 {
+		errMsg = "计算方式参数错误"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	chartClassify, err := data_manage.GetChartClassifyById(req.ChartClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "分类不存在"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+		errMsg = "获取分类信息失败"
+		err = errors.New("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	if chartClassify == nil {
+		errMsg = "分类不存在"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	// 图表操作权限
+	ok := data.CheckOpChartPermission(sysUser, chartItem.SysUserId, true)
+	if !ok {
+		errMsg = "没有该图表的操作权限"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	var edbInfoIdArr []int
+	for _, v := range req.ChartEdbInfoList {
+		edbInfoId := v.EdbInfoId
+		edbInfo, tmpErr := data_manage.GetEdbInfoById(edbInfoId)
+		if tmpErr != nil {
+			if tmpErr.Error() == utils.ErrNoRow() {
+				errMsg = "图表不存在!"
+				err = errors.New("图表指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoId))
+				return
+			} else {
+				errMsg = "获取图表信息失败!"
+				err = errors.New("获取图表的指标信息失败,Err:" + tmpErr.Error())
+				return
+			}
+		}
+
+		if edbInfo == nil {
+			errMsg = "指标不存在!"
+			err = errors.New("指标不存在,ChartInfoId:" + strconv.Itoa(edbInfoId))
+			return
+		}
+		edbInfoIdArr = append(edbInfoIdArr, edbInfoId)
+	}
+
+	sort.Ints(edbInfoIdArr)
+	var edbInfoIdArrStr []string
+	for _, v := range edbInfoIdArr {
+		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v))
+	}
+	edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")
+
+	//判断图表是否存在
+	{
+		var condition string
+		var pars []interface{}
+		condition += " AND chart_info_id <> ? "
+		pars = append(pars, req.ChartInfoId)
+		switch lang {
+		case utils.EnLangVersion:
+			condition += " AND chart_name_en = ? AND source = ? "
+		default:
+			condition += " AND chart_name=? AND source = ? "
+		}
+		pars = append(pars, req.ChartName, chartItem.Source)
+		count, tmpErr := data_manage.GetChartInfoCountByCondition(condition, pars)
+		if tmpErr != nil {
+			errMsg = "判断图表名称是否存在失败"
+			err = errors.New("判断图表名称是否存在失败,Err:" + tmpErr.Error())
+			return
+		}
+		if count > 0 {
+			errMsg = "图表已存在,请重新填写"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+	}
+	// 修改图表与指标mapping
+	req.ChartType = 1
+	dateType := req.DateType
+	err = data_manage.EditRangeChartInfoAndMapping(&req, edbInfoIdStr, lang, "公历", dateType, 0, ``)
+	if err != nil {
+		errMsg = "保存失败"
+		err = errors.New("保存失败,Err:" + err.Error())
+		return
+	}
+
+	addSeries := false
+	chartSeriesOb := new(data_manage.FactorEdbSeriesChartMapping)
+	seriesMappingItem, err := chartSeriesOb.GetItemByChartInfoId(chartItem.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+			addSeries = true
+		} else {
+			err = fmt.Errorf("获取图表关联失败, Err: " + err.Error())
+			return
+		}
+	}
+	if extraConfig.EdbInfoMode == 1 {
+		if addSeries {
+			// 新增指标系列
+			err = AddSeries(edbInfoIdArr, chartItem.ChartInfoId, utils.CHART_SOURCE_RANGE_ANALYSIS, extraConfig, req.ExtraConfig)
+			if err != nil {
+				errMsg = "操作失败"
+				err = errors.New("新增区间计算图表失败, Err: " + err.Error())
+				return
+			}
+		} else {
+			err = EditSeries(seriesMappingItem, edbInfoIdArr, extraConfig, req.ExtraConfig, true)
+			if err != nil {
+				errMsg = "保存失败"
+				err = errors.New("保存失败,Err:" + err.Error())
+				return
+			}
+		}
+		// todo 编辑失败处理
+	} else if extraConfig.EdbInfoMode == 0 {
+		if !addSeries {
+			// 删除相关的系列
+			factorSeriesOb := new(data_manage.FactorEdbSeries)
+			err = factorSeriesOb.RemoveSeriesAndMappingByFactorEdbSeriesId(seriesMappingItem)
+			if err != nil {
+				errMsg = "保存失败"
+				err = errors.New("保存失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+	resp := new(data_manage.AddChartInfoResp)
+	resp.ChartInfoId = chartItem.ChartInfoId
+	resp.UniqueCode = chartItem.UniqueCode
+	resp.ChartType = req.ChartType
+
+	// 添加指标引用记录
+	_ = data.SaveChartEdbInfoRelation(edbInfoIdArr, chartItem)
+	//添加es数据
+	go data.EsAddOrEditChartInfo(chartItem.ChartInfoId)
+	//修改my eta es数据
+	go data.EsAddOrEditMyChartInfoByChartInfoId(chartItem.ChartInfoId)
+
+	return
+}
+
+// CopyChartInfo 复制图表
+func CopyChartInfo(classifyId int, chartName string, oldChartInfo *data_manage.ChartInfo, sysUser *system.Admin, lang string) (chartInfo *data_manage.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	chartInfo = &data_manage.ChartInfo{
+		ChartInfoId:     0,
+		ChartName:       chartName,
+		ChartClassifyId: classifyId,
+		SysUserId:       sysUser.AdminId,
+		SysUserRealName: sysUser.RealName,
+		UniqueCode:      utils.MD5(utils.CHART_PREFIX + "_" + timestamp),
+		CreateTime:      time.Now(),
+		ModifyTime:      time.Now(),
+		DateType:        oldChartInfo.DateType,
+		StartDate:       oldChartInfo.StartDate,
+		EndDate:         oldChartInfo.EndDate,
+		IsSetName:       oldChartInfo.IsSetName,
+		EdbInfoIds:      oldChartInfo.EdbInfoIds,
+		ChartType:       oldChartInfo.ChartType,
+		Calendar:        oldChartInfo.Calendar,
+		SeasonStartDate: oldChartInfo.SeasonStartDate,
+		SeasonEndDate:   oldChartInfo.SeasonEndDate,
+		ChartImage:      oldChartInfo.ChartImage,
+		BarConfig:       oldChartInfo.BarConfig,
+		//Sort:     sort,
+		LeftMin:           oldChartInfo.LeftMin,
+		LeftMax:           oldChartInfo.LeftMax,
+		RightMin:          oldChartInfo.RightMin,
+		RightMax:          oldChartInfo.RightMax,
+		Right2Min:         oldChartInfo.Right2Min,
+		Right2Max:         oldChartInfo.Right2Max,
+		Disabled:          oldChartInfo.Disabled,
+		Source:            oldChartInfo.Source,
+		ExtraConfig:       oldChartInfo.ExtraConfig,
+		SeasonExtraConfig: oldChartInfo.SeasonExtraConfig,
+		StartYear:         oldChartInfo.StartYear,
+		Unit:              oldChartInfo.Unit,
+		UnitEn:            oldChartInfo.UnitEn,
+		ChartThemeId:      oldChartInfo.ChartThemeId,
+		SourcesFrom:       oldChartInfo.SourcesFrom,
+		Instructions:      oldChartInfo.Instructions,
+		MarkersLines:      oldChartInfo.MarkersLines,
+		MarkersAreas:      oldChartInfo.MarkersAreas,
+	}
+	newId, err := data_manage.AddChartInfo(chartInfo)
+	if err != nil {
+		err = fmt.Errorf("保存失败,Err:%s", err.Error())
+		return
+	}
+	chartInfo.ChartInfoId = int(newId)
+	edbInfoMappingList, err := data_manage.GetChartEdbMappingList(oldChartInfo.ChartInfoId)
+	if err != nil {
+		err = fmt.Errorf("获取图表,指标信息失败,Err:" + err.Error())
+		return
+	}
+	// 添加图表与指标的关联关系
+	edbInfoIdArr := make([]int, 0)
+	{
+		mapList := make([]*data_manage.ChartEdbMapping, 0)
+		for _, v := range edbInfoMappingList {
+			edbInfoIdArr = append(edbInfoIdArr, v.EdbInfoId)
+			timestamp = strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem := &data_manage.ChartEdbMapping{
+				//ChartEdbMappingId: 0,
+				ChartInfoId:   chartInfo.ChartInfoId,
+				EdbInfoId:     v.EdbInfoId,
+				CreateTime:    time.Now(),
+				ModifyTime:    time.Now(),
+				UniqueCode:    utils.MD5(utils.CHART_PREFIX + "_" + timestamp),
+				MaxData:       v.MaxData,
+				MinData:       v.MinData,
+				IsOrder:       v.IsOrder,
+				IsAxis:        v.IsAxis,
+				EdbInfoType:   v.EdbInfoType,
+				LeadValue:     v.LeadValue,
+				LeadUnit:      v.LeadUnit,
+				ChartStyle:    v.ChartStyle,
+				ChartColor:    v.ChartColor,
+				ChartWidth:    v.ChartWidth,
+				Source:        v.Source,
+				EdbAliasName:  v.EdbAliasName,
+				IsConvert:     v.IsConvert,
+				ConvertType:   v.ConvertType,
+				ConvertValue:  v.ConvertValue,
+				ConvertUnit:   v.ConvertEnUnit,
+				ConvertEnUnit: v.ConvertEnUnit,
+			}
+			mapList = append(mapList, mapItem)
+		}
+		err = data_manage.AddChartEdbMapping(mapList)
+		if err != nil {
+			err = fmt.Errorf("保存失败,Err:%s", err.Error())
+			return
+		}
+	}
+	// 添加系列和图表映射
+	chartSeriesOb := new(data_manage.FactorEdbSeriesChartMapping)
+	_, err = chartSeriesOb.GetItemByChartInfoId(oldChartInfo.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		} else {
+			err = fmt.Errorf("获取图表关联失败, Err: " + err.Error())
+			return
+		}
+	} else {
+		// 新增指标系列
+		// 区间计算图表配置校验
+		var extraConfig data_manage.ChartRangeAnalysisExtraConf
+		err = json.Unmarshal([]byte(chartInfo.ExtraConfig), &extraConfig)
+		if err != nil {
+			errMsg = "配置信息错误"
+			err = errors.New(errMsg + ", Err: " + err.Error())
+			return
+		}
+		err = AddSeries(edbInfoIdArr, chartInfo.ChartInfoId, utils.CHART_SOURCE_RANGE_ANALYSIS, extraConfig, chartInfo.ExtraConfig)
+		if err != nil {
+			errMsg = "操作失败"
+			err = errors.New("新增区间计算图表失败, Err: " + err.Error())
+			return
+		}
+	}
+
+	//添加es数据
+	go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId)
+
+	return
+}
+
+/*
+// CalculateCorrelation 计算区间计算-获取x轴和y轴
+func CalculateCorrelation(leadValue int, leadUnit, frequencyA, frequencyB string, dataListA, dataListB []*data_manage.EdbDataList) (xEdbIdValue []int, yDataList []data_manage.YData, err error) {
+	xData := make([]int, 0)
+	yData := make([]float64, 0)
+	if leadValue == 0 {
+		xData = append(xData, 0)
+	}
+	if leadValue > 0 {
+		leadMin := 0 - leadValue
+		xLen := 2*leadValue + 1
+		for i := 0; i < xLen; i++ {
+			n := leadMin + i
+			xData = append(xData, n)
+		}
+	}
+
+	// 计算窗口,不包含第一天
+	//startDateTime, _ := time.ParseInLocation(utils.FormatDate, startDate, time.Local)
+	//startDate = startDateTime.AddDate(0, 0, 1).Format(utils.FormatDate)
+
+	//// 2023-03-02 时间序列始终以指标B为基准, 始终是A进行平移
+	//baseEdbInfo := edbInfoMappingB
+	//changeEdbInfo := edbInfoMappingA
+	// 2023-03-17 时间序列始终以指标A为基准, 始终是B进行平移
+	//baseEdbInfo := edbInfoMappingA
+	//changeEdbInfo := edbInfoMappingB
+
+	// 获取时间基准指标在时间区间内的值
+	//aDataList := make([]*data_manage.EdbDataList, 0)
+	//switch baseEdbInfo.EdbInfoCategoryType {
+	//case 0:
+	//	aDataList, err = data_manage.GetEdbDataList(baseEdbInfo.Source, baseEdbInfo.SubSource, baseEdbInfo.EdbInfoId, startDate, endDate)
+	//case 1:
+	//	_, aDataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(baseEdbInfo.EdbInfoId, startDate, endDate, false)
+	//default:
+	//	err = errors.New("指标base类型异常")
+	//	return
+	//}
+	//
+	//// 获取变频指标所有日期的值, 插值法完善数据
+	//bDataList := make([]*data_manage.EdbDataList, 0)
+	//switch changeEdbInfo.EdbInfoCategoryType {
+	//case 0:
+	//	bDataList, err = data_manage.GetEdbDataList(changeEdbInfo.Source, changeEdbInfo.SubSource, changeEdbInfo.EdbInfoId, "", "")
+	//case 1:
+	//	_, bDataList, _, _, err, _ = data.GetPredictDataListByPredictEdbInfoId(changeEdbInfo.EdbInfoId, "", "", false)
+	//default:
+	//	err = errors.New("指标change类型异常")
+	//	return
+	//}
+	//changeDataMap := make(map[string]float64)
+	//newChangeDataList, e := HandleDataByLinearRegression(bDataList, changeDataMap)
+	//if e != nil {
+	//	err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
+	//	return
+	//}
+
+	// 2023-03-17 时间序列始终以指标A为基准, 始终是B进行平移
+	baseDataList := make([]*data_manage.EdbDataList, 0)
+	baseDataMap := make(map[string]float64)
+	changeDataList := make([]*data_manage.EdbDataList, 0)
+	changeDataMap := make(map[string]float64)
+
+	// 先把低频指标升频为高频
+	{
+		frequencyIntMap := map[string]int{
+			"日度": 1,
+			"周度": 2,
+			"旬度": 3,
+			"月度": 4,
+			"季度": 5,
+			"年度": 6,
+		}
+
+		// 如果A指标是高频,那么就需要对B指标进行升频
+		if frequencyIntMap[frequencyA] < frequencyIntMap[frequencyB] {
+			tmpNewChangeDataList, e := HandleDataByLinearRegression(dataListB, changeDataMap)
+			if e != nil {
+				err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
+				return
+			}
+			changeDataList = tmpNewChangeDataList
+			baseDataList = dataListA
+			for _, v := range baseDataList {
+				baseDataMap[v.DataTime] = v.Value
+			}
+
+		} else if frequencyIntMap[frequencyA] > frequencyIntMap[frequencyB] {
+			// 如果B指标是高频,那么就需要对A指标进行升频
+			tmpNewChangeDataList, e := HandleDataByLinearRegression(dataListA, baseDataMap)
+			if e != nil {
+				err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
+				return
+			}
+			baseDataList = tmpNewChangeDataList
+
+			changeDataList = dataListB
+			for _, v := range changeDataList {
+				changeDataMap[v.DataTime] = v.Value
+			}
+		} else {
+			baseDataList = dataListA
+			for _, v := range baseDataList {
+				baseDataMap[v.DataTime] = v.Value
+			}
+			changeDataList = dataListB
+			for _, v := range changeDataList {
+				changeDataMap[v.DataTime] = v.Value
+			}
+		}
+
+	}
+
+	// 计算不领先也不滞后时的相关系数
+	baseCalculateData := make([]float64, 0)
+	baseDataTimeArr := make([]string, 0)
+	for i := range baseDataList {
+		baseDataTimeArr = append(baseDataTimeArr, baseDataList[i].DataTime)
+		baseCalculateData = append(baseCalculateData, baseDataList[i].Value)
+	}
+
+	//zeroBaseData := make([]float64, 0)
+	//zeroCalculateData := make([]float64, 0)
+	//for i := range baseDataTimeArr {
+	//	tmpBaseVal, ok1 := baseDataMap[baseDataTimeArr[i]]
+	//	tmpCalculateVal, ok2 := changeDataMap[baseDataTimeArr[i]]
+	//	if ok1 && ok2 {
+	//		zeroBaseData = append(zeroBaseData, tmpBaseVal)
+	//		zeroCalculateData = append(zeroCalculateData, tmpCalculateVal)
+	//	}
+	//}
+	//if len(zeroBaseData) != len(zeroCalculateData) {
+	//	err = fmt.Errorf("相关系数两组序列元素数不一致, %d-%d", len(baseCalculateData), len(zeroCalculateData))
+	//	return
+	//}
+	//zeroRatio := utils.CalculateCorrelationByIntArr(zeroBaseData, zeroCalculateData)
+	//if leadValue == 0 {
+	//	yData = append(yData, zeroRatio)
+	//}
+
+	// 计算领先/滞后N期
+	if leadValue > 0 {
+		// 平移变频指标领先/滞后的日期(单位天)
+		moveUnitDays := utils.FrequencyDaysMap[leadUnit]
+
+		for i := range xData {
+			//if xData[i] == 0 {
+			//	yData = append(yData, zeroRatio)
+			//	continue
+			//}
+			xCalculateData := make([]float64, 0)
+			yCalculateData := make([]float64, 0)
+
+			// 平移指定天数
+			mDays := int(moveUnitDays) * xData[i]
+			_, dMap := MoveDataDaysToNewDataList(changeDataList, mDays)
+
+			// 取出对应的基准日期的值
+			for i2 := range baseDataTimeArr {
+				tmpDate := baseDataTimeArr[i2]
+				if yVal, ok := dMap[tmpDate]; ok {
+					xCalculateData = append(xCalculateData, baseCalculateData[i2])
+					yCalculateData = append(yCalculateData, yVal)
+				}
+			}
+			if len(yCalculateData) <= 0 {
+				//err = fmt.Errorf("领先滞后相关系数两组序列元素数不一致, %d-%d", len(baseCalculateData), len(yCalculateData))
+				//return
+				// 领先滞后后,没有可以计算的数据了
+				continue
+			}
+
+			// 公式计算出领先/滞后频度对应点的区间计算系数
+			ratio := utils.CalculateCorrelationByIntArr(xCalculateData, yCalculateData)
+			yData = append(yData, ratio)
+		}
+	}
+
+	xEdbIdValue = xData
+	yDataList = make([]data_manage.YData, 0)
+	yDate := "0000-00-00"
+	yDataList = append(yDataList, data_manage.YData{
+		Date:  yDate,
+		Value: yData,
+	})
+	return
+}
+
+// GetFactorChartDataByChartId 获取多因子区间计算图表数据
+func GetFactorChartDataByChartId(chartInfoId int, extraConfig string) (xEdbIdValue []int, yDataList []data_manage.YData, err error) {
+	if chartInfoId <= 0 {
+		return
+	}
+	// 指标对应的图例
+	extra := new(data_manage.CorrelationChartInfoExtraConfig)
+	if extraConfig != "" {
+		if e := json.Unmarshal([]byte(extraConfig), extra); e != nil {
+			err = fmt.Errorf("解析图表额外配置失败, err: %v", e)
+			return
+		}
+	}
+	legends := make(map[string]*data_manage.CorrelationChartLegend)
+	if extra != nil {
+		for _, v := range extra.LegendConfig {
+			s := fmt.Sprintf("%d-%d", v.SeriesId, v.EdbInfoId)
+			legends[s] = v
+		}
+	}
+
+	// 获取图表引用到的系列指标
+	chartMappingOb := new(data_manage.FactorEdbSeriesChartMapping)
+	cond := fmt.Sprintf(" AND %s = ? AND %s = 1", chartMappingOb.Cols().ChartInfoId, chartMappingOb.Cols().EdbUsed)
+	pars := make([]interface{}, 0)
+	pars = append(pars, chartInfoId)
+	chartMappings, e := chartMappingOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("获取图表引用系列指标失败")
+		return
+	}
+
+	// 取出计算结果
+	yDataList = make([]data_manage.YData, 0)
+	yDate := "0000-00-00"
+	for k, m := range chartMappings {
+		var values []data_manage.FactorEdbSeriesCorrelationMatrixValues
+		if m.CalculateData != "" {
+			e = json.Unmarshal([]byte(m.CalculateData), &values)
+			if e != nil {
+				err = fmt.Errorf("系列指标计算数据有误, err: %v", e)
+				return
+			}
+		}
+		var y []float64
+		for _, v := range values {
+			if k == 0 {
+				xEdbIdValue = append(xEdbIdValue, v.XData)
+			}
+			y = append(y, v.YData)
+		}
+		var yData data_manage.YData
+		yData.Date = yDate
+		yData.Value = y
+		yData.SeriesEdb.SeriesId = m.FactorEdbSeriesId
+		yData.SeriesEdb.EdbInfoId = m.EdbInfoId
+
+		// 图例
+		s := fmt.Sprintf("%d-%d", m.FactorEdbSeriesId, m.EdbInfoId)
+		legend := legends[s]
+		if legend != nil {
+			yData.Name = legend.LegendName
+			yData.Color = legend.Color
+		}
+		yDataList = append(yDataList, yData)
+	}
+	return
+}
+
+// FormatChartEdbInfoMappings 补充指标信息
+func FormatChartEdbInfoMappings(chartInfoId int, mappings []*data_manage.ChartEdbInfoMapping) (edbList []*data_manage.ChartEdbInfoMapping, err error) {
+	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
+	if len(mappings) == 0 {
+		return
+	}
+
+	for _, v := range mappings {
+		if chartInfoId <= 0 {
+			v.IsAxis = 1
+			v.LeadValue = 0
+			v.LeadUnit = ""
+			v.ChartEdbMappingId = 0
+			v.ChartInfoId = 0
+			v.IsOrder = false
+			v.EdbInfoType = 1
+			v.ChartStyle = ""
+			v.ChartColor = ""
+			v.ChartWidth = 0
+		} else {
+			v.LeadUnitEn = data.GetLeadUnitEn(v.LeadUnit)
+			v.LeadUnitEn = data.GetLeadUnitEn(v.LeadUnit)
+		}
+		v.FrequencyEn = data.GetFrequencyEn(v.Frequency)
+		if v.Unit == `无` {
+			v.Unit = ``
+		}
+		edbList = append(edbList, v)
+	}
+	return
+}*/
+
+func GetEdbDateByMoveForward(moveForward int, edbDataList []*data_manage.EdbDataList) (date string) {
+	moveForward = 0
+	// 根据日期进行排序
+	index := len(edbDataList) - 1 - moveForward
+	for k, v := range edbDataList {
+		if k == index {
+			date = v.DataTime
+			return
+		}
+	}
+	return
+}
+
+// HandleEdbDateChange 处理日期变换
+func HandleEdbDateChange(date string, dateChange []*data_manage.EdbDateConfDateChange) (newDate string, err error) {
+	newDate = date
+	if newDate != "" {
+		if len(dateChange) > 0 {
+			var dateTime time.Time
+			dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+			if err != nil {
+				err = fmt.Errorf("日期解析失败: %s", err.Error())
+				return
+			}
+			for _, v := range dateChange {
+				if v.ChangeType == 1 {
+					dateTime = dateTime.AddDate(v.Year, v.Month, v.Day)
+					newDate = dateTime.Format(utils.FormatDate)
+				} else if v.ChangeType == 2 {
+					newDate, err, _ = utils.HandleSystemAppointDateT(dateTime, v.FrequencyDay, v.Frequency)
+					if err != nil {
+						return
+					}
+					dateTime, err = time.ParseInLocation(utils.FormatDate, newDate, time.Local)
+					if err != nil {
+						err = fmt.Errorf("日期解析失败: %s", err.Error())
+						return
+					}
+				}
+			}
+		}
+	}
+
+	return
+}
+
+// 添加指标系列和数据
+func AddSeries(edbInfoIds []int, chartInfoId, chartInfoSource int, extraConf data_manage.ChartRangeAnalysisExtraConf, calculatesJson string) (err error) {
+	edbArr, e := data_manage.GetEdbInfoByIdList(edbInfoIds)
+	if e != nil {
+		err = fmt.Errorf("获取指标列表失败, Err: " + e.Error())
+		return
+	}
+	if len(edbArr) == 0 {
+		err = fmt.Errorf("获取指标列表失败, 指标不存在")
+		return
+	}
+	edbInfoType := edbArr[0].EdbInfoType
+	// 新增指标系列
+	seriesItem := new(data_manage.FactorEdbSeries)
+	seriesItem.SeriesName = extraConf.SeriesName
+	seriesItem.EdbInfoType = edbInfoType
+	seriesItem.CreateTime = time.Now().Local()
+	seriesItem.ModifyTime = time.Now().Local()
+	seriesItem.CalculateState = data_manage.FactorEdbSeriesCalculating
+	seriesItem.CalculateStep = calculatesJson
+	mappings := make([]*data_manage.FactorEdbSeriesMapping, 0)
+	for _, v := range edbArr {
+		mappings = append(mappings, &data_manage.FactorEdbSeriesMapping{
+			EdbInfoId:  v.EdbInfoId,
+			EdbCode:    v.EdbCode,
+			CreateTime: time.Now().Local(),
+			ModifyTime: time.Now().Local(),
+		})
+	}
+
+	seriesId, e := seriesItem.CreateSeriesAndMapping(seriesItem, mappings)
+	if e != nil {
+		err = fmt.Errorf("新增因子指标系列失败, Err: " + e.Error())
+		return
+	}
+
+	// 图表关联-此处添加的chart_info_id=0
+	seriesChartMapping := new(data_manage.FactorEdbSeriesChartMapping)
+	seriesChartMapping.CalculateType = data_manage.FactorEdbSeriesChartCalculateTypeRange
+	//新增图表和指标的映射关系
+	seriesChartMapping.CalculateData = ""
+	seriesChartMapping.FactorEdbSeriesId = seriesId
+	seriesChartMapping.ChartInfoId = chartInfoId
+	seriesChartMapping.Source = chartInfoSource
+	seriesChartMapping.CreateTime = time.Now().Local()
+	seriesChartMapping.ModifyTime = time.Now().Local()
+
+	if e = seriesChartMapping.Create(); e != nil {
+		err = fmt.Errorf("新增图表关联失败, Err: " + e.Error())
+		return
+	}
+
+	// todo 计算指标数据并存储
+	_, e = FactorEdbStepCalculateRange(seriesId, edbInfoIds, extraConf, false)
+	if e != nil {
+		err = fmt.Errorf("计算因子指标失败, Err: " + e.Error())
+		return
+	}
+
+	// 更新系列计算状态
+	cols := []string{seriesItem.Cols().CalculateState, seriesItem.Cols().ModifyTime}
+	seriesItem.CalculateState = data_manage.FactorEdbSeriesCalculated
+	seriesItem.ModifyTime = time.Now().Local()
+	if e = seriesItem.Update(cols); e != nil {
+		err = fmt.Errorf("更新因子指标系列计算状态失败, Err: " + e.Error())
+		return
+	}
+	return
+}
+
+func EditSeries(seriesMapping *data_manage.FactorEdbSeriesChartMapping, edbInfoIds []int, extraConf data_manage.ChartRangeAnalysisExtraConf, calculatesJson string, recalculate bool) (err error) {
+	seriesOb := new(data_manage.FactorEdbSeries)
+	seriesItem, e := seriesOb.GetItemById(seriesMapping.FactorEdbSeriesId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("因子指标系列不存在, Err: " + e.Error())
+			return
+		}
+		err = fmt.Errorf("获取因子指标系列失败, Err: " + e.Error())
+		return
+	}
+
+	edbArr, e := data_manage.GetEdbInfoByIdList(edbInfoIds)
+	if e != nil {
+		err = fmt.Errorf("获取指标列表失败, Err: " + e.Error())
+		return
+	}
+	if len(edbArr) == 0 {
+		err = fmt.Errorf("指标列表为空")
+		return
+	}
+	var calculateResp data_manage.FactorEdbSeriesStepCalculateResp
+	calculateResp.SeriesId = seriesItem.FactorEdbSeriesId
+
+	// 如果不需要进行重新计算(比如只改了系列名称)那么只更新指标系列
+	seriesItem.SeriesName = extraConf.SeriesName
+	seriesItem.EdbInfoType = edbArr[0].EdbInfoType
+	seriesItem.ModifyTime = time.Now().Local()
+	updateCols := []string{seriesOb.Cols().SeriesName, seriesOb.Cols().EdbInfoType, seriesOb.Cols().ModifyTime}
+	if !recalculate {
+		if e = seriesItem.Update(updateCols); e != nil {
+			err = fmt.Errorf("更新因子指标系列失败, Err: " + e.Error())
+			return
+		}
+		return
+	}
+
+	// 更新系列信息和指标关联
+	seriesItem.CalculateState = data_manage.FactorEdbSeriesCalculating
+	seriesItem.CalculateStep = calculatesJson
+	updateCols = append(updateCols, seriesOb.Cols().CalculateState, seriesOb.Cols().CalculateStep)
+
+	mappings := make([]*data_manage.FactorEdbSeriesMapping, 0)
+	for _, v := range edbArr {
+		mappings = append(mappings, &data_manage.FactorEdbSeriesMapping{
+			EdbInfoId:  v.EdbInfoId,
+			EdbCode:    v.EdbCode,
+			CreateTime: time.Now().Local(),
+			ModifyTime: time.Now().Local(),
+		})
+	}
+	if e = seriesItem.EditSeriesAndMapping(seriesItem, mappings, updateCols); e != nil {
+		err = fmt.Errorf("更新因子指标系列信息失败, Err: %s", e.Error())
+		return
+	}
+
+	// todo 重新计算
+	_, e = FactorEdbStepCalculateRange(seriesItem.FactorEdbSeriesId, edbInfoIds, extraConf, false)
+	if e != nil {
+		err = fmt.Errorf("计算因子指标失败, Err: " + e.Error())
+		return
+	}
+
+	// 更新系列计算状态
+	cols := []string{seriesItem.Cols().CalculateState, seriesItem.Cols().ModifyTime}
+	seriesItem.CalculateState = data_manage.FactorEdbSeriesCalculated
+	seriesItem.ModifyTime = time.Now().Local()
+	if e = seriesItem.Update(cols); e != nil {
+		err = fmt.Errorf("更新因子指标系列计算状态失败, Err: %s", e.Error())
+		return
+	}
+	return
+}
+
+// FactorEdbStepCalculateRange 因子指标-区间计算
+func FactorEdbStepCalculateRange(seriesId int, edbArr []int, extraConf data_manage.ChartRangeAnalysisExtraConf, recalculate bool) (calculateResp data_manage.FactorEdbSeriesStepCalculateResp, err error) {
+	// todo 如果指标已保存,则用指标数据还是图表指标数据?
+	// 获取图表x轴y轴
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("StepCalculate计算失败, ErrMsg: %v", err)
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 3)
+		}
+		/*if len(calculateResp.Fail) > 0 {
+			tips := "StepCalculate计算失败, ErrMsg: "
+			for _, f := range calculateResp.Fail {
+				tips += fmt.Sprintf("code: %s, err: %s\n", f.EdbCode, f.ErrMsg)
+			}
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}*/
+	}()
+
+	edbInfoMappingList, e := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbArr)
+	if e != nil {
+		err = fmt.Errorf("获取区间计算图表, A指标mapping信息失败, Err:%v", e)
+		return
+	}
+
+	_, _, _, err = GetChartDataByEdbInfoList(0, "", "", edbInfoMappingList, &extraConf)
+	if err != nil {
+		err = fmt.Errorf("获取图表数据失败, ErrMsg: %v", err)
+		return
+	}
+
+	// 重新计算-先清除原数据
+	calculateDataOb := new(data_manage.FactorEdbSeriesCalculateDataQjjs)
+
+	cond := fmt.Sprintf("%s = ?", calculateDataOb.Cols().FactorEdbSeriesId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, seriesId)
+	if e := calculateDataOb.RemoveByCondition(cond, pars); e != nil {
+		err = fmt.Errorf("清除原数据失败, err: %v", e)
+		return
+	}
+
+	// 计算成功的保存结果
+	dataArr := make([]*data_manage.FactorEdbSeriesCalculateDataQjjs, 0)
+	for _, v := range edbInfoMappingList {
+		dataList := v.DataList.([]*data_manage.EdbDataList)
+		for _, dataItem := range dataList {
+			dataTime, _ := time.ParseInLocation(utils.FormatDate, dataItem.DataTime, time.Local)
+			dataArr = append(dataArr, &data_manage.FactorEdbSeriesCalculateDataQjjs{
+				FactorEdbSeriesId: seriesId,
+				EdbInfoId:         v.EdbInfoId,
+				EdbCode:           v.EdbCode,
+				DataTime:          dataTime,
+				Value:             dataItem.Value,
+				CreateTime:        time.Now().Local(),
+				ModifyTime:        time.Now().Local(),
+				DataTimestamp:     dataItem.DataTimestamp,
+			})
+		}
+	}
+	if len(dataArr) == 0 {
+		err = fmt.Errorf("计算结果无数据, seriesId: %d", seriesId)
+		return
+	}
+	if e = calculateDataOb.CreateMulti(dataArr); e != nil {
+		err = fmt.Errorf("保存计算结果失败, seriesId: %d, err: %v, ", seriesId, e)
+		return
+	}
+	return
+}

+ 1 - 0
services/elastic.go

@@ -111,6 +111,7 @@ func EsAddOrEditReport(indexName, docId string, item *models.ElasticReportDetail
 			"ClassifyName":       item.ClassifyName,
 			"Categories":         item.Categories,
 			"StageStr":           item.StageStr,
+			"BodyMd5":            utils.MD5(item.BodyContent), // 对内容进行Md5加密,keywords类型,处理分组查询
 		}).Do(context.Background())
 		if err != nil {
 			return err

+ 41 - 0
services/eta_forum/eta_forum_hub.go

@@ -27,6 +27,8 @@ type ChartSaveLibReq struct {
 	EdbInfoDataList         []*AddEdbDataReq
 	ChartEdbMapping         []*data_manage.ChartEdbMapping
 	EdbInfoCalculateMapping []*data_manage.EdbInfoCalculateMapping
+	ChartSeries             []*data_manage.ChartSeries
+	ChartSeriesEdbMapping   []*data_manage.ChartSeriesEdbMapping
 	CreatorInfo             *system.Admin //创建者的账号信息
 	UploaderInfo            *system.Admin //上传者的账号信息
 }
@@ -90,6 +92,24 @@ func UploadChart(chartInfoId int, description string, uploaderInfo *system.Admin
 	for _, v := range chartMappingList {
 		edbIds = append(edbIds, v.EdbInfoId)
 	}
+	chartSeriesList := make([]*data_manage.ChartSeries, 0)
+	chartSeriesEdbList := make([]*data_manage.ChartSeriesEdbMapping, 0)
+	if chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+		chartSeriesList, err = data_manage.GetChartSeriesByChartInfoId(chartInfoId)
+		if err != nil {
+			errMsg = "获取图表关联的系列信息失败"
+			err = fmt.Errorf("获取图表关联的系列信息失败,Err:" + err.Error())
+			return
+		}
+
+		chartSeriesEdbList, err = data_manage.GetChartSeriesEdbByChartInfoId(chartInfoId)
+		if err != nil {
+			errMsg = "获取图表关联的系列指标信息失败"
+			err = fmt.Errorf("获取图表关联的系列指标信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
 	var (
 		edbInfoList     []*data_manage.EdbInfo
 		edbMappingList  []*data_manage.EdbInfoCalculateMapping
@@ -110,6 +130,8 @@ func UploadChart(chartInfoId int, description string, uploaderInfo *system.Admin
 	req.EdbInfoDataList = edbInfoDataList
 	req.EdbInfoCalculateMapping = edbMappingList
 	req.Description = description
+	req.ChartSeries = chartSeriesList
+	req.ChartSeriesEdbMapping = chartSeriesEdbList
 
 	// 查询创建者信息
 	creatorInfo, _ := system.GetSysAdminById(chartInfo.SysUserId)
@@ -189,6 +211,23 @@ func UpdateChart(chartInfoId int) (err error, errMsg string) {
 	for _, v := range chartMappingList {
 		edbIds = append(edbIds, v.EdbInfoId)
 	}
+	chartSeriesList := make([]*data_manage.ChartSeries, 0)
+	chartSeriesEdbList := make([]*data_manage.ChartSeriesEdbMapping, 0)
+	if chartInfo.ChartType == utils.CHART_TYPE_SECTION_COMBINE {
+		chartSeriesList, err = data_manage.GetChartSeriesByChartInfoId(chartInfoId)
+		if err != nil {
+			errMsg = "获取图表关联的系列信息失败"
+			err = fmt.Errorf("获取图表关联的系列信息失败,Err:" + err.Error())
+			return
+		}
+
+		chartSeriesEdbList, err = data_manage.GetChartSeriesEdbByChartInfoId(chartInfoId)
+		if err != nil {
+			errMsg = "获取图表关联的系列指标信息失败"
+			err = fmt.Errorf("获取图表关联的系列指标信息失败,Err:" + err.Error())
+			return
+		}
+	}
 	var (
 		edbInfoList     []*data_manage.EdbInfo
 		edbMappingList  []*data_manage.EdbInfoCalculateMapping
@@ -208,6 +247,8 @@ func UpdateChart(chartInfoId int) (err error, errMsg string) {
 	req.EdbInfoList = edbInfoList
 	req.EdbInfoDataList = edbInfoDataList
 	req.EdbInfoCalculateMapping = edbMappingList
+	req.ChartSeries = chartSeriesList
+	req.ChartSeriesEdbMapping = chartSeriesEdbList
 	// 查询创建者信息
 	creatorInfo, _ := system.GetSysAdminById(chartInfo.SysUserId)
 	if creatorInfo != nil {

+ 31 - 35
services/excel/lucky_sheet.go

@@ -630,24 +630,22 @@ func handleTableDataList(tableDataList [][]LuckySheetDataValue, luckySheetDataCo
 		flag = false
 		//尾部
 		deleteBottomRowIndexList := make([]int, 0)
-		// 数据要大于1行才会处理
-		if len(tableDataList) > 1 {
-			for rowIndex := lenRow - 1; rowIndex >= 0; rowIndex-- {
-				isDelete := true
-				for _, v := range tableDataList[rowIndex] {
-					if v.Monitor != `` {
-						isDelete = false
-						flag = true
-						break
-					}
-				}
-				if flag {
+		// 数据第1行不处理
+		for rowIndex := lenRow - 1; rowIndex > removeTopRow; rowIndex-- {
+			isDelete := true
+			for _, v := range tableDataList[rowIndex] {
+				if v.Monitor != `` {
+					isDelete = false
+					flag = true
 					break
 				}
-				if isDelete {
-					deleteBottomRowIndexList = append(deleteBottomRowIndexList, rowIndex)
-					removeBottomRow++
-				}
+			}
+			if flag {
+				break
+			}
+			if isDelete {
+				deleteBottomRowIndexList = append(deleteBottomRowIndexList, rowIndex)
+				removeBottomRow++
 			}
 		}
 
@@ -699,28 +697,26 @@ func handleTableDataList(tableDataList [][]LuckySheetDataValue, luckySheetDataCo
 		flag = false
 		//右边
 		deleteTailColumnIndexList := make([]int, 0)
-		// 数据要大于1列才会处理
-		if lenColumn > 1 {
-			for columnIndex := lenColumn - 1; columnIndex >= 0; columnIndex-- {
-				isDelete := true
-				for _, v := range tableDataList {
-					//如果一列都没有,说明是上面几行是空行,没有数据
-					if len(v) <= 0 {
-						continue
-					}
-					if v[columnIndex].Monitor != `` || (v[columnIndex].MergeCell.Column != columnIndex && v[columnIndex].MergeCell.Column != 0) {
-						isDelete = false
-						flag = true
-						break
-					}
+		// 数据第1列不处理
+		for columnIndex := lenColumn - 1; columnIndex > removeLeftColumn; columnIndex-- {
+			isDelete := true
+			for _, v := range tableDataList {
+				//如果一列都没有,说明是上面几行是空行,没有数据
+				if len(v) <= 0 {
+					continue
 				}
-				if flag {
+				if v[columnIndex].Monitor != `` || (v[columnIndex].MergeCell.Column != columnIndex && v[columnIndex].MergeCell.Column != 0) {
+					isDelete = false
+					flag = true
 					break
 				}
-				if isDelete {
-					deleteTailColumnIndexList = append(deleteTailColumnIndexList, columnIndex)
-					removeRightColumn++
-				}
+			}
+			if flag {
+				break
+			}
+			if isDelete {
+				deleteTailColumnIndexList = append(deleteTailColumnIndexList, columnIndex)
+				removeRightColumn++
 			}
 		}
 

+ 15 - 0
services/report.go

@@ -124,6 +124,21 @@ func UpdateChaptersVideo(ids []int) (err error) {
 //	return
 //}
 
+func initp2_838 (){
+	var condition string
+	var pars []interface{}
+	condition = " AND  state =  2 "
+	list ,err:= models.GetReportByCondition(condition,pars,[]string{},"",false,0,0)
+	if err != nil{
+		fmt.Println(err)
+	}
+	fmt.Println(len(list))
+	for  _,v:=  range  list{
+		fmt.Println(v.Id)
+		UpdateReportEs(v.Id,2)
+	}
+}
+
 // UpdateReportEs 更新报告/章节Es
 func UpdateReportEs(reportId int, publishState int) (err error) {
 	if reportId <= 0 {

+ 20 - 20
services/sandbox/sandbox.go

@@ -876,26 +876,6 @@ func sandboxClassifyHaveChildV2(allNode []*sandbox.SandboxClassifyItems, node *s
 	return
 }
 
-func GetSandBoxEdbIdsByContent(content string) (edbInfoIds []int, err error) {
-	var contentInfo sandbox.ContentDataStruct
-	err = json.Unmarshal([]byte(content), &contentInfo)
-	if err != nil {
-		err = fmt.Errorf("json.Unmarshal err:%s", err.Error())
-		return
-	}
-	// 遍历所有节点
-	for _, node := range contentInfo.Cells {
-		if node.Data == nil {
-			continue
-		}
-		for _, v := range node.Data.LinkData {
-			if v.Type == 1 {
-				edbInfoIds = append(edbInfoIds, v.Id)
-			}
-		}
-	}
-	return
-}
 func ReplaceEdbInSandbox(oldEdbInfoId, newEdbInfoId int) (err error) {
 	updateTotal := 0
 	logMsg := ""
@@ -955,6 +935,26 @@ func ReplaceEdbInSandbox(oldEdbInfoId, newEdbInfoId int) (err error) {
 		}
 		updateTotal += len(updateSandBox)
 	}
+	return
+}
 
+func GetSandBoxEdbIdsByContent(content string) (edbInfoIds []int, err error) {
+	var contentInfo sandbox.ContentDataStruct
+	err = json.Unmarshal([]byte(content), &contentInfo)
+	if err != nil {
+		err = fmt.Errorf("json.Unmarshal err:%s", err.Error())
+		return
+	}
+	// 遍历所有节点
+	for _, node := range contentInfo.Cells {
+		if node.Data == nil {
+			continue
+		}
+		for _, v := range node.Data.LinkData {
+			if v.Type == 1 {
+				edbInfoIds = append(edbInfoIds, v.Id)
+			}
+		}
+	}
 	return
 }

+ 19 - 18
services/user_login.go

@@ -252,24 +252,24 @@ func LdapUserCheck(userName, password string) (pass bool, err error) {
 // @return err error
 // @return errMsg string
 func ThirdLogin(req map[string]interface{}) (data GetCrmTokenData, err error, errMsg string) {
-	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
-		authCode, ok := req["code"]
-		if !ok {
-			errMsg = "参数有误"
-			err = errors.New("参数缺失, code")
-			return
-		}
-		authCodeStr := fmt.Sprint(authCode)
-		if authCodeStr == "" {
-			errMsg = "参数有误"
-			err = errors.New("参数缺失, AuthCode")
-			return
-		}
-
-		data, err = CodeLoginFromMiddleServer(authCodeStr)
-
-		return
-	}
+	//if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox || utils.BusinessCode == utils.BusinessCodeDebug {
+	//	authCode, ok := req["code"]
+	//	if !ok {
+	//		errMsg = "参数有误"
+	//		err = errors.New("参数缺失, code")
+	//		return
+	//	}
+	//	authCodeStr := fmt.Sprint(authCode)
+	//	if authCodeStr == "" {
+	//		errMsg = "参数有误"
+	//		err = errors.New("参数缺失, AuthCode")
+	//		return
+	//	}
+	//
+	//	data, err = CodeLoginFromMiddleServer(authCodeStr)
+	//
+	//	return
+	//}
 
 	data, err, errMsg = ThirdCodeLoginFromMiddleServer(req)
 
@@ -287,6 +287,7 @@ func ThirdLogin(req map[string]interface{}) (data GetCrmTokenData, err error, er
 // @return err error
 func ThirdCodeLoginFromMiddleServer(param map[string]interface{}) (tokenResp GetCrmTokenData, err error, errMsg string) {
 	if utils.EtaBridgeUrl == `` || utils.EtaBridgeLoginUrl == "" {
+
 		errMsg = `未配置第三方登录的桥接服务地址`
 		err = errors.New(errMsg)
 		return

+ 2 - 0
services/video.go

@@ -313,6 +313,8 @@ func CreateReportVideo(reportTitle, reportContent, reportTime string) (uploadUrl
 		time.Sleep(5 * time.Second)
 	}
 
+	fmt.Println("savePath:" + savePath)
+
 	//上传到阿里云 和 minio
 	//if utils.ObjectStorageClient == "minio" {
 	//	uploadUrl, err = UploadAudioToMinIo(saveName, savePath)

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