Parcourir la source

Merge branch 'master' into feature/eta2.2.8_predit_date

# Conflicts:
#	models/data_manage/edb_info.go
xyxie il y a 1 jour
Parent
commit
dd3da44858
100 fichiers modifiés avec 12259 ajouts et 371 suppressions
  1. 12 6
      controllers/ai/ai_summary.go
  2. 1 0
      controllers/base_auth.go
  3. 1315 0
      controllers/bi_dashboard.go
  4. 46 5
      controllers/classify.go
  5. 507 0
      controllers/data_manage/ai_predict_model/classify.go
  6. 940 0
      controllers/data_manage/ai_predict_model/index.go
  7. 708 0
      controllers/data_manage/base_from_rzd_index_controller.go
  8. 902 19
      controllers/data_manage/ccf_data.go
  9. 31 1
      controllers/data_manage/chart_classify.go
  10. 48 27
      controllers/data_manage/chart_info.go
  11. 7 4
      controllers/data_manage/chart_theme.go
  12. 9 0
      controllers/data_manage/correlation/correlation_chart_classify.go
  13. 18 1
      controllers/data_manage/correlation/correlation_chart_info.go
  14. 17 1
      controllers/data_manage/cross_variety/chart_info.go
  15. 8 0
      controllers/data_manage/edb_classify.go
  16. 48 13
      controllers/data_manage/edb_info.go
  17. 35 7
      controllers/data_manage/edb_info_relation.go
  18. 3 2
      controllers/data_manage/excel/balance_table.go
  19. 3 0
      controllers/data_manage/excel/excel_classify.go
  20. 33 12
      controllers/data_manage/excel/excel_info.go
  21. 6 3
      controllers/data_manage/future_good/future_good_chart_classify.go
  22. 107 60
      controllers/data_manage/future_good/future_good_chart_info.go
  23. 8 5
      controllers/data_manage/line_equation/line_chart_classify.go
  24. 18 1
      controllers/data_manage/line_equation/line_chart_info.go
  25. 51 23
      controllers/data_manage/line_feature/chart_info.go
  26. 8 5
      controllers/data_manage/line_feature/classify.go
  27. 8 4
      controllers/data_manage/multiple_graph_config.go
  28. 18 1
      controllers/data_manage/my_chart.go
  29. 9 0
      controllers/data_manage/predict_edb_classify.go
  30. 36 6
      controllers/data_manage/predict_edb_info.go
  31. 9 0
      controllers/data_manage/range_analysis/chart_classify.go
  32. 3 0
      controllers/data_manage/range_analysis/chart_info.go
  33. 220 0
      controllers/data_manage/stl/stl.go
  34. 9 5
      controllers/data_manage/supply_analysis/variety_edb.go
  35. 1394 0
      controllers/data_manage/usda_fas_data.go
  36. 42 0
      controllers/data_stat/edb_terminal.go
  37. 398 0
      controllers/edb_monitor/edb_monitor.go
  38. 240 0
      controllers/edb_monitor/edb_monitor_classify.go
  39. 251 0
      controllers/edb_monitor/edb_monitor_message.go
  40. 22 13
      controllers/english_report/english_classify.go
  41. 36 5
      controllers/english_report/report.go
  42. 19 2
      controllers/message.go
  43. 94 0
      controllers/report_chapter.go
  44. 1 0
      controllers/report_chapter_type.go
  45. 15 1
      controllers/report_v2.go
  46. 284 0
      controllers/residual_analysis/residual_analysis_controller.go
  47. 10 1
      controllers/sandbox/sandbox.go
  48. 18 1
      controllers/sys_admin.go
  49. 26 18
      controllers/target.go
  50. 20 3
      go.mod
  51. 56 6
      go.sum
  52. 356 0
      models/ai_predict_model/ai_predict_model_classify.go
  53. 261 0
      models/ai_predict_model/ai_predict_model_dashboard.go
  54. 176 0
      models/ai_predict_model/ai_predict_model_dashboard_detail.go
  55. 205 0
      models/ai_predict_model/ai_predict_model_data.go
  56. 390 0
      models/ai_predict_model/ai_predict_model_index.go
  57. 9 2
      models/ai_summary/ai_summary_classify.go
  58. 169 0
      models/bi_dashboard/bi_dashboard.go
  59. 136 0
      models/bi_dashboard/bi_dashboard_classify.go
  60. 77 0
      models/bi_dashboard/bi_dashboard_detail.go
  61. 75 0
      models/bi_dashboard/bi_dashboard_grant.go
  62. 46 0
      models/bi_dashboard/bi_dashboard_home_page.go
  63. 37 0
      models/binlog/binlog.go
  64. 64 0
      models/binlog/business_sys_interaction_log.go
  65. 11 0
      models/business_conf.go
  66. 60 1
      models/classify.go
  67. 97 0
      models/data_manage/base_from_ccf.go
  68. 60 0
      models/data_manage/base_from_rzd_classify.go
  69. 115 0
      models/data_manage/base_from_rzd_data.go
  70. 213 0
      models/data_manage/base_from_rzd_index.go
  71. 2 0
      models/data_manage/base_from_sci.go
  72. 8 6
      models/data_manage/base_from_smm.go
  73. 272 0
      models/data_manage/base_from_usda_fas.go
  74. 238 0
      models/data_manage/base_from_usda_fas_classify.go
  75. 50 6
      models/data_manage/chart_classify.go
  76. 19 7
      models/data_manage/chart_info.go
  77. 26 4
      models/data_manage/data_manage_permission/edb.go
  78. 2 0
      models/data_manage/edb_data_base.go
  79. 6 6
      models/data_manage/edb_data_wind.go
  80. 59 34
      models/data_manage/edb_info.go
  81. 14 1
      models/data_manage/edb_info_calculate_mapping.go
  82. 11 11
      models/data_manage/edb_info_relation.go
  83. 8 0
      models/data_manage/edb_terminal.go
  84. 4 2
      models/data_manage/excel/request/mixed_table.go
  85. 2 0
      models/data_manage/excel/response/excel_info.go
  86. 11 10
      models/data_manage/mysteel_chemical_index.go
  87. 3 2
      models/data_manage/smm_data.go
  88. 34 0
      models/data_manage/stl/calculate_stl_config.go
  89. 38 0
      models/data_manage/stl/calculate_stl_config_mapping.go
  90. 60 0
      models/data_manage/stl/edb_data_calculate_stl.go
  91. 28 0
      models/data_manage/stl/request/stl.go
  92. 60 0
      models/data_manage/stl/response/stl.go
  93. 87 8
      models/data_manage/trade_analysis/trade_analysis.go
  94. 70 0
      models/db.go
  95. 20 10
      models/document_manage_model/outside_report.go
  96. 129 0
      models/edb_monitor/edb_monitor.go
  97. 191 0
      models/edb_monitor/edb_monitor_classify.go
  98. 79 0
      models/edb_monitor/edb_monitor_message.go
  99. 25 0
      models/edb_monitor/request/edb_monitor.go
  100. 19 0
      models/edb_monitor/request/edb_monitor_classify.go

+ 12 - 6
controllers/ai/ai_summary.go

@@ -11,9 +11,10 @@ import (
 	"eta/eta_api/services/aiser"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // AiSummaryClassifyItems
@@ -40,6 +41,13 @@ func (this *AiController) AiSummaryClassifyItems() {
 			br.ErrMsg = err.Error()
 			return
 		}
+		nodeAll, err := aiSummaryService.GetAiSummaryClassifyByIsShowMe(resp.AllNodes, aiSummaryClassifyId, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		resp.AllNodes = nodeAll
 
 		br.Ret = 200
 		br.Success = true
@@ -936,7 +944,7 @@ func (this *AiController) AiSummaryDetail() {
 		AiSummaryItems: detail,
 	}
 
-	if detail.SaDocId >0 {
+	if detail.SaDocId > 0 {
 		item := new(saModel.SaDoc)
 		e := item.GetItemById(detail.SaDocId)
 		if e != nil {
@@ -952,7 +960,6 @@ func (this *AiController) AiSummaryDetail() {
 		resp.SaDocClassifyId = item.ClassifyId
 	}
 
-
 	br.Data = resp
 	br.Ret = 200
 	br.Success = true
@@ -1017,7 +1024,7 @@ func (this *AiController) AddAiSummary() {
 		br.ErrMsg = "保存分类失败,Err:" + err.Error()
 		return
 	}
-	classify ,err := ai_summary.GetAiSummaryClassifyById(req.ClassifyId)
+	classify, err := ai_summary.GetAiSummaryClassifyById(req.ClassifyId)
 	if err != nil {
 		br.Msg = "获取分类信息失败"
 		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
@@ -1562,7 +1569,6 @@ func (this *AiController) GenerateAiSummary() {
 //	return
 //}
 
-
 // AiSummaryClassifyList
 // @Title 获取所有纪要分类接口-不包含沙盘
 // @Description 获取所有纪要分类接口-不包含沙盘
@@ -1604,4 +1610,4 @@ func (this *AiController) AiSummaryClassifyList() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = resp
-}
+}

+ 1 - 0
controllers/base_auth.go

@@ -213,6 +213,7 @@ func (c *BaseAuthController) Prepare() {
 					api += v.Api + "&"
 				}
 			}
+			api += "&" + models.BusinessConfMap["PublicApi"]
 			//处理uri请求,去除前缀和参数
 			api = strings.TrimRight(api, "&")
 			uri = strings.Replace(uri, "/adminapi", "", 1)

+ 1315 - 0
controllers/bi_dashboard.go

@@ -0,0 +1,1315 @@
+package controllers
+
+import (
+	"encoding/json"
+	"errors"
+	"eta/eta_api/models"
+	"eta/eta_api/models/bi_dashboard"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services"
+	"eta/eta_api/utils"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type BIDaShboardController struct {
+	BaseAuthController
+}
+
+// GroupList
+// @Title 获取我的列表
+// @Description 获取我的列表接口
+// @Success 200 {object} models.RespGroupList
+// @router /my_list [get]
+func (this *BIDaShboardController) MyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	// 获取我的看板列表
+	privateCond := ` AND sys_admin_id = ? `
+	privatePars := []interface{}{this.SysUser.AdminId}
+	privateList, err := bi_dashboard.GetBiDashboardList(privateCond, privatePars)
+	if err != nil {
+		err = errors.New("我的看板列表查询出错:" + err.Error())
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = privateList
+	return
+}
+
+// AddDashboard
+// @Title 新增看板
+// @Description 新增看板接口
+// @Param	request	body models.AddDashboardReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /add [post]
+func (this *BIDaShboardController) AddDashboard() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.AddDashboardReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BiDashboardName == "" {
+		br.Msg = "名称不能为空"
+		return
+	}
+	nameItem, err := bi_dashboard.GetDashboardByName(req.BiDashboardName, this.SysUser.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败,Err:" + err.Error()
+		return
+	}
+	if nameItem != nil && nameItem.BiDashboardId > 0 {
+		br.Msg = "名称重复"
+		return
+	}
+
+	item := &bi_dashboard.BiDashboard{
+		//BiDashboardClassifyId: req.ClassifyId,
+		BiDashboardName: req.BiDashboardName,
+		SysAdminId:      this.SysUser.AdminId,
+		SysAdminName:    this.SysUser.RealName,
+		//Sort:                  0,
+		CreateTime: time.Now(),
+		ModifyTime: time.Now(),
+		State:      1,
+	}
+	id, e := bi_dashboard.AddBiDashboard(item)
+	if e != nil {
+		err = e
+		br.Msg = "新增失败"
+		br.ErrMsg = "新增失败,Err:" + e.Error()
+		return
+	}
+	detailList := make([]*bi_dashboard.BiDashboardDetail, 0)
+	for i, v := range req.List {
+		item := &bi_dashboard.BiDashboardDetail{
+			BiDashboardId: int(id),
+			Type:          v.Type,
+			UniqueCode:    v.UniqueCode,
+			Sort:          i + 1,
+			CreateTime:    time.Now(),
+			ModifyTime:    time.Now(),
+		}
+		detailList = append(detailList, item)
+	}
+	err = bi_dashboard.AddBiDashboardDetailMulti(detailList)
+	if err != nil {
+		br.Msg = "新增详情失败"
+		br.ErrMsg = "新增详情失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新增成功"
+	//br.Data =
+}
+
+// EditPpt
+// @Title 编辑看板
+// @Description 编辑看板接口
+// @Param	request	body bi_dashboard.EditDashboardReq true "type json string"
+// @Success 200 Ret=200 编辑成功
+// @router /edit [post]
+func (this *BIDaShboardController) EditDashboard() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.EditDashboardReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BiDashboardName == "" {
+		br.Msg = "标题不能为空"
+		return
+	}
+	item, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "获取数据异常!"
+		br.ErrMsg = "获取数据异常,Err:" + err.Error()
+		return
+	}
+	
+	// 修改
+	item.BiDashboardName = req.BiDashboardName
+	item.ModifyTime = time.Now()
+
+	err = bi_dashboard.EditDashboard(item)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑失败,Err:" + err.Error()
+		return
+	}
+
+	err = bi_dashboard.DeleteBiDashboardDetail(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "删除详情失败"
+		br.ErrMsg = "删除详情失败,Err:" + err.Error()
+		return
+	}
+
+	detailList := make([]*bi_dashboard.BiDashboardDetail, 0)
+	for _, v := range req.List {
+		item := &bi_dashboard.BiDashboardDetail{
+			BiDashboardId: req.BiDashboardId,
+			Type:          v.Type,
+			UniqueCode:    v.UniqueCode,
+			Sort:          v.Sort,
+			CreateTime:    time.Now(),
+			ModifyTime:    time.Now(),
+		}
+		detailList = append(detailList, item)
+	}
+	err = bi_dashboard.AddBiDashboardDetailMulti(detailList)
+	if err != nil {
+		br.Msg = "新增详情失败"
+		br.ErrMsg = "新增详情失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "编辑成功"
+	br.IsAddLog = true
+}
+
+// DeleteDashboard
+// @Title 删除看板
+// @Description 删除看板接口
+// @Param	request	body bi_dashboard.DelDashboardReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /delete [post]
+func (this *BIDaShboardController) DeleteDashboard() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.DelDashboardReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BiDashboardId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	item, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "获取数据异常!"
+		br.ErrMsg = "获取数据异常,Err:" + err.Error()
+		return
+	}
+	if item.SysAdminId != this.SysUser.AdminId {
+		br.Msg = "无权删除"
+		return
+	}
+	err = bi_dashboard.DelDashboard(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	//如果该看板存在共享记录,则删除共享
+	err = bi_dashboard.DeleteDashboardGrant(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "删除成功"
+}
+
+// DeleteDashboard
+// @Title 删除看板详情
+// @Description 删除看板详情接口
+// @Param	request	body bi_dashboard.DelDashboardDetailReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /detail/delete [post]
+func (this *BIDaShboardController) DeleteDashboardDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.DelDashboardDetailReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BiDashboardDetailId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	err = bi_dashboard.DeleteBiDashboardDetailByDetailId(req.BiDashboardDetailId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "删除成功"
+}
+
+// DetailDashboard
+// @Title 获取看板详情
+// @Description 获取看板详情接口
+// @Param   PptId   query   int  true       "PptId"
+// @Success 200 {object} models.PptV2
+// @router /detail [get]
+func (this *BIDaShboardController) DetailDashboard() {
+	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
+	}
+	dashboardId, _ := this.GetInt("DashboardId")
+	resp := new(bi_dashboard.DashboardDetailResp)
+	dashboardItem, err := bi_dashboard.GetDashboardById(dashboardId)
+	if err != nil {
+		err = errors.New("我的看板列表查询出错:" + err.Error())
+		br.Msg = "我的看板列表查询出错"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	grantInfoList, err := bi_dashboard.GetDashboardGrantInfo(dashboardId)
+	if err != nil {
+		br.Msg = "信息获取失败"
+		br.ErrMsg = "共享信息获取失败,Err:" + err.Error()
+		return
+	}
+	if len(grantInfoList) > 0 {
+		resp.IsGrant = 1
+	}
+
+	detailList, err := bi_dashboard.GetBiDashboardDetailById(dashboardId)
+	if err != nil {
+		br.Msg = "详情获取失败"
+		br.ErrMsg = "详情获取失败,Err:" + err.Error()
+		return
+	}
+
+	editor, e := services.UpdateBiDashboardEditing(dashboardId, 0, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "编辑状态更新失败"
+		br.ErrMsg = "编辑状态更新失败,Err:" + e.Error()
+		return
+	}
+
+	resp.Editor = editor
+	resp.BiDashboard = dashboardItem
+	resp.List = detailList
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// MoveDashboard
+// @Title 移动看板详情
+// @Description 移动看板详情接口
+// @Param	request	body bi_dashboard.MoveDashboardDetailReq true "type json string"
+// @Success 200 Ret=200 移动成功
+// @router /detail/move [post]
+func (this *BIDaShboardController) MoveDashboard() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.MoveDashboardDetailReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.BiDashboardId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	if req.BiDashboardDetailId == req.OtherDetailId || req.BiDashboardDetailId <= 0 || req.OtherDetailId <= 0 {
+		br.Msg = "看板Id有误"
+		return
+	}
+	if req.Sort < 0 || req.OtherSort < 0 || req.Sort == req.OtherSort {
+		br.Msg = "排序有误"
+		return
+	}
+
+	item, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "获取数据异常!"
+		br.ErrMsg = "获取数据异常,Err:" + err.Error()
+		return
+	}
+
+	// 判断权限
+	if item.SysAdminId != this.SysUser.AdminId {
+		br.Msg = `无权移动`
+		return
+	}
+
+	// 修改
+	detailItem := &bi_dashboard.BiDashboardDetail{
+		BiDashboardDetailId: req.BiDashboardDetailId,
+		Sort:                req.OtherSort,
+		ModifyTime:          time.Now(),
+	}
+	err = bi_dashboard.EditBiDashboardDetail(detailItem)
+	if err != nil {
+		br.Msg = "编辑详情失败"
+		br.ErrMsg = "编辑详情失败,Err:" + err.Error()
+		return
+	}
+
+	otherItem := &bi_dashboard.BiDashboardDetail{
+		BiDashboardDetailId: req.OtherDetailId,
+		Sort:                req.Sort,
+		ModifyTime:          time.Now(),
+	}
+	err = bi_dashboard.EditBiDashboardDetail(otherItem)
+	if err != nil {
+		br.Msg = "编辑详情失败"
+		br.ErrMsg = "编辑详情失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "编辑成功"
+	br.IsAddLog = true
+}
+
+// ShareList
+// @Title 获取共享列表
+// @Description 获取共享列表接口
+// @Success 200 {object} models.RespGroupList
+// @router /share_list [get]
+func (this *BIDaShboardController) ShareList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	//myPptList := make([]*bi_dashboard.BiDashboard, 0)
+	//otherPptList := make([]*bi_dashboard.BiDashboard, 0)
+	grantList := bi_dashboard.RespGroupList{}
+
+	// 获取我的看板列表
+	ShareList, err := bi_dashboard.GetAllMyShareList(this.SysUser.AdminId)
+	if err != nil {
+		err = errors.New("我的看板列表查询出错:" + err.Error())
+		br.ErrMsg = err.Error()
+		br.Msg = "查询失败"
+		return
+	}
+	grantList.MyList = ShareList
+
+	//dashboardMap := make(map[int]*bi_dashboard.BiDashboard)
+	adminIdList := make([]int, 0)   //需要查询的创建人admin_id列表集合
+	adminIdMap := make(map[int]int) //需要查询的创建人admin_id集合,用来去重的,避免重复id
+	grantDashboardList, err := bi_dashboard.GetAllGrantList(this.SysUser.AdminId)
+	if err != nil {
+		err = errors.New("我的看板列表查询出错:" + err.Error())
+		br.ErrMsg = err.Error()
+		br.Msg = "查询失败"
+		return
+	}
+
+	publicAdminIdList := make([]int, 0)
+	publicDashboardListMap := make(map[int][]*bi_dashboard.BiDashboard)
+	for _, v := range grantDashboardList {
+		publicDashboardList, ok := publicDashboardListMap[v.SysAdminId]
+		if !ok {
+			publicDashboardList = make([]*bi_dashboard.BiDashboard, 0)
+			publicAdminIdList = append(publicAdminIdList, v.SysAdminId)
+			if _, ok := adminIdMap[v.SysAdminId]; !ok {
+				adminIdList = append(adminIdList, v.SysAdminId) //需要查询的创建人admin_id列表集合
+				adminIdMap[v.SysAdminId] = v.SysAdminId         //需要查询的创建人admin_id集合,用来去重的,避免重复id
+			}
+		}
+
+		tmp := &bi_dashboard.BiDashboard{
+			BiDashboardId:   v.BiDashboardId,
+			BiDashboardName: v.BiDashboardName,
+			CreateTime:      v.CreateTime,
+			ModifyTime:      v.ModifyTime,
+			Sort:            v.Sort,
+			State:           v.State,
+			SysAdminId:      v.SysAdminId,
+			SysAdminName:    v.SysAdminName,
+		}
+		publicDashboardList = append(publicDashboardList, tmp)
+		publicDashboardListMap[v.SysAdminId] = publicDashboardList
+	}
+	// 创建人信息
+	systemAdminMap := make(map[int]*system.Admin)
+	systemAdminList, err := system.GetAdminListByIdList(adminIdList)
+	if err != nil {
+		return
+	}
+	for _, v := range systemAdminList {
+		systemAdminMap[v.AdminId] = v
+	}
+
+	for _, v := range publicAdminIdList {
+		systemAdmin, ok := systemAdminMap[v]
+		if !ok {
+			continue
+		}
+
+		// 看板 列表信息
+		respGroupNameListItemList, ok := publicDashboardListMap[v]
+		if !ok {
+			respGroupNameListItemList = make([]*bi_dashboard.BiDashboard, 0)
+		}
+
+		// ppt 分组信息
+		tmpRespGroupListItem := &bi_dashboard.RespOtherGroupListItem{
+			GroupId:       int64(systemAdmin.AdminId),
+			GroupName:     systemAdmin.RealName,
+			AdminId:       systemAdmin.AdminId,
+			DashboardList: respGroupNameListItemList,
+		}
+		grantList.OtherList = append(grantList.OtherList, tmpRespGroupListItem)
+	}
+	//if len(dashboradIds) > 0 {
+	//	// 通过dashboradIds列表字段获取所有的看板信息
+	//	dashboradList, tmpErr := bi_dashboard.GetDashboradByIds(dashboradIds)
+	//	if tmpErr != nil {
+	//		err = errors.New("查询dashborad详情出错:" + err.Error())
+	//		br.Msg = "查询dashborad详情出错"
+	//		br.ErrMsg = err.Error()
+	//		return
+	//	}
+	//	for _, v := range dashboradList {
+	//		dashboardMap[v.BiDashboardId] = v
+	//	}
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = grantList
+	return
+}
+
+// PublicList
+// @Title 获取公共列表
+// @Description 获取公共列表接口
+// @Success 200 {object} models.RespGroupList
+// @router /public_list [get]
+func (this *BIDaShboardController) PublicList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	resp := make([]*bi_dashboard.RespPublicGroupListItem, 0)
+	// 获取公共看板列表
+	publicCond := ` AND state = 6 `
+	publicPars := make([]interface{}, 0)
+	publicList, err := bi_dashboard.GetBiDashboardList(publicCond, publicPars)
+	if err != nil {
+		err = errors.New("我的看板列表查询出错:" + err.Error())
+		return
+	}
+
+	//dashboardMap := make(map[int]*bi_dashboard.BiDashboard)
+	adminIdList := make([]int, 0)   //需要查询的创建人admin_id列表集合
+	adminIdMap := make(map[int]int) //需要查询的创建人admin_id集合,用来去重的,避免重复id
+
+	publicAdminIdList := make([]int, 0)
+	publicDashboardListMap := make(map[int][]*bi_dashboard.BiDashboard)
+	publicDashboardClassifyIdMap := make(map[int]int)
+	for _, v := range publicList {
+		publicDashboardList, ok := publicDashboardListMap[v.SysAdminId]
+		if !ok {
+			publicDashboardList = make([]*bi_dashboard.BiDashboard, 0)
+			publicAdminIdList = append(publicAdminIdList, v.SysAdminId)
+			if _, ok := adminIdMap[v.SysAdminId]; !ok {
+				adminIdList = append(adminIdList, v.SysAdminId) //需要查询的创建人admin_id列表集合
+				adminIdMap[v.SysAdminId] = v.SysAdminId         //需要查询的创建人admin_id集合,用来去重的,避免重复id
+			}
+		}
+
+		tmp := &bi_dashboard.BiDashboard{
+			BiDashboardId:         v.BiDashboardId,
+			BiDashboardClassifyId: v.BiDashboardClassifyId,
+			BiDashboardName:       v.BiDashboardName,
+			CreateTime:            v.CreateTime,
+			ModifyTime:            v.ModifyTime,
+			Sort:                  v.Sort,
+			State:                 v.State,
+			SysAdminId:            v.SysAdminId,
+			SysAdminName:          v.SysAdminName,
+		}
+		publicDashboardList = append(publicDashboardList, tmp)
+		publicDashboardListMap[v.SysAdminId] = publicDashboardList
+		publicDashboardClassifyIdMap[v.BiDashboardClassifyId] = v.BiDashboardClassifyId
+	}
+	// 创建人信息
+	systemAdminMap := make(map[int]*system.Admin)
+	systemAdminList, err := system.GetAdminListByIdList(adminIdList)
+	if err != nil {
+		return
+	}
+	for _, v := range systemAdminList {
+		systemAdminMap[v.AdminId] = v
+	}
+
+	for _, v := range adminIdList {
+		systemAdmin, ok := systemAdminMap[v]
+		if !ok {
+			continue
+		}
+
+		// 看板 列表信息
+		respGroupNameListItemList, ok := publicDashboardListMap[v]
+		if !ok {
+			respGroupNameListItemList = make([]*bi_dashboard.BiDashboard, 0)
+		}
+
+		// ppt 分组信息
+		tmpRespGroupListItem := &bi_dashboard.RespPublicGroupListItem{
+			GroupId:       int64(systemAdmin.AdminId),
+			GroupName:     systemAdmin.RealName,
+			AdminId:       systemAdmin.AdminId,
+			DashboardList: make([]*bi_dashboard.BiDashboard, 0),
+		}
+
+		for _, vv := range respGroupNameListItemList {
+			tmpRespGroupListItem.DashboardList = append(tmpRespGroupListItem.DashboardList, vv)
+		}
+		resp = append(resp, tmpRespGroupListItem)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = resp
+	return
+}
+
+// AddDashboardClassify
+// @Title 新增看板分类
+// @Description 新增看板分类接口
+// @Param	request	body bi_dashboard.AddDashboardClassifyReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /classify/add [post]
+func (this *BIDaShboardController) AddDashboardClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.AddDashboardClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "名称不能为空"
+		return
+	}
+	maxSort, err := bi_dashboard.GetBiDashboardClassifyMaxSort()
+	if err != nil {
+		br.Msg = "获取最大排序值失败"
+		br.ErrMsg = "获取最大排序值失败,Err:" + err.Error()
+		return
+	}
+	count, err := bi_dashboard.GetBiDashboardClassifyByName(req.ClassifyName)
+	if err != nil {
+		br.Msg = "获取分类名称失败"
+		br.ErrMsg = "获取分类名称失败,Err:" + err.Error()
+		return
+	}
+	if count > 0 {
+		br.Msg = "分类名称已存在"
+		return
+	}
+
+	item := &bi_dashboard.BiDashboardClassify{
+		BiDashboardClassifyName: req.ClassifyName,
+		Sort:                    maxSort + 1,
+		CreateTime:              time.Now(),
+		ModifyTime:              time.Now(),
+	}
+	_, e := bi_dashboard.AddBiDashboardClassify(item)
+	if e != nil {
+		err = e
+		br.Msg = "新增失败"
+		br.ErrMsg = "新增失败,Err:" + e.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新增成功"
+	//br.Data =
+}
+
+// EditDashboardClassify
+// @Title 编辑看板分类
+// @Description 编辑看板分类接口
+// @Param	request	body bi_dashboard.EditDashboardReq true "type json string"
+// @Success 200 Ret=200 编辑成功
+// @router /classify/edit [post]
+func (this *BIDaShboardController) EditDashboardClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.EditDashboardClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ClassifyName == "" {
+		br.Msg = "标题不能为空"
+		return
+	}
+	if req.BiDashboardClassifyId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, BiDashboardClassifyId: %d", req.BiDashboardClassifyId)
+		return
+	}
+	item, err := bi_dashboard.GetBiDashboardClassifyById(req.BiDashboardClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取数据异常!"
+		br.ErrMsg = "获取数据异常,Err:" + err.Error()
+		return
+	}
+
+	// 修改
+	item.BiDashboardClassifyName = req.ClassifyName
+	item.ModifyTime = time.Now()
+
+	err = bi_dashboard.EditDashboardClassify(item)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "编辑成功"
+	br.IsAddLog = true
+}
+
+// Grant
+// @Title 分配看板权限
+// @Description 分配看板权限接口
+// @Param	request	body models.GrantPptReq true "type json string"
+// @Success 200 Ret=200 分配成功
+// @router /grant [post]
+func (this *BIDaShboardController) Grant() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.GrantDashboardReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.AdminIdStr == "" {
+		br.Msg = "参数错误"
+		return
+	}
+	if req.BiDashboardId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	dashboardItem, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
+	if err != nil {
+		err = errors.New("我的看板列表查询出错:" + err.Error())
+		br.Msg = "我的看板列表查询出错"
+		br.ErrMsg = err.Error()
+		return
+	}
+	if dashboardItem.SysAdminId != this.SysUser.AdminId {
+		br.Msg = "无权配置"
+		return
+	}
+
+	list := make([]*bi_dashboard.BiDashboardGrant, 0)
+
+	grantAdminIdStrList := strings.Split(req.AdminIdStr, ",")
+	lenGrantAdminIdStrList := len(grantAdminIdStrList) //指定用户的人数
+	for _, v := range grantAdminIdStrList {
+		grantAdminId, tmpErr := strconv.Atoi(v)
+		if tmpErr != nil {
+			br.Msg = "参数有误"
+			br.ErrMsg = fmt.Sprintf("参数有误,Err:%s", tmpErr.Error())
+			return
+		}
+
+		//如果只选择了自己作为指定的人,那么就提示他报错。如果多人,那么就过滤自己
+		if grantAdminId == this.SysUser.AdminId {
+			if lenGrantAdminIdStrList == 1 {
+				br.Msg = "不能指定自己为权限用户"
+				br.ErrMsg = fmt.Sprintf("参数有误,Err:%s", tmpErr.Error())
+				return
+			}
+			continue
+		}
+		tmpV := &bi_dashboard.BiDashboardGrant{
+			BiDashboardId: req.BiDashboardId,
+			GrantAdminId:  grantAdminId,
+			CreateTime:    time.Now(),
+		}
+		list = append(list, tmpV)
+	}
+
+	if len(list) <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误,Err:%s", "指定用户为空")
+		return
+	}
+
+	err = bi_dashboard.MultiAddDashboardGrant(req.BiDashboardId, list)
+	if err != nil {
+		br.Msg = "分配失败"
+		br.ErrMsg = fmt.Sprintf("分配失败,Err:%s", err.Error())
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "分配成功"
+}
+
+// Public
+// @Title
+// @Description 设置公共看板
+// @Param	request	body models.GrantPptReq true "type json string"
+// @Success 200 Ret=200 分配成功
+// @router /public [post]
+func (this *BIDaShboardController) Public() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.PublicDashboardReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	item, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "获取数据异常!"
+		br.ErrMsg = "获取数据异常,Err:" + err.Error()
+		return
+	}
+
+	item.State = 6
+
+	err = bi_dashboard.EditDashboard(item)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "分配成功"
+}
+
+// GroupList
+// @Title 获取分类列表
+// @Description 获取分类列表接口
+// @Success 200 {object} models.RespGroupList
+// @router /classify/list [get]
+func (this *BIDaShboardController) ClassifyList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	classifyList, err := bi_dashboard.GetBiDashboardClassifyAllList()
+	if err != nil {
+		br.Msg = "查询失败"
+		br.ErrMsg = "查询失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = classifyList
+	return
+}
+
+// CancelGrant
+// @Title 取消分配看板权限
+// @Description 取消分配看板权限
+// @Param	request	body bi_dashboard.DelDashboardReq true "type json string"
+// @Success 200 Ret=200 分配成功
+// @router /grant/cancel [post]
+func (this *BIDaShboardController) CancelGrant() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.DelDashboardReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BiDashboardId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	item, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "数据不存在"
+		br.ErrMsg = "数据不存在,Err:" + err.Error()
+		return
+	}
+	if item.SysAdminId != this.SysUser.AdminId {
+		br.Msg = "无权配置"
+		return
+	}
+
+	// 分配
+	err = bi_dashboard.DeleteDashboardGrant(req.BiDashboardId)
+	if err != nil {
+		br.Msg = "取消失败"
+		br.ErrMsg = "取消失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "取消成功"
+}
+
+// GrantInfo
+// @Title 获取分配ppt权限详情
+// @Description 获取分配ppt权限详情接口
+// @Param    BiDashboardId   query   int  true       "看板的id"
+// @Success 200 {object} models.GrantInfoResp
+// @router /grant/info [get]
+func (this *BIDaShboardController) GrantInfo() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	biDashboardId, _ := this.GetInt("BiDashboardId")
+	if biDashboardId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	item, err := bi_dashboard.GetDashboardById(biDashboardId)
+	if err != nil {
+		br.Msg = "数据不存在"
+		br.ErrMsg = "数据不存在,Err:" + err.Error()
+		return
+	}
+	if item.SysAdminId != this.SysUser.AdminId {
+		br.Msg = "无权配置"
+		return
+	}
+
+	grantInfoList, err := bi_dashboard.GetDashboardGrantInfo(biDashboardId)
+	if err != nil {
+		br.Msg = "信息获取失败"
+		br.ErrMsg = "信息获取失败,Err:" + err.Error()
+		return
+	}
+
+	if len(grantInfoList) <= 0 {
+		br.Msg = "未配置"
+		br.IsSendEmail = false
+		br.Success = true
+		br.Ret = 200
+		return
+	}
+
+	var adminIdStr string
+
+	adminIdsList := make([]string, 0)
+	for _, v := range grantInfoList {
+		adminIdsList = append(adminIdsList, strconv.Itoa(int(v.GrantAdminId)))
+	}
+	adminIdStr = strings.Join(adminIdsList, ",")
+
+	br.Ret = 200
+	br.Success = true
+	br.Data = adminIdStr
+	br.Msg = "查询成功"
+}
+
+// Public
+// @Title
+// @Description 撤销公共看板
+// @Param	request	body models.GrantPptReq true "type json string"
+// @Success 200 Ret=200 分配成功
+// @router /public/cancel [post]
+func (this *BIDaShboardController) PublicCancel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.DelDashboardReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	item, err := bi_dashboard.GetDashboardById(req.BiDashboardId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取数据异常!"
+		br.ErrMsg = "获取数据异常,Err:" + err.Error()
+		return
+	}
+
+	item.State = 1
+
+	err = bi_dashboard.EditDashboard(item)
+	if err != nil {
+		br.Msg = "编辑失败"
+		br.ErrMsg = "编辑失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+	br.Msg = "撤销成功"
+}
+
+// GroupList
+// @Title 获取我的首页看板
+// @Description 获取我的首页看板接口
+// @Success 200 {object} models.RespGroupList
+// @router /home_page [get]
+func (this *BIDaShboardController) HomePage() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	item, err := bi_dashboard.GetBiDashboardHomePageById(this.SysUser.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "数据不存在"
+		br.ErrMsg = "数据不存在,Err:" + err.Error()
+		return
+	}
+
+	publicCond := ` AND state = 6 AND bi_dashboard_classify_id > 0 `
+	publicPars := []interface{}{this.SysUser.AdminId}
+	publicList, err := bi_dashboard.GetBiDashboardList(publicCond, publicPars)
+	if err != nil {
+		err = errors.New("我的看板列表查询出错:" + err.Error())
+		return
+	}
+
+	if item.BiDashboardHomePageId == 0 && len(publicList) > 0 {
+		item = &bi_dashboard.BiDashboardHomePage{
+			AdminId:       publicList[0].SysAdminId,
+			BiDashboardId: publicList[0].BiDashboardId,
+			FromType:      3,
+		}
+	}
+
+	if item.BiDashboardId == 0 {
+		item = nil
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = item
+	return
+}
+
+// GroupList
+// @Title 保存我的看板首页
+// @Description保存我的看板首页接口
+// @Success 200 {object} models.RespGroupList
+// @router /home_page/save [post]
+func (this *BIDaShboardController) HomePageSave() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req bi_dashboard.SaveHomePageReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.BiDashboardId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	item, err := bi_dashboard.GetBiDashboardHomePageById(this.SysUser.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "首页看板查询失败"
+		br.ErrMsg = "首页看板查询失败,Err:" + err.Error()
+		return
+	}
+
+	homePageItem := &bi_dashboard.BiDashboardHomePage{
+		BiDashboardId: req.BiDashboardId,
+		AdminId:       this.SysUser.AdminId,
+		CreateTime:    time.Now(),
+		ModifyTime:    time.Now(),
+		FromType:      req.FromType,
+	}
+
+	if item.BiDashboardHomePageId > 0 {
+		homePageItem.BiDashboardHomePageId = item.BiDashboardHomePageId
+	}
+
+	err = bi_dashboard.SaveBiDashboardHomePage(homePageItem)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	return
+}
+
+// Editing
+// @Title 标记/查询编辑状态
+// @Description 标记/查询编辑状态
+// @Param	request	body models.PPTEditingReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /editing [post]
+func (this *BIDaShboardController) Editing() {
+	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 bi_dashboard.BiDashboardEditingReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.BiDashboardId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+	if req.Status < 0 {
+		br.Msg = "标记状态异常"
+		return
+	}
+
+	editor, e := services.UpdateBiDashboardEditing(req.BiDashboardId, req.Status, sysUser.AdminId, sysUser.RealName)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新编辑状态失败, err: " + e.Error()
+		return
+	}
+
+	br.Data = editor
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// GroupList
+// @Title 获取我拥有的图库表格菜单权限
+// @Description 获取我拥有的图库表格菜单权限接口
+// @Success 200 {object} models.RespGroupList
+// @router /chart_excel_permission [get]
+func (this *BIDaShboardController) ChartExcelPermission() {
+	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
+	}
+
+	roleId := sysUser.RoleId
+	//roleId=1
+	if roleId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	roleIds := strconv.Itoa(roleId)
+	//查询账号绑定的其他角色
+	otherRoles, err := system.GetRoleIdsByAdminId(sysUser.AdminId)
+	if err != nil {
+		br.Msg = "获取其他角色失败"
+		br.ErrMsg = "获取其他角色失败,Err:" + err.Error()
+		return
+	}
+	if len(otherRoles) > 0 {
+		for _, v := range otherRoles {
+			roleIds += "," + strconv.Itoa(v.RoleId)
+		}
+	}
+	groupId := 0
+	if utils.RunMode == "release" {
+		groupId = 37
+	} else {
+		groupId = 61
+	}
+	//共享客户组下的用户
+	shareSellerMap := make(map[int]bool, 0)
+	subAdmins, err := system.GetAdminByGroupId(groupId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取销售失败"
+		br.ErrMsg = "获取销售失败,Err:" + err.Error()
+		return
+	}
+	for _, admin := range subAdmins {
+		shareSellerMap[admin.AdminId] = true
+	}
+
+	list, err := system.GetMenuByRoleIds(roleIds)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	lenList := len(list)
+
+	menuList := make([]*system.SysMenuSimple, 0)
+
+	chartExcelTypes, e := models.GetBusinessConfByKey("ChartExcelType")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
+		return
+	}
+	types := strings.Split(chartExcelTypes.ConfVal, ",")
+	typeMap := map[string]bool{}
+	for _, v := range types {
+		typeMap[v] = true
+	}
+
+	for i := 0; i < lenList; i++ {
+		item := list[i]
+		if !typeMap[item.LevelPath] && !typeMap[item.Path] {
+			continue
+		}
+		newItem := &system.SysMenuSimple{
+			MenuId:   item.MenuId,
+			ParentId: item.ParentId,
+			Name:     item.Name,
+			Sort:     item.Sort,
+			Path:     item.Path,
+			NameEn:   item.NameEn,
+		}
+		if item.IsLevel == 1 {
+			newItem.Path = item.LevelPath
+		}
+		menuList = append(menuList, newItem)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = menuList
+	return
+}

+ 46 - 5
controllers/classify.go

@@ -204,10 +204,6 @@ func (this *ClassifyController) Delete() {
 		return
 	}
 
-	br.Msg = "报告分类不允许删除"
-	br.IsSendEmail = false
-	return
-
 	item, err := models.GetClassifyById(req.ClassifyId)
 	if err != nil {
 		br.Msg = "获取信息失败"
@@ -218,7 +214,46 @@ func (this *ClassifyController) Delete() {
 		br.Msg = "分类不存在"
 		return
 	}
-	err = models.DeleteClassify(req.ClassifyId)
+	classifyList := &models.ClassifyList{Id: item.Id}
+	err = services.MarkEnableDeleteClassify([]*models.ClassifyList{classifyList})
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "标记失败,Err:" + err.Error()
+		return
+	}
+	if classifyList.IsEnableDelete == 0 {
+		br.Msg = "该分类有关联报告或审批流,不允许删除"
+		return
+	}
+
+	var childClassifyIds []int
+	if item.HasChild == 1 {
+		// 获取所有子分类
+		subClassifyMap := make(map[int][]int)
+		allClassify, err := models.GetAllClassifyWithDesc()
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "获取信息失败,Err:" + err.Error()
+			return
+		}
+		for _, classify := range allClassify {
+			v, ok := subClassifyMap[classify.Id]
+			if !ok {
+				subClassifyMap[classify.Id] = []int{}
+			}
+			pv, pok := subClassifyMap[classify.ParentId]
+			if pok {
+				subClassifyMap[classify.ParentId] = append(pv, classify.Id)
+			} else {
+				subClassifyMap[classify.ParentId] = []int{classify.Id}
+			}
+			if ok {
+				subClassifyMap[classify.ParentId] = append(subClassifyMap[classify.ParentId], v...)
+			}
+		}
+		childClassifyIds = subClassifyMap[item.Id]
+	}
+	err = item.Delete(childClassifyIds)
 	if err != nil {
 		br.Msg = "删除失败"
 		br.ErrMsg = "删除失败,Err:" + err.Error()
@@ -505,6 +540,12 @@ func (this *ClassifyController) ListClassify() {
 		}
 	}
 
+	err = services.MarkEnableDeleteClassify(list)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "标记可删除分类失败,Err:" + err.Error()
+		return
+	}
 	// 先将分类列表排序
 	services.SortClassifyListBySortAndCreateTime(list)
 	// 接着转换结构

+ 507 - 0
controllers/data_manage/ai_predict_model/classify.go

@@ -0,0 +1,507 @@
+package ai_predict_model
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	aiPredictModel "eta/eta_api/models/ai_predict_model"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services"
+	"eta/eta_api/utils"
+	"fmt"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AiPredictModelClassifyController AI预测模型-分类
+type AiPredictModelClassifyController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 分类列表
+// @Description 分类列表
+// @Param   ParentId   query   bool   false   "父级ID"
+// @Success 200 {object} data_manage.ChartClassifyListResp
+// @router /classify/list [get]
+func (this *AiPredictModelClassifyController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	parentId, _ := this.GetInt("ParentId")
+	resp := new(aiPredictModel.AiPredictModelClassifyListResp)
+
+	// (懒加载)仅查询直属分类
+	classifyOb := new(aiPredictModel.AiPredictModelClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, parentId)
+		list, e := classifyOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", classifyOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取子分类失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			resp.AllNodes = append(resp.AllNodes, &aiPredictModel.AiPredictModelClassifyListItem{
+				NodeName:     v.ClassifyName,
+				ClassifyId:   v.AiPredictModelClassifyId,
+				ClassifyName: v.ClassifyName,
+				ParentId:     v.ParentId,
+				Level:        v.Level,
+				Sort:         v.Sort,
+				UniqueCode:   v.UniqueCode,
+			})
+		}
+	}
+
+	// 非顶级目录查询指标
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	if parentId > 0 {
+		parentClassify, e := classifyOb.GetItemById(parentId)
+		if e != nil {
+			br.Msg = "父级分类不存在, 请刷新页面"
+			return
+		}
+
+		cond := fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, parentId)
+		list, e := indexOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", indexOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取分类下指标失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			resp.AllNodes = append(resp.AllNodes, &aiPredictModel.AiPredictModelClassifyListItem{
+				NodeType:     1,
+				NodeName:     v.IndexName,
+				ClassifyId:   parentClassify.AiPredictModelClassifyId,
+				ClassifyName: parentClassify.ClassifyName,
+				IndexId:      v.AiPredictModelIndexId,
+				IndexCode:    v.IndexCode,
+				IndexName:    v.IndexName,
+				ParentId:     parentId,
+				Sort:         v.Sort,
+				UniqueCode:   v.IndexCode,
+			})
+		}
+	}
+	sort.Slice(resp.AllNodes, func(i, j int) bool {
+		return resp.AllNodes[i].Sort < resp.AllNodes[j].Sort
+	})
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增分类
+// @Description 新增分类
+// @Param	request	body aiPredictModel.AiPredictModelClassifyAddReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /classify/add [post]
+func (this *AiPredictModelClassifyController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req aiPredictModel.AiPredictModelClassifyAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "请选择上级分类"
+		return
+	}
+	if req.Level > 5 {
+		br.Msg = "目前只支持6级目录"
+		return
+	}
+
+	// 校验分类名称
+	classifyOb := new(aiPredictModel.AiPredictModelClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		if this.Lang == utils.EnLangVersion {
+			cond += fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ClassifyNameEn)
+		} else {
+			cond += fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ClassifyName)
+		}
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ParentId, req.ClassifyName)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类名称重复数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在"
+			return
+		}
+	}
+
+	// 层级路径
+	var levelPath string
+	var rootId int
+	if req.ParentId > 0 {
+		parent, e := classifyOb.GetItemById(req.ParentId)
+		if e != nil {
+			br.Msg = "上级分类有误"
+			br.ErrMsg = fmt.Sprintf("获取上级分类失败, %v", e)
+			return
+		}
+		levelPath = parent.LevelPath
+		rootId = parent.RootId
+	}
+
+	sortMax, e := classifyOb.GetSortMax(req.ParentId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类最大排序失败, %v", e)
+		return
+	}
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	classifyOb.ParentId = req.ParentId
+	classifyOb.ClassifyName = req.ClassifyName
+	classifyOb.ClassifyNameEn = req.ClassifyName
+	classifyOb.Level = req.Level + 1
+	classifyOb.Sort = sortMax + 1
+	classifyOb.SysUserId = sysUser.AdminId
+	classifyOb.SysUserRealName = sysUser.RealName
+	classifyOb.UniqueCode = utils.MD5(classifyOb.TableName() + "_" + timestamp)
+	classifyOb.CreateTime = time.Now().Local()
+	classifyOb.ModifyTime = time.Now().Local()
+	if e = classifyOb.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("新增分类失败, %v", e)
+		return
+	}
+	if req.ParentId > 0 {
+		// 用英文逗号拼接方便查询
+		classifyOb.LevelPath = fmt.Sprintf("%s,%d", levelPath, classifyOb.AiPredictModelClassifyId)
+		classifyOb.RootId = rootId
+	} else {
+		classifyOb.LevelPath = fmt.Sprint(classifyOb.AiPredictModelClassifyId)
+		classifyOb.RootId = classifyOb.AiPredictModelClassifyId
+	}
+	if e = classifyOb.Update([]string{classifyOb.Cols().LevelPath, classifyOb.Cols().RootId}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新分类失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "操作成功"
+	br.Success = true
+}
+
+// Edit
+// @Title 修改分类
+// @Description 修改分类
+// @Param	request	body aiPredictModel.AiPredictModelClassifyEditReq true "type json string"
+// @Success 200 Ret=200 修改成功
+// @router /classify/edit [post]
+func (this *AiPredictModelClassifyController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req aiPredictModel.AiPredictModelClassifyEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId < 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+
+	classifyOb := new(aiPredictModel.AiPredictModelClassify)
+	classifyItem, e := classifyOb.GetItemById(req.ClassifyId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+		return
+	}
+
+	// 校验分类名称
+	{
+		cond := fmt.Sprintf(" AND %s <> ?", classifyOb.Cols().PrimaryId)
+		if this.Lang == utils.EnLangVersion {
+			cond += fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ClassifyNameEn)
+		} else {
+			cond += fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ClassifyName)
+		}
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ClassifyId, req.ClassifyName)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类名称重复数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在"
+			return
+		}
+	}
+	classifyItem.ClassifyName = req.ClassifyName
+	classifyItem.ClassifyNameEn = req.ClassifyName
+	classifyItem.ModifyTime = time.Now().Local()
+	updateCols := []string{classifyOb.Cols().ClassifyName, classifyOb.Cols().ClassifyNameEn, classifyOb.Cols().ModifyTime}
+	if e = classifyItem.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新分类失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "操作成功"
+	br.Success = true
+}
+
+// RemoveCheck
+// @Title 删除校验
+// @Description 删除校验
+// @Param	request	body aiPredictModel.AiPredictModelClassifyRemoveReq true "type json string"
+// @Success 200 Ret=200 检测成功
+// @router /classify/remove_check [post]
+func (this *AiPredictModelClassifyController) RemoveCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req aiPredictModel.AiPredictModelClassifyRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId < 0 && req.IndexId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	var deleteStatus int
+	var tipsMsg string
+	// 删除分类
+	if req.ClassifyId > 0 && req.IndexId == 0 {
+		count, err := aiPredictModel.GetAiPredictModelIndexCountByClassifyId(req.ClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有图表失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			deleteStatus = 1
+			tipsMsg = "该分类下关联图表不可删除"
+		}
+	}
+
+	if deleteStatus != 1 && req.IndexId == 0 {
+		classifyCount, err := aiPredictModel.GetAiPredictModelClassifyCountByClassifyId(req.ClassifyId)
+		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
+}
+
+// Remove
+// @Title 删除分类/标的
+// @Description 删除分类/标的
+// @Param	request	body aiPredictModel.AiPredictModelClassifyRemoveReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /classify/remove [post]
+func (this *AiPredictModelClassifyController) Remove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req aiPredictModel.AiPredictModelClassifyRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId < 0 && req.IndexId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	// 删除分类
+	if req.ClassifyId > 0 && req.IndexId == 0 {
+		count, err := aiPredictModel.GetAiPredictModelIndexCountByClassifyId(req.ClassifyId)
+		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 = aiPredictModel.RemoveAiPredictModelClassify(req.ClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	// 删除标的
+	if req.IndexId > 0 {
+		indexOb := new(aiPredictModel.AiPredictModelIndex)
+		_, e := indexOb.GetItemById(req.IndexId)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Ret = 200
+				br.Msg = "删除成功"
+				br.Success = true
+				return
+			}
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取标的信息失败, %v", e)
+			return
+		}
+
+		// 删除标的及数据
+		if e = indexOb.RemoveIndexAndData(req.IndexId); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("删除标的及数据失败, %v", e)
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// Move
+// @Title 移动
+// @Description 移动
+// @Success 200 {object} aiPredictModel.AiPredictModelClassifyMoveReq
+// @router /classify/move [post]
+func (this *AiPredictModelClassifyController) Move() {
+	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 aiPredictModel.AiPredictModelClassifyMoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId <= 0 && req.ItemId <= 0 {
+		br.Msg = "请选择分类或指标"
+		return
+	}
+
+	err, errMsg := services.AiPredictModelMoveClassify(req, sysUser)
+	if errMsg != `` {
+		br.Msg = errMsg
+		br.ErrMsg = errMsg
+		if err != nil {
+			br.ErrMsg = err.Error()
+		} else {
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}

+ 940 - 0
controllers/data_manage/ai_predict_model/index.go

@@ -0,0 +1,940 @@
+package ai_predict_model
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	aiPredictModel "eta/eta_api/models/ai_predict_model"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AiPredictModelIndexController AI预测模型标的
+type AiPredictModelIndexController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 标的列表
+// @Description 标的列表
+// @Param   PageSize   query   int   true   "每页数据条数"
+// @Param   CurrentIndex   query   int   true   "当前页页码,从1开始"
+// @Param   ClassifyId   query   int   false   "分类id"
+// @Param   Keyword   query   string   false   "搜索关键词"
+// @Success 200 {object} data_manage.ChartListResp
+// @router /index/list [get]
+func (this *AiPredictModelIndexController) 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
+	}
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	classifyId, _ := this.GetInt("ClassifyId")
+	keyword := this.GetString("KeyWord")
+	keyword = strings.TrimSpace(keyword)
+	resp := new(aiPredictModel.AiPredictModelIndexPageListResp)
+
+	// 分页
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	// 分类
+	classifyIdName := make(map[int]string)
+	{
+		classifyOb := new(aiPredictModel.AiPredictModelClassify)
+		list, e := classifyOb.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			classifyIdName[v.AiPredictModelClassifyId] = v.ClassifyName
+		}
+	}
+
+	// 筛选条件
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	var cond string
+	var pars []interface{}
+	{
+		if classifyId > 0 {
+			cond += fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
+			pars = append(pars, classifyId)
+		}
+		if keyword != "" {
+			cond += fmt.Sprintf(" AND %s LIKE ?", indexOb.Cols().IndexName)
+			pars = append(pars, fmt.Sprint("%", keyword, "%"))
+		}
+	}
+
+	// 获取列表
+	total, e := indexOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取标的总数失败, %v", e)
+		return
+	}
+	list, e := indexOb.GetPageItemsByCondition(cond, pars, []string{}, "", startSize, pageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取分页列表失败, %v", e)
+		return
+	}
+	pageList := make([]*aiPredictModel.AiPredictModelIndexItem, 0)
+	for _, v := range list {
+		t := v.Format2Item()
+		t.ClassifyName = classifyIdName[v.ClassifyId]
+		pageList = append(pageList, t)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp.Paging = page
+	resp.List = pageList
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Import
+// @Title 导入标的和数据
+// @Description 导入标的和数据
+// @Param   IndexFile   query   file   true   "标的文件"
+// @Success 200 Ret=200 录入成功
+// @router /index/import [post]
+func (this *AiPredictModelIndexController) Import() {
+	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
+	}
+
+	file, _, e := this.GetFile("IndexFile")
+	if e != nil {
+		br.Msg = "导入失败"
+		br.ErrMsg = fmt.Sprintf("获取文件失败, %v", e)
+		return
+	}
+	path := "./static/ai_predict_model_temp_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	defer func() {
+		_ = file.Close()
+		_ = os.Remove(path)
+	}()
+	if e = this.SaveToFile("IndexFile", path); e != nil {
+		br.Msg = "导入失败"
+		br.ErrMsg = fmt.Sprintf("保存文件失败, %v", e)
+		return
+	}
+	xlFile, e := xlsx.OpenFile(path)
+	if e != nil {
+		br.Msg = "导入失败"
+		br.ErrMsg = fmt.Sprintf("打开excel文件失败, %v", e)
+		return
+	}
+
+	// 获取分类和用户,遍历时校验
+	classifyNameId := make(map[string]int)
+	adminNameId := make(map[string]int)
+	{
+		classifyOb := new(aiPredictModel.AiPredictModelClassify)
+		classifyCond := fmt.Sprintf(` AND %s = ?`, classifyOb.Cols().ParentId)
+		classifyPars := make([]interface{}, 0)
+		classifyPars = append(classifyPars, 0) // 只取一级分类(临时过渡方案,业务端只会加一级)
+		classifies, e := classifyOb.GetItemsByCondition(classifyCond, classifyPars, []string{}, "")
+		if e != nil {
+			br.Msg = "导入失败"
+			br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+			return
+		}
+		for _, v := range classifies {
+			classifyNameId[v.ClassifyName] = v.AiPredictModelClassifyId
+		}
+
+		admins, e := system.GetSysAdminList(``, make([]interface{}, 0), []string{}, "")
+		if e != nil {
+			br.Msg = "导入失败"
+			br.ErrMsg = fmt.Sprintf("获取用户失败, %v", e)
+			return
+		}
+		for _, v := range admins {
+			adminNameId[v.RealName] = v.AdminId
+		}
+	}
+
+	// 遍历sheet页
+	// 列表页:预测标的|分类|模型框架|创建人|预测日期|预测值|预测频度|方向准确率|绝对偏差
+	type ImportDataColKey struct {
+		IndexName string
+		ColKey    int
+		DataDate  time.Time
+	}
+	imports := make(map[string]*aiPredictModel.AiPredictModelImportData)
+	importsData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
+	importsDailyData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
+	for sheetKey, sheet := range xlFile.Sheets {
+		maxRow := sheet.MaxRow
+
+		// 列表页
+		if sheetKey == 0 {
+			for i := 0; i < maxRow; i++ {
+				// 忽略首行标题
+				if i < 1 {
+					continue
+				}
+				row := sheet.Row(i)
+				cells := row.Cells
+				if len(cells) < 9 {
+					continue
+				}
+
+				// 标的名称
+				indexName := strings.TrimSpace(cells[0].String())
+				if indexName == "" {
+					continue
+				}
+				if imports[indexName] == nil {
+					imports[indexName] = new(aiPredictModel.AiPredictModelImportData)
+					imports[indexName].Index = new(aiPredictModel.AiPredictModelIndex)
+					imports[indexName].Data = make([]*aiPredictModel.AiPredictModelData, 0)
+				}
+				imports[indexName].Index.IndexName = indexName
+				imports[indexName].Index.CreateTime = time.Now()
+				imports[indexName].Index.ModifyTime = time.Now()
+
+				// 分类
+				classifyName := strings.TrimSpace(cells[1].String())
+				if classifyNameId[classifyName] <= 0 {
+					br.Msg = fmt.Sprintf("分类:%s不存在", classifyName)
+					return
+				}
+				imports[indexName].Index.ClassifyId = classifyNameId[classifyName]
+
+				// 创建人
+				adminName := strings.TrimSpace(cells[3].String())
+				if adminNameId[adminName] <= 0 {
+					br.Msg = fmt.Sprintf("创建人:%s不存在", adminName)
+					return
+				}
+				imports[indexName].Index.SysUserId = adminNameId[adminName]
+				imports[indexName].Index.SysUserRealName = adminName
+
+				// 其余信息
+				imports[indexName].Index.ModelFramework = strings.TrimSpace(cells[2].String())
+				strDate := strings.TrimSpace(cells[4].String())
+				predictDate, _ := time.Parse("2006/01/02", strDate)
+				if predictDate.IsZero() {
+					predictDate, _ = time.Parse("01-02-06", strDate)
+					if predictDate.IsZero() {
+						predictDate, _ = time.Parse("2006/1/2", strDate)
+					}
+				}
+				imports[indexName].Index.PredictDate = predictDate
+
+				strVal := strings.TrimSpace(cells[5].String())
+				if strVal == "" {
+					continue
+				}
+				predictVal, _ := strconv.ParseFloat(strVal, 64)
+				imports[indexName].Index.PredictValue = predictVal
+				imports[indexName].Index.PredictFrequency = strings.TrimSpace(cells[6].String())
+				imports[indexName].Index.DirectionAccuracy = strings.TrimSpace(cells[7].String())
+				imports[indexName].Index.AbsoluteDeviation = strings.TrimSpace(cells[8].String())
+			}
+		}
+
+		// 月度数据页
+		if sheetKey == 1 {
+			// 每五列为一个指标的数据
+			colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
+			for i := 0; i < maxRow; i++ {
+				// 首行为指标名称
+				if i == 0 {
+					nameCol := 0
+					row := sheet.Row(i)
+					for ck, cell := range row.Cells {
+						nameCol += 1
+						if nameCol > 5 {
+							nameCol = 1
+						}
+						if nameCol == 1 {
+							// nameCol=1时为指标/数据行则为日期
+							indexName := strings.TrimSpace(cell.String())
+							if indexName == "" {
+								continue
+							}
+							importsData[indexName] = make(map[time.Time]*aiPredictModel.AiPredictModelData)
+
+							colKeys[ck] = &ImportDataColKey{
+								ColKey:    1,
+								IndexName: indexName,
+							}
+
+							// 后面四列分别对应: 实际值|预测值|方向|偏差率, 这里直接加无须考虑是否会越界
+							colKeys[ck+1] = &ImportDataColKey{
+								ColKey:    2,
+								IndexName: indexName,
+							}
+							colKeys[ck+2] = &ImportDataColKey{
+								ColKey:    3,
+								IndexName: indexName,
+							}
+							colKeys[ck+3] = &ImportDataColKey{
+								ColKey:    4,
+								IndexName: indexName,
+							}
+							colKeys[ck+4] = &ImportDataColKey{
+								ColKey:    5,
+								IndexName: indexName,
+							}
+							continue
+						}
+					}
+					continue
+				}
+
+				// 第二行为标题,跳过
+				if i == 1 {
+					continue
+				}
+
+				// 剩余为数据行
+				row := sheet.Row(i)
+				for ck, cell := range row.Cells {
+					if colKeys[ck] == nil {
+						continue
+					}
+					if colKeys[ck].IndexName == "" {
+						continue
+					}
+					switch colKeys[ck].ColKey {
+					case 1:
+						// 日期列
+						strDate := strings.TrimSpace(cell.String())
+						dataDate, _ := time.Parse("2006/01/02", strDate)
+						if dataDate.IsZero() {
+							dataDate, _ = time.Parse("01-02-06", strDate)
+							if dataDate.IsZero() {
+								dataDate, _ = time.Parse("2006/1/2", strDate)
+								if dataDate.IsZero() {
+									continue
+								}
+							}
+						}
+						colKeys[ck].DataDate = dataDate
+						colKeys[ck+1].DataDate = dataDate
+						colKeys[ck+2].DataDate = dataDate
+						colKeys[ck+3].DataDate = dataDate
+						colKeys[ck+4].DataDate = dataDate
+						importRow := imports[colKeys[ck].IndexName]
+						if importRow == nil {
+							continue
+						}
+						// 新增当前日期数据
+						importsData[colKeys[ck].IndexName][dataDate] = new(aiPredictModel.AiPredictModelData)
+						importsData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
+						importsData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
+						importsData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
+						importsData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceMonthly
+					case 2, 3:
+						// 实际值和预测值, 可能为空
+						dataDate := colKeys[ck].DataDate
+						if importsData[colKeys[ck].IndexName][dataDate] == nil {
+							continue
+						}
+						strVal := strings.TrimSpace(cell.String())
+						if strVal == "" {
+							continue
+						}
+						val, _ := strconv.ParseFloat(strVal, 64)
+						if colKeys[ck].ColKey == 2 {
+							importsData[colKeys[ck].IndexName][dataDate].Value.Valid = true
+							importsData[colKeys[ck].IndexName][dataDate].Value.Float64 = val
+						} else {
+							importsData[colKeys[ck].IndexName][dataDate].PredictValue.Valid = true
+							importsData[colKeys[ck].IndexName][dataDate].PredictValue.Float64 = val
+						}
+					case 4, 5:
+						// 方向/偏差率
+						dataDate := colKeys[ck].DataDate
+						if importsData[colKeys[ck].IndexName][dataDate] == nil {
+							continue
+						}
+						str := strings.TrimSpace(cell.String())
+						if str == "" {
+							continue
+						}
+						if colKeys[ck].ColKey == 4 {
+							importsData[colKeys[ck].IndexName][dataDate].Direction = str
+						} else {
+							importsData[colKeys[ck].IndexName][dataDate].DeviationRate = str
+						}
+					default:
+						continue
+					}
+				}
+			}
+		}
+
+		// 日度数据页
+		if sheetKey == 2 {
+			// 每3列为一个指标的数据
+			colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
+			for i := 0; i < maxRow; i++ {
+				// 首行为指标名称
+				if i == 0 {
+					nameCol := 0
+					row := sheet.Row(i)
+					for ck, cell := range row.Cells {
+						nameCol += 1
+						if nameCol > 3 {
+							nameCol = 1
+						}
+						if nameCol == 1 {
+							// nameCol=1时为指标/数据行则为日期
+							indexName := strings.TrimSpace(cell.String())
+							if indexName == "" {
+								continue
+							}
+							importsDailyData[indexName] = make(map[time.Time]*aiPredictModel.AiPredictModelData)
+
+							colKeys[ck] = &ImportDataColKey{
+								ColKey:    1,
+								IndexName: indexName,
+							}
+
+							// 后面两列分别对应: 实际值|预测值, 这里直接加无须考虑是否会越界
+							colKeys[ck+1] = &ImportDataColKey{
+								ColKey:    2,
+								IndexName: indexName,
+							}
+							colKeys[ck+2] = &ImportDataColKey{
+								ColKey:    3,
+								IndexName: indexName,
+							}
+							continue
+						}
+					}
+					continue
+				}
+
+				// 第二行为标题,遇到"预测值"单元格,需要取出其中的值作为预测图例名称
+				if i == 1 {
+					row := sheet.Row(i)
+					for ck, cell := range row.Cells {
+						if colKeys[ck] == nil {
+							continue
+						}
+						if colKeys[ck].IndexName == "" {
+							continue
+						}
+						if colKeys[ck].ColKey != 3 {
+							continue
+						}
+						if imports[colKeys[ck].IndexName] != nil && imports[colKeys[ck].IndexName].Index != nil {
+							var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
+							extraConfig.DailyChart.PredictLegendName = strings.TrimSpace(cell.String())
+							b, _ := json.Marshal(extraConfig)
+							imports[colKeys[ck].IndexName].Index.ExtraConfig = string(b)
+						}
+					}
+					continue
+				}
+
+				// 剩余为数据行
+				row := sheet.Row(i)
+				for ck, cell := range row.Cells {
+					if colKeys[ck] == nil {
+						continue
+					}
+					if colKeys[ck].IndexName == "" {
+						continue
+					}
+					switch colKeys[ck].ColKey {
+					case 1:
+						// 日期列
+						strDate := strings.TrimSpace(cell.String())
+						dataDate, _ := time.Parse("2006/01/02", strDate)
+						if dataDate.IsZero() {
+							dataDate, _ = time.Parse("01-02-06", strDate)
+							if dataDate.IsZero() {
+								dataDate, _ = time.Parse("2006/1/2", strDate)
+								if dataDate.IsZero() {
+									continue
+								}
+							}
+						}
+						colKeys[ck].DataDate = dataDate
+						colKeys[ck+1].DataDate = dataDate
+						colKeys[ck+2].DataDate = dataDate
+						importRow := imports[colKeys[ck].IndexName]
+						if importRow == nil {
+							continue
+						}
+						// 新增当前日期数据
+						importsDailyData[colKeys[ck].IndexName][dataDate] = new(aiPredictModel.AiPredictModelData)
+						importsDailyData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
+						importsDailyData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
+						importsDailyData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
+						importsDailyData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceDaily
+					case 2, 3:
+						// 实际值和预测值, 可能为空
+						dataDate := colKeys[ck].DataDate
+						if importsDailyData[colKeys[ck].IndexName][dataDate] == nil {
+							continue
+						}
+						strVal := strings.TrimSpace(cell.String())
+						if strVal == "" {
+							continue
+						}
+						val, _ := strconv.ParseFloat(strVal, 64)
+						if colKeys[ck].ColKey == 2 {
+							importsDailyData[colKeys[ck].IndexName][dataDate].Value.Valid = true
+							importsDailyData[colKeys[ck].IndexName][dataDate].Value.Float64 = val
+						} else {
+							importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Valid = true
+							importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Float64 = val
+						}
+					default:
+						continue
+					}
+				}
+			}
+		}
+	}
+
+	for indexName, v := range importsData {
+		if imports[indexName] == nil {
+			continue
+		}
+		for _, dateData := range v {
+			imports[indexName].Data = append(imports[indexName].Data, dateData)
+		}
+	}
+	for indexName, v := range importsDailyData {
+		if imports[indexName] == nil {
+			continue
+		}
+		for _, dateData := range v {
+			imports[indexName].Data = append(imports[indexName].Data, dateData)
+		}
+	}
+	importIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
+	for _, v := range imports {
+		importIndexes = append(importIndexes, v)
+	}
+
+	// 导入指标
+	if e = services.ImportAiPredictModelIndexAndData(importIndexes); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("导入指标数据失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// Detail
+// @Title 标的详情
+// @Description 标的详情
+// @Param   IndexId   query   int   true   "标的ID"
+// @Success 200 {object} data_manage.ChartListResp
+// @router /index/detail [get]
+func (this *AiPredictModelIndexController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	indexId, _ := this.GetInt("IndexId")
+	if indexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", indexId)
+		return
+	}
+	resp := new(aiPredictModel.AiPredictModelDetailResp)
+
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	indexItem, e := indexOb.GetItemById(indexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "标的已被删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
+		return
+	}
+
+	// 获取标的数据
+	monthData, dailyData := make([]*aiPredictModel.AiPredictModelData, 0), make([]*aiPredictModel.AiPredictModelData, 0)
+	{
+		tableData := make([]*aiPredictModel.AiPredictModelDataItem, 0)
+		dataOb := new(aiPredictModel.AiPredictModelData)
+		dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
+		dataPars := make([]interface{}, 0)
+		dataPars = append(dataPars, indexItem.IndexCode)
+		list, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取标的数据失败, %v", e)
+			return
+		}
+
+		// tableData取月度数据,最多显示10条
+		count, limit := 0, 10
+		for _, v := range list {
+			// 日度数据
+			if v.Source == aiPredictModel.ModelDataSourceDaily {
+				dailyData = append(dailyData, v)
+				continue
+			}
+
+			// 月度数据
+			if count < limit {
+				tableData = append(tableData, v.Format2Item())
+				count += 1
+			}
+			monthData = append(monthData, v)
+		}
+		resp.TableData = tableData
+	}
+
+	// 月度图表
+	if len(monthData) > 0 {
+		chartDetail, e := services.GetAiPredictChartDetailByData(indexItem, monthData, aiPredictModel.ModelDataSourceMonthly)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取月度图表失败, %v", e)
+			return
+		}
+		resp.ChartView = chartDetail
+	}
+
+	// 日度图表
+	if len(dailyData) > 0 {
+		dailyChartDetail, e := services.GetAiPredictChartDetailByData(indexItem, dailyData, aiPredictModel.ModelDataSourceDaily)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取日度图表失败, %v", e)
+			return
+		}
+		resp.DailyChartView = dailyChartDetail
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Save
+// @Title 保存标的
+// @Description 保存标的
+// @Param	request	body aiPredictModel.AiPredictModelIndexSaveReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /index/save [post]
+func (this *AiPredictModelIndexController) Save() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req aiPredictModel.AiPredictModelIndexSaveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.IndexId < 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("标的ID有误, IndexId: %d", req.IndexId)
+		return
+	}
+
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	indexItem, e := indexOb.GetItemById(req.IndexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "标的已被删除,请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
+		return
+	}
+
+	var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
+	if indexItem.ExtraConfig != "" {
+		if e = json.Unmarshal([]byte(indexItem.ExtraConfig), &extraConfig); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("标的配置解析失败, %v", e)
+			return
+		}
+	}
+	if req.MonthlyChart != nil {
+		extraConfig.MonthlyChart.LeftMin = req.MonthlyChart.LeftMin
+		extraConfig.MonthlyChart.LeftMax = req.MonthlyChart.LeftMax
+		extraConfig.MonthlyChart.Unit = req.MonthlyChart.Unit
+	}
+	if req.DailyChart != nil {
+		extraConfig.DailyChart.LeftMin = req.DailyChart.LeftMin
+		extraConfig.DailyChart.LeftMax = req.DailyChart.LeftMax
+		extraConfig.DailyChart.Unit = req.DailyChart.Unit
+	}
+
+	configByte, _ := json.Marshal(extraConfig)
+	indexItem.ExtraConfig = string(configByte)
+	indexItem.ModifyTime = time.Now()
+	updateCols := []string{indexOb.Cols().ExtraConfig, indexOb.Cols().ModifyTime}
+	if e = indexItem.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("保存标的失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "操作成功"
+	br.Success = true
+}
+
+// DashboardSave
+// @Title 保存看板
+// @Description 保存看板
+// @Param	request	body aiPredictModel.AiPredictModelDashboardSaveReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /index/dashboard/save [post]
+func (this *AiPredictModelIndexController) DashboardSave() {
+	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 aiPredictModel.AiPredictModelDashboardSaveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, %d", req.IndexId)
+		return
+	}
+	req.DashboardName = strings.TrimSpace(req.DashboardName)
+
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	_, e := indexOb.GetItemById(req.IndexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "标的已被删除,请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
+		return
+	}
+
+	// 获取看板
+	var isUpdate bool
+	var updateCols []string
+	dashboardItem := new(aiPredictModel.AiPredictModelDashboard)
+	if req.IndexId > 0 {
+		cond := fmt.Sprintf(" AND %s = ?", dashboardItem.Cols().AiPredictModelIndexId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.IndexId)
+		item, e := dashboardItem.GetItemByCondition(cond, pars, "")
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取标的看板失败, %v", e)
+			return
+		}
+		if item != nil {
+			isUpdate = true
+			dashboardItem = item
+			dashboardItem.DashboardName = req.DashboardName
+			dashboardItem.ModifyTime = time.Now()
+			updateCols = append(updateCols, dashboardItem.Cols().DashboardName, dashboardItem.Cols().ModifyTime)
+		}
+	}
+	if !isUpdate {
+		dashboardItem.AiPredictModelIndexId = req.IndexId
+		dashboardItem.DashboardName = req.DashboardName
+		dashboardItem.SysUserId = sysUser.AdminId
+		dashboardItem.SysUserRealName = sysUser.RealName
+		dashboardItem.CreateTime = time.Now()
+		dashboardItem.ModifyTime = time.Now()
+	}
+
+	// 详情
+	dashboardDetails := make([]*aiPredictModel.AiPredictModelDashboardDetail, 0)
+	for i, v := range req.List {
+		t := &aiPredictModel.AiPredictModelDashboardDetail{
+			Type:       v.Type,
+			UniqueCode: v.UniqueCode,
+			Sort:       i + 1,
+			CreateTime: time.Now(),
+			ModifyTime: time.Now(),
+		}
+		dashboardDetails = append(dashboardDetails, t)
+	}
+
+	// 保存
+	if e := dashboardItem.SaveIndexDashboard(dashboardItem, dashboardDetails, isUpdate, updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("保存标的看板失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// DashboardDetail
+// @Title 看板详情
+// @Description 看板详情
+// @Param   IndexId   query   int   true   "标的ID"
+// @Success 200 {object} aiPredictModel.AiPredictModelDashboardDetailResp
+// @router /index/dashboard/detail [get]
+func (this *AiPredictModelIndexController) DashboardDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	indexId, _ := this.GetInt("IndexId")
+	resp := new(aiPredictModel.AiPredictModelDashboardDetailResp)
+
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	indexItem, e := indexOb.GetItemById(indexId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "标的已被删除,请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
+		return
+	}
+	resp.CreateUserId = indexItem.SysUserId
+	resp.CreateUserRealName = indexItem.SysUserRealName
+
+	// 获取标的看板
+	dashboardOb := new(aiPredictModel.AiPredictModelDashboard)
+	dashboardItem := new(aiPredictModel.AiPredictModelDashboardItem)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", dashboardOb.Cols().AiPredictModelIndexId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, indexId)
+		item, e := dashboardOb.GetItemByCondition(cond, pars, "")
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取标的看板失败, %v", e)
+			return
+		}
+		if item != nil {
+			dashboardItem = item.Format2Item()
+		}
+	}
+
+	// 获取看板详情
+	dashboardDetails := make([]*aiPredictModel.AiPredictModelDashboardDetailItem, 0)
+	if dashboardItem.DashboardId > 0 {
+		detailOb := new(aiPredictModel.AiPredictModelDashboardDetail)
+		cond := fmt.Sprintf(" AND %s = ?", detailOb.Cols().AiPredictModelDashboardId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, dashboardItem.DashboardId)
+		list, e := detailOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", detailOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取看板详情失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			dashboardDetails = append(dashboardDetails, v.Format2Item())
+		}
+	}
+
+	resp.AiPredictModelDashboardItem = dashboardItem
+	resp.List = dashboardDetails
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 708 - 0
controllers/data_manage/base_from_rzd_index_controller.go

@@ -0,0 +1,708 @@
+// Package data_manage
+// @Author gmy 2024/8/12 14:31:00
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// BaseFromRzdIndexController 睿姿得数据控制器
+type BaseFromRzdIndexController struct {
+	controllers.BaseAuthController
+}
+
+// RzdClassify
+// @Title 睿姿得数据分类
+// @Description 汾渭数据分类接口
+// @Success 200 {object} data_manage.BaseFromRzdClassifyResponse
+// @router /rzd/classify [get]
+func (this *BaseFromRzdIndexController) RzdClassify() {
+	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
+	}
+
+	classifies, e := data.RzdClassifyList()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取睿姿得数据分类失败, Err: " + e.Error()
+		return
+	}
+
+	br.Data = classifies
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// RzdIndexData
+// @Title 获取睿姿得数据
+// @Description 获取睿姿得数据
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   string  true       "分类id"
+// @Param   Frequency   query   string  true       "频率"
+// @Success 200
+// @router /rzd/index/data [get]
+func (this *BaseFromRzdIndexController) RzdIndexData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+	frequency := this.GetString("Frequency")
+
+	resultList, err := data.RzdIndexData(classifyId, frequency, currentIndex, startSize, pageSize)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resultList
+}
+
+// RzdIndexDetail
+// @Title 获取睿姿得数据指标详情
+// @Description 获取睿姿得数据指标详情
+// @Param   IndexCode   query   string  true       "查询参数 指标id"
+// @Success 200
+// @router /rzd/index/detail [get]
+func (this *BaseFromRzdIndexController) RzdIndexDetail() {
+	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
+	}
+
+	indexCode := this.GetString("IndexCode")
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	indexDetail, err := data.GetRzdIndexDetail(indexCode, currentIndex, startSize, pageSize)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = indexDetail
+}
+
+// RzdIndexList
+// @Title 获取睿姿得数据指标列表
+// @Description 获取睿姿得数据指标详情
+// @Param   searchParams   query   string  true       "查询参数 指标id/指标名称"
+// @Success 200
+// @router /rzd/index/list [get]
+func (this *BaseFromRzdIndexController) RzdIndexList() {
+	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
+	}
+
+	searchParams := this.GetString("SearchParams")
+
+	indexList, err := data.GetRzdIndexList(searchParams)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = indexList
+}
+
+// GetRzdFrequencyList
+// @Title 查询频率列表
+// @Description 查询频率列表
+// @Param   classifyId   query  int  false   "指标唯一编码"
+// @Success 200 {object} []string
+// @router /rzd/frequency/list [get]
+func (this *BaseFromRzdIndexController) GetRzdFrequencyList() {
+	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
+	}
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+	frequencyList, err := data.GetRzdIndexFrequency(classifyId)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = frequencyList
+}
+
+// RzdIndexAddValidate
+// @Title 新增加入到指标库校验
+// @Description 新增加入到指标库校验
+// @Param   req    body   data_manage.BaseFromFenWeiIndexBatchAddCheckReq     true        "请求参数"
+// @Success 200 {object} []data_manage.IndexCheckData
+// @router /rzd/index/add/validate [post]
+func (this *BaseFromRzdIndexController) RzdIndexAddValidate() {
+	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.BaseFromRzdIndexBatchAddCheckReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+
+	// 校验指标编码是否存在
+	addValidate, err := data.RzdIndexAddValidate(req)
+	if err != nil {
+		br.Ret = 403
+		br.Success = false
+		br.Msg = err.Error()
+		return
+	}
+	br.Data = addValidate
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// RzdIndexAdd
+// @Title 指标添加到指标库
+// @Description 指标添加到指标库
+// @Param   req    body   []data_manage.AddEdbInfoReq     true        "请求参数"
+// @Success 200 string "操作成功"
+// @router /rzd/index/add [post]
+func (this *BaseFromRzdIndexController) RzdIndexAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_RZD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > 30 {
+		br.Msg = "批量添加指标数量不得超过" + strconv.Itoa(30) + "个"
+		return
+	}
+
+	indexNames := make([]string, 0)
+	resp := make([]*data_manage.RzdNameCheckResult, 0)
+	for _, index := range req {
+		index.EdbCode = strings.TrimSpace(index.EdbCode)
+		if index.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		index.EdbName = strings.TrimSpace(index.EdbName)
+		if index.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		index.Frequency = strings.TrimSpace(index.Frequency)
+		if index.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		index.Unit = strings.TrimSpace(index.Unit)
+		if index.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if index.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+		indexNames = append(indexNames, index.EdbName)
+		resp = append(resp, &data_manage.RzdNameCheckResult{
+			IndexCode: index.EdbCode,
+			IndexName: index.EdbName,
+			Exist:     false,
+		})
+	}
+
+	// 指标名称重复校验
+	nameCheck, err := data.RzdIndexNameCheck(indexNames, resp)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = err.Error()
+		return
+	}
+	for _, v := range nameCheck {
+		if v.Exist {
+			br.Msg = "指标名称重复"
+			br.Data = nameCheck
+			br.Ret = 200
+			br.Success = true
+			return
+		}
+	}
+
+	for _, v := range req {
+		var rzdIndexAddReq data_manage.RzdIndexAddReq
+		rzdIndexAddReq.EdbCode = v.EdbCode
+		rzdIndexAddReq.EdbName = v.EdbName
+		rzdIndexAddReq.Frequency = v.Frequency
+		rzdIndexAddReq.Unit = v.Unit
+		rzdIndexAddReq.ClassifyId = v.ClassifyId
+		rzdIndexAddReq.AdminId = sysUser.AdminId
+		rzdIndexAddReq.AdminRealName = sysUser.RealName
+
+		// 新增指标到指标库
+		edbInfo, e, errMsg, skip := data.RzdIndexAdd(rzdIndexAddReq, this.Lang)
+		if e != nil {
+			br.Msg = "操作失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = e.Error()
+			return
+		}
+		if skip {
+			continue
+		}
+
+		// todo 下面两段代码能否抽离出来???
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(this.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = this.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// RzdIndexDataExport
+// @Title 导出指标数据
+// @Description 导出指标数据
+// @Param  IndexCode     query   string     false        "指标编码"
+// @Param  ClassifyId     query   int     false        "分类ID"
+// @Success 200 string "操作成功"
+// @router /rzd/index/data/export [get]
+func (this *BaseFromRzdIndexController) RzdIndexDataExport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	classifyId, _ := this.GetInt("ClassifyId") //分类
+	indexCode := this.GetString("IndexCode")   //指标唯一编码
+
+	if classifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+
+	downLoadFilePath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	classifyIdList, err := data.RzdClassifyRecursiveQuery(classifyId)
+	if err != nil {
+		br.Msg = "查询分类失败"
+		br.ErrMsg = "查询分类失败"
+		return
+	}
+	classifyIdList = append(classifyIdList, classifyId)
+
+	frequencies, err := data_manage.GetRzdIndexFrequency(classifyIdList)
+	if err != nil {
+		br.Msg = "查询频度失败"
+		br.ErrMsg = "查询频度失败"
+		return
+	}
+
+	fileName := `睿姿得数据`
+	if classifyId > 0 && indexCode == "" {
+		fenWeiClassify, err := data_manage.GetRzdClassifyItemByClassifyId(classifyId)
+		if err != nil {
+			return
+		}
+		fileName = fenWeiClassify.ClassifyName
+	}
+	if frequencies == nil {
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+	}
+
+	for _, frequency := range frequencies {
+		fenWeiIndices, err := data_manage.GetRzdIndexByCodeAndClassify(indexCode, classifyIdList, frequency)
+		if err != nil {
+			return
+		}
+		var sheet *xlsx.Sheet
+		if len(fenWeiIndices) > 0 {
+			sheetName := *frequency
+			if sheetName == "" {
+				sheetName = "无频度"
+			}
+			sheet, err = xlsxFile.AddSheet(sheetName)
+			if err != nil {
+				br.Msg = "新增Sheet失败"
+				br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+				return
+			}
+		} else {
+			continue
+		}
+
+		if indexCode != "" {
+			fileName = fenWeiIndices[0].IndexName
+		}
+
+		//获取指标数据
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("指标名称/Metric Nome")
+		rowFrequency := sheet.AddRow()
+		celFrequency := rowFrequency.AddCell()
+		celFrequency.SetValue("频率/Frequency")
+		rowUnit := sheet.AddRow()
+		celUnit := rowUnit.AddCell()
+		celUnit.SetValue("单位/Unit")
+		rowModifyDate := sheet.AddRow()
+		rowModifyCell := rowModifyDate.AddCell()
+		rowModifyCell.SetValue("更新时间/Update Time")
+
+		dataMap := make(map[string]map[string]*data_manage.BaseFromRzdData)
+		var tradeCodeList []string
+		for _, v := range fenWeiIndices {
+			cellSenName := rowSecName.AddCell()
+			cellSenName.SetValue(v.IndexName)
+			celFrequency := rowFrequency.AddCell()
+			celFrequency.SetValue(v.Frequency)
+			celUnit := rowUnit.AddCell()
+			celUnit.SetValue(v.Unit)
+			rowModifyCell := rowModifyDate.AddCell()
+			updateTimeStr := utils.FormatDateString(v.ModifyTime)
+			rowModifyCell.SetValue(updateTimeStr)
+			tradeCodeList = append(tradeCodeList, v.IndexCode)
+
+			var dataList []*data_manage.BaseFromRzdData
+			dataList, err = data_manage.GetBaseFormRzdDataByIndexCode(v.IndexCode)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.ErrMsg = "GetBaseFormRzdDataByIndexCode,Err:" + err.Error()
+				br.Msg = "获取数据失败"
+				return
+			}
+			for _, item := range dataList {
+				if dataMap[item.IndexCode] == nil {
+					dataMap[item.IndexCode] = make(map[string]*data_manage.BaseFromRzdData)
+				}
+				dataMap[item.IndexCode][item.DataTime] = item
+			}
+		}
+
+		tradeCodeStr := strings.Join(tradeCodeList, "','")
+		tradeCodeStr = "'" + tradeCodeStr + "'"
+		dataTimeList, err := data_manage.GetRzdDataListByIndexCodes(tradeCodeStr)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		for _, dataTime := range dataTimeList {
+			rowData := sheet.AddRow()
+			celDate := rowData.AddCell()
+			celDate.SetValue(dataTime)
+
+			for _, m := range fenWeiIndices {
+				celData := rowData.AddCell()
+				if dataMap[m.IndexCode][dataTime] != nil {
+					celData.SetValue(dataMap[m.IndexCode][dataTime].Value)
+				}
+			}
+		}
+	}
+
+	err = xlsxFile.Save(downLoadFilePath)
+	if err != nil {
+		//有指标无数据时先导出一遍空表
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		err = xlsxFile.Save(downLoadFilePath)
+		if err != nil {
+			br.Msg = "保存文件失败"
+			br.ErrMsg = "保存文件失败"
+			return
+		}
+	}
+
+	fileName += time.Now().Format("06.01.02") + `.xlsx` //文件名称
+	this.Ctx.Output.Download(downLoadFilePath, fileName)
+	defer func() {
+		os.Remove(downLoadFilePath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+}
+
+// GetRzdIndexInfo
+// @Title 添加指标-根据条件获取指标信息
+// @Description 添加指标-根据条件获取指标信息
+// @Param   KeyWord   query   string  false       "关键字"
+// @Param   ClassifyIds   query   string  false       "分类id"
+// @Param   Frequencies   query   string  false       "频率"
+// @Param   PageSize   query   int  false       "每页数据条数"
+// @Param   CurrentIndex   query   int  false       "当前页页码,从1开始"
+// @Success 200 {object} data_manage.BaseFromRzdIndexPage
+// @router /rzd/get/index/info [get]
+func (this *BaseFromRzdIndexController) GetRzdIndexInfo() {
+	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
+	}
+
+	keyWord := this.GetString("KeyWord")
+	classifyIds := this.GetString("ClassifyIds")
+	frequencies := this.GetString("Frequencies")
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var classifyIdList []string
+	var frequencyList []string
+	if classifyIds != "" {
+		classifyIdList = strings.Split(classifyIds, ",")
+	}
+	if frequencies != "" {
+		frequencyList = strings.Split(frequencies, ",")
+	}
+	indexInfoPage, err := data.GetRzdIndexInfo(keyWord, classifyIdList, frequencyList, currentIndex, startSize, pageSize)
+	if err != nil {
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = indexInfoPage
+}

+ 902 - 19
controllers/data_manage/ccf_data.go

@@ -1,14 +1,19 @@
 package data_manage
 
 import (
+	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"github.com/tealeg/xlsx"
 	"os"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -110,7 +115,8 @@ func (this *EdbInfoController) CCFIndexData() {
 		br.ErrMsg = "请选择分类"
 		return
 	}
-
+	// 增加频度请求入参
+	frequency := this.GetString("Frequency")
 	// 获取指标
 	var condition string
 	var pars []interface{}
@@ -118,7 +124,10 @@ func (this *EdbInfoController) CCFIndexData() {
 		condition += ` AND classify_id=? `
 		pars = append(pars, classifyId)
 	}
-
+	if frequency != "" {
+		condition += ` AND frequency=? `
+		pars = append(pars, frequency)
+	}
 	indexes, err := data_manage.GetCCFIndex(condition, pars)
 	if err != nil {
 		br.Msg = "获取数据失败"
@@ -130,7 +139,7 @@ func (this *EdbInfoController) CCFIndexData() {
 	for _, v := range indexes {
 		indexCodes = append(indexCodes, v.IndexCode)
 	}
-	indexCounts, e := data_manage.GetCCFIndexDataCountGroup(indexCodes)
+	/*indexCounts, e := data_manage.GetCCFIndexDataCountGroup(indexCodes)
 	if e != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取指标数据总量失败, Err:" + err.Error()
@@ -139,7 +148,53 @@ func (this *EdbInfoController) CCFIndexData() {
 	countMap := make(map[string]int)
 	for _, v := range indexCounts {
 		countMap[v.IndexCode] = v.Count
+	}*/
+	edbInfoMap := make(map[string]*data_manage.EdbInfo)
+	dataMap := make(map[string][]*data_manage.BaseFromCCFData)
+	total := 0
+	if len(indexCodes) > 0 {
+		edbInfoList, err := data_manage.GetEdbInfoByEdbCodeList(utils.DATA_SOURCE_CCF, indexCodes)
+		if err != nil {
+			br.Msg = "获取数据源失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range edbInfoList {
+			edbInfoMap[v.EdbCode] = v
+		}
+		// 首先对分类下的指标按照日期进行分页,再针对日期,进行排序
+		dataTimes, err := data_manage.GetCCFIndexDataTimePageByCodes(indexCodes, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据日期信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataTimes) > 0 {
+			startDate := dataTimes[len(dataTimes)-1]
+			endDate := dataTimes[0]
+			// 把截止日往后加1天
+			endDateT, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+			endDate = endDateT.AddDate(0, 0, 1).Format(utils.FormatDate)
+			dataList, e := data_manage.GetCCFIndexDataByDataTime(indexCodes, startDate, endDate)
+			if e != nil {
+				br.Msg = "获取数据失败"
+				br.ErrMsg = "获取指标数据失败,Err:" + e.Error()
+				return
+			}
+			//将数据按照指标进行分类
+			for _, v := range dataList {
+				dataMap[v.IndexCode] = append(dataMap[v.IndexCode], v)
+			}
+		}
+
+		total, err = data_manage.GetCCFIndexDataTimePageCount(indexCodes)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
 	}
+	page := paging.GetPaging(currentIndex, pageSize, total)
 
 	resultList := make([]*data_manage.BaseFromCCFIndexList, 0)
 	for _, v := range indexes {
@@ -152,19 +207,24 @@ func (this *EdbInfoController) CCFIndexData() {
 		product.Frequency = v.Frequency
 		product.CreateTime = v.CreateTime
 		product.ModifyTime = v.ModifyTime
-
-		total := countMap[v.IndexCode]
+		if edb, ok := edbInfoMap[v.IndexCode]; ok {
+			product.EdbInfoId = edb.EdbInfoId
+			product.EdbExist = 1
+		}
+		/*total := countMap[v.IndexCode]
 		page := paging.GetPaging(currentIndex, pageSize, total)
 		dataList, e := data_manage.GetCCFIndexData(v.IndexCode, startSize, pageSize)
 		if e != nil {
 			br.Msg = "获取数据失败"
 			br.ErrMsg = "获取指标数据失败,Err:" + e.Error()
 			return
+		}*/
+
+		dataListTmp, ok := dataMap[v.IndexCode]
+		if !ok {
+			dataListTmp = make([]*data_manage.BaseFromCCFData, 0)
 		}
-		if dataList == nil {
-			dataList = make([]*data_manage.BaseFromCCFData, 0)
-		}
-		product.DataList = dataList
+		product.DataList = dataListTmp
 		product.Paging = page
 		resultList = append(resultList, product)
 	}
@@ -350,27 +410,29 @@ func (this *EdbInfoController) ExportCCFList() {
 		br.Msg = "success"
 		return
 	}
-	sheetNew := new(xlsx.Sheet)
-	sheetNew, err = xlsxFile.AddSheet("CCF化纤")
+	//sheetNew := new(xlsx.Sheet)
+	//sheetNew, err = xlsxFile.AddSheet("CCF化纤")
 
 	//sheetNew.SetColWidth()
 	//获取指标数据
-	windRow := sheetNew.AddRow()
+	/*windRow := sheetNew.AddRow()
 	secNameRow := sheetNew.AddRow()
 	indexCodeRow := sheetNew.AddRow()
 	frequencyRow := sheetNew.AddRow()
 	unitRow := sheetNew.AddRow()
-	lastModifyDateRow := sheetNew.AddRow()
+	lastModifyDateRow := sheetNew.AddRow()*/
 	//获取分类下指标最大数据量
 	var dataMax int
-	setRowIndex := 6
-	indexCodeList := make([]string, 0)
+	//setRowIndex := 6
+	codeList := make([]string, 0)
+	frequenciesMap := make(map[string][]*data_manage.BaseFromCCFIndexList)
 	for _, v := range indexList {
-		indexCodeList = append(indexCodeList, v.IndexCode)
+		codeList = append(codeList, v.IndexCode)
+		frequenciesMap[v.Frequency] = append(frequenciesMap[v.Frequency], v)
 	}
 	dataListMap := make(map[string][]*data_manage.BaseFromCCFData)
 	if len(indexList) > 0 {
-		allDataList, e := data_manage.GetCCFIndexDataByCodes(indexCodeList)
+		allDataList, e := data_manage.GetCCFIndexDataByCodes(codeList)
 		if e != nil {
 			br.Msg = "获取数据失败"
 			br.ErrMsg = "获取数据失败,Err:" + e.Error()
@@ -385,8 +447,118 @@ func (this *EdbInfoController) ExportCCFList() {
 			}
 		}
 	}
+	// 按照频率分组排序
+	frequencies := []string{
+		"日度", "周度", "旬度", "月度", "季度", "半年度", "年度",
+	}
+	for _, frequency := range frequencies {
+		//获取指标
+		indexCodeList, ok := frequenciesMap[frequency]
+		if !ok {
+			continue
+		}
+		if len(indexCodeList) <= 0 {
+			fmt.Printf("sheet:%s, 不存在指标", frequency)
+			return
+		}
+		var sheetName string
+		switch frequency {
+		case "日度":
+			sheetName = "日度(Daily)"
+		case "周度":
+			sheetName = "周度(Weekly)"
+		case "旬度":
+			sheetName = "旬度(ten-day)"
+		case "月度":
+			sheetName = "月度(Monthly)"
+		case "季度":
+			sheetName = "季度(Quarterly)"
+		case "半年度":
+			sheetName = "半年度(Semi-annual)"
+		case "年度":
+			sheetName = "年度(Annual)"
+		default:
+			sheetName = "其他数据"
+		}
+		sheetNew, err := xlsxFile.AddSheet(sheetName)
+		if err != nil {
+			fmt.Println("新增Sheet失败", err.Error())
+			return
+		}
+		secNameRow := sheetNew.AddRow()
+		frequencyRow := sheetNew.AddRow()
+		unitRow := sheetNew.AddRow()
+		lastModifyDateRow := sheetNew.AddRow()
+
+		var indexIdList []int
+		for _, idx := range frequenciesMap[frequency] {
+			indexIdList = append(indexIdList, idx.BaseFromCcfIndexId)
+		}
+		dataTimeList, err := data_manage.GetCCFDataDataTimeByIndexId(indexIdList)
+		if err != nil {
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取数据时间失败,Err:" + err.Error()
+			fmt.Println("获取数据时间失败", err.Error())
+			return
+		}
+
+		// 添加excel左侧指标日期
+		setRowIndex := 4
+		for rk, dv := range dataTimeList {
+			rowIndex := setRowIndex + rk
+			row := sheetNew.Row(rowIndex)
+			displayDate, _ := time.Parse(utils.FormatDate, dv)
+			displayDateCell := row.AddCell()
+			style := new(xlsx.Style)
+			style.ApplyAlignment = true
+			style.Alignment.WrapText = true
+			displayDateCell.SetStyle(style)
+			displayDateCell.SetDate(displayDate)
+
+		}
+		for k, icl := range indexCodeList {
+			// 获取数据
+			dataList, ok := dataListMap[icl.IndexCode]
+			if !ok {
+				continue
+			}
+			if k == 0 {
+				secNameRow.AddCell().SetValue("指标名称/Metric Name")
+				frequencyRow.AddCell().SetValue("频度/Frequency")
+				unitRow.AddCell().SetValue("单位/Unit")
+				lastModifyDateRow.AddCell().SetValue("更新时间/Update Time")
+				min := k * 3
+				sheetNew.SetColWidth(min, min, 15)
+			}
+			if len(dataList) == 0 {
+				continue
+			}
+			secNameRow.AddCell().SetValue(icl.IndexName)
+			frequencyRow.AddCell().SetValue(icl.Frequency)
+			unitRow.AddCell().SetValue(icl.Unit)
+
+			timeDate, err := time.Parse(utils.FormatDateTime, dataList[0].ModifyTime)
+			if err != nil {
+				continue
+			}
+			lastModifyDateRow.AddCell().SetValue(timeDate.Format(utils.FormatDate))
+			dataInfoMap := make(map[string]*data_manage.BaseFromCCFData)
+			for _, v := range dataList {
+				dataInfoMap[v.DataTime] = v
+			}
 
-	for k, sv := range indexList {
+			for rk, dtv := range dataTimeList {
+				rowIndex := setRowIndex + rk
+				row := sheetNew.Row(rowIndex)
+				displayDateCell := row.AddCell()
+				tmpData, ok := dataInfoMap[dtv]
+				if ok {
+					displayDateCell.SetValue(tmpData.Value)
+				}
+			}
+		}
+	}
+	/*for k, sv := range indexList {
 		//获取数据
 		dataList, ok := dataListMap[sv.IndexCode]
 		if !ok {
@@ -454,7 +626,7 @@ func (this *EdbInfoController) ExportCCFList() {
 				}
 			}
 		}
-	}
+	}*/
 
 	err = xlsxFile.Save(downFile)
 	if err != nil {
@@ -597,3 +769,714 @@ func (this *EdbInfoController) CCFStockTable() {
 	br.Msg = "获取成功"
 	br.Data = resp
 }
+
+// CCFIndexBatchSearch
+// @Title CCF化纤信息指标查询
+// @Description CCF化纤信息指标查询
+// @Param   ClassifyIds   query   string  true       "分类id, 多个分类用英文"
+// @Param   Keyword   query   string  true       "关键词, 指标ID/指标名称"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /ccf/batch_search [get]
+func (this *EdbInfoController) CCFIndexBatchSearch() {
+	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
+	}
+	classifyIdStr := this.GetString("ClassifyIds")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+	resp := data_manage.BaseFromCCFIndexSearchList{}
+	total := 0
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	var list = make([]*data_manage.BaseFromCCFIndexList, 0)
+	var condition string
+	var pars []interface{}
+	classifyIds := strings.Split(classifyIdStr, ",")
+	if len(classifyIds) > 0 && classifyIds[0] != `` {
+		condition += " AND classify_id IN (" + utils.GetOrmInReplace(len(classifyIds)) + " ) "
+		pars = append(pars, classifyIds)
+	}
+	keyword := this.GetString("Keyword")
+	if keyword != `` {
+		condition += " AND (index_name like ? OR index_code like ?) "
+		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+	}
+	frequencies := this.GetString("Frequencies")
+	if frequencies != "" {
+		frequencyList := strings.Split(frequencies, ",")
+		condition += " AND frequency IN (" + utils.GetOrmInReplace(len(frequencyList)) + " ) "
+		pars = append(pars, frequencyList)
+	}
+	condition += ` AND index_code not in (SELECT edb_code FROM edb_info WHERE source=?) `
+	pars = append(pars, utils.DATA_SOURCE_CCF)
+	list, err := data_manage.GetCCFIndexPage(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	// 获取最新值
+	endTimeList := make([]string, 0)
+	endValMap := make(map[int]string)
+	indexIds := make([]int, 0)
+	for _, v := range list {
+		endTimeList = append(endTimeList, v.EndDate)
+		indexIds = append(indexIds, v.BaseFromCcfIndexId)
+	}
+
+	if len(indexIds) > 0 {
+		dataTmpList, e := data_manage.GetCCFIndexDataByIndexIdAndDataTime(indexIds, endTimeList)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取指标最新值失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range dataTmpList {
+			endValMap[v.BaseFromCcfIndexId] = v.Value
+		}
+	}
+	for _, v := range list {
+		if val, ok := endValMap[v.BaseFromCcfIndexId]; ok {
+			v.EndValue = val
+		}
+	}
+	total, err = data_manage.GetCCFIndexPageCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// CCFIndexBatchAdd
+// @Title CCF化纤信息批量新增
+// @Description CCF化纤信息批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ccf/batch_add [post]
+func (this *EdbInfoController) CCFIndexBatchAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_CCF_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		v.Frequency = strings.TrimSpace(v.Frequency)
+		if v.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		v.Unit = strings.TrimSpace(v.Unit)
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+	}
+
+	// 限定同一时间最多批量新增30个指标
+	for _, v := range req {
+		var r data_manage.CCFIndexSource2EdbReq
+		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.CCFIndexSource2Edb(r, this.Lang)
+		if e != nil {
+			br.Msg = "操作失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = e.Error()
+			return
+		}
+		if skip {
+			continue
+		}
+
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(this.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = this.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// CCFIndexNameCheck
+// @Title 加入指标库的重名检测
+// @Description 加入指标库的重名检测
+// @Param   ClassifyIds   query   string  true       "分类id, 多个分类用英文"
+// @Param   Keyword   query   string  true       "关键词, 指标ID/指标名称"
+// @Success 200 {object} NameCheckResult
+// @router /ccf/edb_info/name_check [post]
+func (this *EdbInfoController) CCFIndexNameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req []*data_manage.NameCheckEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req)
+	if codeLen > codeMaxT {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+
+	indexNames := make([]string, 0)
+	resp := make([]*data_manage.EdbNameCheckResult, 0)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &data_manage.EdbNameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+		dataItems, err := data_manage.GetEdbDataAllByEdbCode(v.EdbCode, utils.DATA_SOURCE_CCF, 0, utils.EDB_DATA_LIMIT)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取钢联已存在信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataItems) <= 0 {
+			respItem, err := data.AddEdbData(utils.DATA_SOURCE_CCF, v.EdbCode, v.Frequency)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				return
+			}
+			if respItem.Ret != 200 {
+				br.Msg = "未搜索到该指标"
+				br.ErrMsg = respItem.ErrMsg + ";EdbCode:" + v.EdbCode
+				return
+			}
+		}
+	}
+
+	// 重名校验
+	edbList, e := data_manage.GetEdbInfoByNameArr(indexNames, utils.EDB_INFO_TYPE)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名指标失败, Err: " + e.Error()
+		return
+	}
+	nameExists := make(map[string]bool)
+	for _, v := range edbList {
+		nameExists[v.EdbName] = true
+	}
+	if len(nameExists) > 0 {
+		for _, v := range resp {
+			v.Exist = nameExists[v.EdbName]
+		}
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// CCFAddCheck
+// @Title 加入指标库指标Id检测
+// @Description 加入指标库指标Id检测
+// @Param	request	body request.BatchAddCheckReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ccf/edb_info/add_check [post]
+func (c *EdbInfoController) CCFAddCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.BatchAddCheckReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req.IndexCodes)
+
+	// 获取指标库已有指标
+	existsEdb, e := data_manage.GetEdbCodesBySource(utils.DATA_SOURCE_CCF)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取自有数据已添加的指标失败, Err: " + e.Error()
+		return
+	}
+	existMap := make(map[string]*data_manage.EdbInfo)
+	for _, v := range existsEdb {
+		existMap[v.EdbCode] = v
+	}
+
+	if codeLen == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if codeLen > codeMaxT {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMaxT)
+		return
+	}
+
+	// 查询选中的指标
+	cond := fmt.Sprintf(` AND index_code IN (%s)`, utils.GetOrmInReplace(codeLen))
+	pars := make([]interface{}, 0)
+	pars = append(pars, req.IndexCodes)
+	list, err := data_manage.GetCCFIndex(cond, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取钢联已存在信息失败,Err:" + err.Error()
+		return
+	}
+
+	if len(list) > codeMaxT {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMaxT)
+		return
+	}
+
+	resp := make([]*data_manage.BaseFromCCFIndexList, 0)
+	for _, v := range list {
+		if edb, ok := existMap[v.IndexCode]; ok {
+			v.EdbInfoId = edb.EdbInfoId
+			// todo 验证下面三个字段的用处
+			//v.EdbClassifyId = edb.ClassifyId
+			//v.EdbUniqueCode = edb.UniqueCode
+			v.EdbExist = 1
+		}
+		resp = append(resp, v)
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// CCFEdbInfoAdd
+// @Title 新增指标接口
+// @Description 新增指标接口
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /ccf/edb_info/add [post]
+func (this *EdbInfoController) CCFEdbInfoAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req data_manage.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	req.EdbCode = strings.Trim(req.EdbCode, " ")
+
+	if req.EdbCode == "" {
+		br.Msg = "指标ID不能为空"
+		return
+	}
+
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	count, err := data_manage.GetCCFIndexDataCount(req.EdbCode)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	if count == 0 {
+		br.Msg = "指标不存在"
+	}
+
+	// 指标入库
+	edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(utils.DATA_SOURCE_CCF, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, req.StartDate, req.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	// 试用平台更新用户累计新增指标数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + e.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserIndexNum(r)
+		}()
+	}
+
+	//新增操作日志
+	{
+		edbLog := new(data_manage.EdbInfoLog)
+		edbLog.EdbInfoId = edbInfo.EdbInfoId
+		edbLog.SourceName = edbInfo.SourceName
+		edbLog.Source = edbInfo.Source
+		edbLog.EdbCode = edbInfo.EdbCode
+		edbLog.EdbName = edbInfo.EdbName
+		edbLog.ClassifyId = edbInfo.ClassifyId
+		edbLog.SysUserId = sysUser.AdminId
+		edbLog.SysUserRealName = sysUser.RealName
+		edbLog.CreateTime = time.Now()
+		edbLog.Content = string(this.Ctx.Input.RequestBody)
+		edbLog.Status = "新增指标"
+		edbLog.Method = this.Ctx.Input.URI()
+		go data_manage.AddEdbInfoLog(edbLog)
+	}
+
+	// 更新es
+	go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+
+	resp := new(data_manage.AddEdbInfoResp)
+	resp.EdbInfoId = edbInfo.EdbInfoId
+	resp.UniqueCode = edbInfo.UniqueCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// GetFrequency
+// @Title CCF化纤信息数据频度
+// @Description CCF化纤信息数据频度接口
+// @Param   ClassifyId   query   string  true       "分类Id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /ccf/frequency [get]
+func (this *EdbInfoController) GetCCFFrequency() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+
+	frequencyList, err := data_manage.GetCCFFrequencyByClassifyId(classifyId)
+	if err != nil {
+		br.Msg = "获取频度失败"
+		br.ErrMsg = "获取频度失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = frequencyList
+}
+
+// BatchAddEdbCheck
+// @Title 新增校验
+// @Description 新增校验
+// @Param	request	body data_manage.BatchManualEdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /ccf/batch/add/check [post]
+func (c *EdbInfoController) 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
+	}
+
+	// 最大批量添加的数量
+	codeMaxT := 31
+
+	var req data_manage.BatchCheckCCFEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,Err:" + err.Error()
+		return
+	}
+	req.Keyword = strings.TrimSpace(req.Keyword)
+	classifyIdStr := req.ClassifyIds
+
+	var list = make([]*data_manage.BaseFromCCFIndexList, 0)
+	var condition string
+	var pars []interface{}
+
+	if req.ListAll {
+		classifyIds := strings.Split(classifyIdStr, ",")
+		if len(classifyIds) > 0 && classifyIds[0] != `` {
+			condition += " AND classify_id IN (" + utils.GetOrmInReplace(len(classifyIds)) + " ) "
+			pars = append(pars, classifyIds)
+		}
+		keyword := req.Keyword
+		if keyword != `` {
+			condition += " AND (index_name like ? OR index_code like ?) "
+			pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+		}
+		frequencies := req.Frequencies
+		if frequencies != "" {
+			frequencyList := strings.Split(frequencies, ",")
+			condition += " AND frequency IN (" + utils.GetOrmInReplace(len(frequencyList)) + " ) "
+			pars = append(pars, frequencyList)
+		}
+		codes := req.TradeCodeList
+		codeList := make([]string, 0)
+		if codes != "" {
+			codeList = strings.Split(codes, ",")
+		}
+		if len(codeList) > 0 {
+			condition += ` AND index_code not in (` + utils.GetOrmInReplace(len(codeList)) + `) `
+			pars = append(pars, codeList)
+		}
+	} else {
+		codes := req.TradeCodeList
+		codeList := make([]string, 0)
+		if codes != "" {
+			codeList = strings.Split(codes, ",")
+		}
+		if len(codeList) <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		condition += ` AND index_code in (` + utils.GetOrmInReplace(len(codeList)) + `) `
+		pars = append(pars, codeList)
+
+	}
+	condition += ` AND index_code not in (SELECT edb_code FROM edb_info WHERE source=?) `
+	pars = append(pars, utils.DATA_SOURCE_CCF)
+
+	list, err = data_manage.GetCCFIndexPage(condition, pars, 0, codeMaxT)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	if len(list) >= codeMaxT {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+
+	br.Data = list
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 31 - 1
controllers/data_manage/chart_classify.go

@@ -251,6 +251,7 @@ func getChartClassifyListForMe(adminInfo system.Admin, resp *data_manage.ChartCl
 // ChartClassifyItems
 // @Title 获取所有图表分类接口-不包含图表
 // @Description 获取所有图表分类接口-不包含图表
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
 // @Success 200 {object} data_manage.ChartClassifyListResp
 // @router /chart_classify/items [get]
 func (this *ChartClassifyController) ChartClassifyItems() {
@@ -259,6 +260,7 @@ func (this *ChartClassifyController) ChartClassifyItems() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
+	isShowMe, _ := this.GetBool("IsShowMe")
 
 	level, _ := this.GetInt(`Level`)
 	rootList, err := data_manage.GetChartClassifyByParentId(0, utils.CHART_SOURCE_DEFAULT)
@@ -290,6 +292,28 @@ func (this *ChartClassifyController) ChartClassifyItems() {
 		nodeAll = append(nodeAll, rootNode)
 	}
 
+	if isShowMe {
+		// 自己拥有的分类id列表
+		chartClassifyIdList, err := data_manage.GetChartClassifyIdListByAdminId(this.SysUser.AdminId, utils.CHART_SOURCE_DEFAULT)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+			return
+		}
+
+		tmpNodeAll := nodeAll
+		nodeAll = make([]*data_manage.ChartClassifyItems, 0)
+		for _, node := range tmpNodeAll {
+			if node.Children == nil || len(node.Children) <= 0 {
+				continue
+			}
+			isSelf := data.RemoveNotChartClassifyItemsMakeTree(node, chartClassifyIdList)
+			if isSelf {
+				nodeAll = append(nodeAll, node)
+			}
+		}
+	}
+
 	language := `CN`
 	// 指标显示的语言
 	{
@@ -1091,6 +1115,12 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 		}
 		// 移除没有权限的图表
 		allNodes := data.HandleNoPermissionChart(allChartInfo, noPermissionChartIdMap, this.SysUser.AdminId)
+		allNodes, err = data.GetChartClassifyByIsMe(sysUser.AdminId, chartClassifyId, utils.CHART_SOURCE_DEFAULT, allNodes)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
 		resp.AllNodes = allNodes
 
 		br.Ret = 200
@@ -1136,4 +1166,4 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = resp
-}
+}

+ 48 - 27
controllers/data_manage/chart_info.go

@@ -16,13 +16,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"
 )
 
 // 图表管理
@@ -1041,7 +1042,7 @@ func (this *ChartInfoController) ChartInfoDetail() {
 		}
 		extraConfigStr = chartInfo.BarConfig
 	}
-	yearMax := 0
+	var dateMax time.Time
 	if dateType == utils.DateTypeNYears {
 		for _, v := range mappingList {
 			if v.LatestDate != "" {
@@ -1051,14 +1052,18 @@ func (this *ChartInfoController) ChartInfoDetail() {
 					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
 					return
 				}
-				if lastDateT.Year() > yearMax {
-					yearMax = lastDateT.Year()
+				if lastDateT.After(dateMax) {
+					dateMax = lastDateT
 				}
 			}
 		}
 	}
+	if chartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+		// 季度图,的至今N年,需要特殊处理,将日期范围扩大到下一年
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
 	// 开始/结束日期
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, dateMax)
 
 	if chartInfo.HaveOperaAuth {
 		// 获取图表中的指标数据
@@ -1287,7 +1292,7 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
-	yearMax := 0
+	var dateMax time.Time
 	for k, v := range mappingList {
 		if tmpV, ok := edbInfoIdMapping[v.EdbInfoId]; ok {
 			v.EdbInfoType = tmpV.EdbInfoType
@@ -1311,8 +1316,8 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
 					return
 				}
-				if lastDateT.Year() > yearMax {
-					yearMax = lastDateT.Year()
+				if lastDateT.After(dateMax) {
+					dateMax = lastDateT
 				}
 			}
 		}
@@ -1326,8 +1331,13 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		mappingList[k] = v
 	}
 
+	if req.ChartType == utils.CHART_TYPE_SEASON && req.DateType == utils.DateTypeNYears {
+		// 季节性图表,要特殊处理起始日期, 最近N年
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
+
 	// 开始/结束日期
-	startDate, endDate := utils.GetDateByDateTypeV2(req.DateType, req.StartDate, req.EndDate, req.StartYear, yearMax)
+	startDate, endDate := utils.GetDateByDateTypeV2(req.DateType, req.StartDate, req.EndDate, req.StartYear, dateMax)
 	if startDate == "" {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败 时间格式错误"
@@ -1593,23 +1603,26 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 			dateType = 3
 		}
 	}
-	yearMax := 0
+	var dateMax time.Time
 	if dateType == utils.DateTypeNYears {
 		for _, v := range mappingList {
-			if v.LatestDate != "" {
+			if v.LatestDate != "" && v.LatestDate != "0000-00-00" {
 				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
 				if tErr != nil {
 					br.Msg = "获取失败"
 					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
 					return
 				}
-				if lastDateT.Year() > yearMax {
-					yearMax = lastDateT.Year()
+				if lastDateT.After(dateMax) {
+					dateMax = lastDateT
 				}
 			}
 		}
 	}
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+	if dateType == utils.DateTypeNYears && chartInfo.ChartType == utils.CHART_TYPE_SEASON {
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, dateMax)
 
 	if chartInfo.ChartType == 2 {
 		chartInfo.StartDate = startDate
@@ -1755,7 +1768,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 	}
 
 	resp.ClassifyLevels = classifyLevels
-	
+
 	//图表操作权限
 	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, chartInfo.HaveOperaAuth)
 	chartInfo.Button = data_manage.ChartViewButton{
@@ -1891,13 +1904,13 @@ func (this *ChartInfoController) ChartInfoEdbInfoDetail() {
 		br.Msg = "获取失败,Err:" + err.Error()
 		return
 	}
-	maxYear := 0
+	var maxDate time.Time
 	if edbInfo.LatestDate != "" {
 		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
-		maxYear = latestDateT.Year()
+		maxDate = latestDateT
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxDate)
 	if startDate == "" {
 		br.Msg = "参数错误"
 		br.Msg = "参数错误,无效的查询日期"
@@ -2733,7 +2746,7 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		return
 	}
 
-	yearMax := 0
+	var dateMax time.Time
 	if dateType == utils.DateTypeNYears {
 		for _, v := range mappingList {
 			if v.LatestDate != "" {
@@ -2743,13 +2756,17 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 					errMsg = "获取图表日期信息失败,Err:" + tErr.Error()
 					return
 				}
-				if lastDateT.Year() > yearMax {
-					yearMax = lastDateT.Year()
+				if lastDateT.After(dateMax) {
+					dateMax = lastDateT
 				}
 			}
 		}
 	}
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+	if chartInfo.ChartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+		// 季度图,的至今N年,需要特殊处理,将日期范围扩大到下一年
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, dateMax)
 
 	extraConfigStr := chartInfo.ExtraConfig //图表额外数据参数
 	var barConfig data_manage.BarChartInfoReq
@@ -4163,7 +4180,7 @@ func (this *ChartInfoController) ChartInfoConvertDetail() {
 			}
 			extraConfigStr = chartInfo.BarConfig
 		}
-		yearMax := 0
+		var dateMax time.Time
 		if dateType == utils.DateTypeNYears {
 			for _, v := range mappingList {
 				if v.LatestDate != "" {
@@ -4173,14 +4190,18 @@ func (this *ChartInfoController) ChartInfoConvertDetail() {
 						br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
 						return
 					}
-					if lastDateT.Year() > yearMax {
-						yearMax = lastDateT.Year()
+					if lastDateT.After(dateMax) {
+						dateMax = lastDateT
 					}
 				}
 			}
 		}
+		if chartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+			// 季节性图,最近N年需要特殊处理
+			dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+		}
 		// 开始/结束日期
-		startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+		startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, dateMax)
 
 		// 获取图表中的指标数据
 		edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartConvertEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr, chartInfo.SeasonExtraConfig, isAxis)
@@ -5012,4 +5033,4 @@ func (this *ChartInfoController) ModifyChartList() {
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
-}
+}

+ 7 - 4
controllers/data_manage/chart_theme.go

@@ -231,7 +231,7 @@ func (c *ChartThemeController) GetThemePreviewData() {
 	chartInfo.Source = chartThemeType.ChartSource
 	chartInfo.ChartType = chartThemeType.ChartType
 
-	yearMax := 0
+	var dateMax time.Time
 	if dateType == utils.DateTypeNYears {
 		for _, v := range mappingList {
 			if v.LatestDate != "" {
@@ -241,14 +241,17 @@ func (c *ChartThemeController) GetThemePreviewData() {
 					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
 					return
 				}
-				if lastDateT.Year() > yearMax {
-					yearMax = lastDateT.Year()
+				if lastDateT.After(dateMax) {
+					dateMax = lastDateT
 				}
 			}
 		}
 	}
+	if chartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+		dateMax = time.Date(dateMax.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+	}
 	// 开始/结束日期
-	startDate, endDate := utils.GetDateByDateTypeV2(dateType, tmpStartDate, tmpEndDate, startYear, yearMax)
+	startDate, endDate := utils.GetDateByDateTypeV2(dateType, tmpStartDate, tmpEndDate, startYear, dateMax)
 
 	// 获取图表中的指标数据
 	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetThemePreviewChartEdbData(chartType, calendar, startDate, endDate, mappingList, extraConfigStr, chartInfo.SeasonExtraConfig)

+ 9 - 0
controllers/data_manage/correlation/correlation_chart_classify.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data"
+	"eta/eta_api/services/data/correlation"
 	correlationServ "eta/eta_api/services/data/correlation"
 	"eta/eta_api/services/data/data_manage_permission"
 	"eta/eta_api/utils"
@@ -135,6 +136,14 @@ func (this *CorrelationChartClassifyController) ChartClassifyList() {
 			return nodeAll[i].Sort < nodeAll[j].Sort
 		})
 	}
+	if isShowMe {
+		nodeAll, err = correlation.GetCorrelationClassifyByIsMe(this.SysUser.AdminId, parentId, source, nodeAll)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取我的分类失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	resp.AllNodes = nodeAll
 	br.Ret = 200

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

@@ -517,8 +517,25 @@ func (this *CorrelationChartInfoController) List() {
 		}
 		condition += " AND chart_classify_id IN(" + chartClassifyId + ") "
 	}
+
+	var keyWordArr []string
 	if keyword != "" {
-		condition += ` AND  ( chart_name LIKE '%` + keyword + `%' )`
+		newKeyWord := strings.Split(keyword, " ")
+		keywordStr := strings.Replace(keyword, " ", "", -1)
+
+		condition += " AND ( "
+		condition += ` chart_name LIKE '%` + keywordStr + `%' OR chart_name_en LIKE '%` + keywordStr + `%' OR`
+
+		keyWordArr = append(keyWordArr, newKeyWord...)
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				if v != "" {
+					condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
+				}
+			}
+		}
+		condition = strings.TrimRight(condition, "OR")
+		condition += " ) "
 	}
 
 	//只看我的

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

@@ -91,8 +91,24 @@ func (c *ChartInfoController) List() {
 		}
 		condition += " AND chart_classify_id IN(" + chartClassifyId + ") "
 	}
+
+	var keyWordArr []string
 	if keyword != "" {
-		condition += ` AND  ( chart_name LIKE '%` + keyword + `%' )`
+		newKeyWord := strings.Split(keyword, " ")
+		keywordStr := strings.Replace(keyword, " ", "", -1)
+
+		condition += " AND ( "
+		condition += ` chart_name LIKE '%` + keywordStr + `%' OR chart_name_en LIKE '%` + keywordStr + `%' OR`
+
+		keyWordArr = append(keyWordArr, newKeyWord...)
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				if v != "" {
+					condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
+				}			}
+		}
+		condition = strings.TrimRight(condition, "OR")
+		condition += " ) "
 	}
 
 	//只看我的

+ 8 - 0
controllers/data_manage/edb_classify.go

@@ -1058,6 +1058,14 @@ func (this *EdbClassifyController) SimpleList() {
 		sortList = nodeAll
 		sort.Sort(sortList)
 	}
+	if isOnlyMe {
+		sortList, err = data.GetEdbClassifyByIsMe(sysUserId, parentId, 0, sortList)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	language := `CN`
 	// 指标显示的语言

+ 48 - 13
controllers/data_manage/edb_info.go

@@ -1864,7 +1864,7 @@ func (this *EdbInfoController) EdbInfoSearch() {
 				searchItem.Unit = indexInfo.Unit
 				searchItem.EdbName = indexInfo.IndexName
 			}
-		}  else if source == utils.DATA_SOURCE_HISUGAR { //泛糖科技
+		} else if source == utils.DATA_SOURCE_HISUGAR { //泛糖科技
 			dataItems, err := data_manage.GetEdbDataAllByEdbCode(edbCode, source, subSource, utils.EDB_DATA_LIMIT)
 			if err != nil && err.Error() != utils.ErrNoRow() {
 				br.Msg = "获取失败"
@@ -3896,12 +3896,11 @@ func (this *ChartInfoController) EdbInfoData() {
 	resp.DataList = make([]*data_manage.EdbDataList, 0)
 	// 数据获取
 	if fullEdb.HaveOperaAuth { // 有权限才获取数据
-		maxYear := 0
+		var latestDateT time.Time
 		if edbInfo.LatestDate != "" {
-			latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
-			maxYear = latestDateT.Year()
+			latestDateT, _ = time.Parse(utils.FormatDate, edbInfo.LatestDate)
 		}
-		startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+		startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, latestDateT)
 		dataList, err = data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfoId, startDate, endDate)
 		if err != nil {
 			br.Msg = "获取失败"
@@ -4020,6 +4019,42 @@ func (this *ChartInfoController) EdbInfoReplace() {
 		return
 	}
 
+	if oldEdbInfo.EdbInfoType == 1 || newEdbInfo.EdbInfoType == 1 {
+		br.Msg = "预测指标不允许替换"
+		br.ErrMsg = "预测指标不允许替换"
+		return
+	}
+	// 判断指标是否循环引用
+	if oldEdbInfo.EdbType == 2 {
+		// 查询该计算指标的所有相关指标,如果相关指标中包含新指标,则提示
+		hasRelation, e := data.CheckTwoEdbInfoRelation(oldEdbInfo, newEdbInfo)
+		if e != nil {
+			br.Msg = "替换失败!"
+			br.ErrMsg = "查询指标引用关系失败,Err:" + e.Error()
+			return
+		}
+		if hasRelation {
+			br.Msg = "原指标与替换指标存在引用关系,不允许替换"
+			br.ErrMsg = "原指标与替换指标存在引用关系,不允许替换"
+			return
+		}
+	}
+
+	if newEdbInfo.EdbType == 2 {
+		// 查询该计算指标的所有相关指标,如果相关指标中包含新指标,则提示
+		hasRelation, e := data.CheckTwoEdbInfoRelation(newEdbInfo, oldEdbInfo)
+		if e != nil {
+			br.Msg = "替换失败!"
+			br.ErrMsg = "查询指标引用关系失败,Err:" + e.Error()
+			return
+		}
+		if hasRelation {
+			br.Msg = "原指标与替换指标存在引用关系,不允许替换"
+			br.ErrMsg = "原指标与替换指标存在引用关系,不允许替换"
+			return
+		}
+	}
+
 	sysAdminId := sysUser.AdminId
 	//replaceChartTotal, replaceCalculateTotal, err := data.EdbInfoReplace(oldEdbInfo, newEdbInfo, sysAdminId, sysUser.RealName)
 	_, _, err = data.EdbInfoReplace(oldEdbInfo, newEdbInfo, sysAdminId, sysUser.RealName)
@@ -4534,13 +4569,12 @@ func (this *ChartInfoController) EdbInfoDataTb() {
 		br.Msg = "获取失败,Err:" + err.Error()
 		return
 	}
-	maxYear := 0
+	var latestDateT time.Time
 	if edbInfo.LatestDate != "" {
-		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
-		maxYear = latestDateT.Year()
+		latestDateT, _ = time.Parse(utils.FormatDate, edbInfo.LatestDate)
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, latestDateT)
 
 	var startDateTime time.Time
 	if startDate != `` {
@@ -4637,13 +4671,14 @@ func (this *ChartInfoController) EdbInfoDataSeasonal() {
 		br.Msg = "获取失败,Err:" + err.Error()
 		return
 	}
-	maxYear := 0
+	var latestDateT time.Time
 	if edbInfo.LatestDate != "" {
-		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
-		maxYear = latestDateT.Year()
+		// 季节性图需要特殊处理最近N年数据
+		latestDateT, _ = time.Parse(utils.FormatDate, edbInfo.LatestDate)
+		latestDateT = time.Date(latestDateT.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, latestDateT)
 
 	dataList, err := data.GetChartEdbSeasonalData(calendar, startDate, endDate, edbInfo)
 	if err != nil {

+ 35 - 7
controllers/data_manage/edb_info_relation.go

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

+ 3 - 2
controllers/data_manage/excel/balance_table.go

@@ -17,13 +17,14 @@ import (
 	excel2 "eta/eta_api/services/excel"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/tealeg/xlsx"
 	"io/ioutil"
 	"os"
 	"sort"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/tealeg/xlsx"
 )
 
 // GetChildTable
@@ -1413,7 +1414,7 @@ func downloadBalanceTable(excelInfo *excel.ExcelInfo, lang string) (savePath, zi
 				errMsg = msg
 				return
 			}
-			tableData, er := excel2.GetTableDataByMixedTableData(newResult, false)
+			tableData, er := excel2.GetTableDataByMixedTableData(newResult, false, childExcelInfo.ExcelInfoId)
 			if er != nil {
 				errMsg = "获取失败"
 				err = fmt.Errorf("转换成table失败,Err:" + err.Error())

+ 3 - 0
controllers/data_manage/excel/excel_classify.go

@@ -153,6 +153,9 @@ func (this *ExcelClassifyController) List() {
 			nodeAll = append(nodeAll, v)
 		}
 	}
+	if isShowMe {
+		nodeAll = excel2.GetClassifyListRemoveNoExcel(nodeAll)
+	}
 
 	resp := response2.ExcelClassifyListResp{
 		AllNodes: nodeAll,

+ 33 - 12
controllers/data_manage/excel/excel_info.go

@@ -373,10 +373,14 @@ func (c *ExcelInfoController) List() {
 
 	pageSize, _ := c.GetInt("PageSize")
 	currentIndex, _ := c.GetInt("CurrentIndex")
-	keyword := c.GetString("Keyword")
+	keyword := c.GetString("KeyWord")
 	adminId, _ := c.GetInt("AdminId")
 	source, _ := c.GetInt("Source")
 
+	if keyword == `` { // 兼容前端
+		keyword = c.GetString("Keyword")
+	}
+
 	var total int
 	page := paging.GetPaging(currentIndex, pageSize, total)
 
@@ -431,10 +435,27 @@ func (c *ExcelInfoController) List() {
 			pars = append(pars, classifyIds)
 		}
 	}
+
+	var keyWordArr []string
 	if keyword != "" {
-		condition += ` AND  ( excel_name LIKE ? )`
-		pars = utils.GetLikeKeywordPars(pars, keyword, 1)
+		newKeyWord := strings.Split(keyword, " ")
+		keywordStr := strings.Replace(keyword, " ", "", -1)
+
+		condition += " AND ( "
+		condition += ` excel_name LIKE '%` + keywordStr + `%' OR`
+
+		keyWordArr = append(keyWordArr, newKeyWord...)
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				if v != "" {
+					condition += ` excel_name LIKE '%` + v + `%' OR`
+				}
+			}
+		}
+		condition = strings.TrimRight(condition, "OR")
+		condition += " ) "
 	}
+
 	if adminId > 0 {
 		condition += " AND sys_user_id = ? "
 		pars = append(pars, adminId)
@@ -1617,7 +1638,7 @@ func (c *ExcelInfoController) GetExcelTableData() {
 			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
 			return
 		}
-		tableData, err = excel.GetTableDataByMixedTableData(newResult, true)
+		tableData, err = excel.GetTableDataByMixedTableData(newResult, true, excelInfo.ExcelInfoId)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
@@ -1646,7 +1667,7 @@ func (c *ExcelInfoController) GetExcelTableData() {
 	}
 
 	tableData = excel.HandleTableCell(tableData)
-	tableData, err = excel.HandleRuleToTableCell(excelInfo.ExcelInfoId, tableData)
+	// tableData, err = excel.HandleRuleToTableCell(excelInfo.ExcelInfoId, tableData)
 	if err != nil {
 		utils.FileLog.Info("表格管理规则处理失败,HandleRuleToTableCell err:", err.Error())
 	}
@@ -1675,6 +1696,8 @@ func (c *ExcelInfoController) GetExcelTableData() {
 		SourcesFrom:   excelInfo.SourcesFrom,
 		ExcelSource:   excelSource,
 		ExcelSourceEn: excelSourceEn,
+		ExcelInfoId:   excelInfo.ExcelInfoId,
+		Source:        excelInfo.Source,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -2624,7 +2647,7 @@ func (c *ExcelInfoController) Download() {
 			br.ErrMsg = "获取最新的数据失败,Err:" + err.Error()
 			return
 		}
-		tableData, err = excel.GetTableDataByMixedTableData(newResult, false)
+		tableData, err = excel.GetTableDataByMixedTableData(newResult, false, excelInfo.ExcelInfoId)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
@@ -3206,12 +3229,10 @@ func (c *ExcelInfoController) EditExcelRule() {
 		br.Msg = "应用选区不能为空"
 		return
 	}
-	if req.FontColor == "" {
-		br.Msg = "字体颜色不能为空"
-		return
-	}
-	if req.BackgroundColor == "" {
-		br.Msg = "背景颜色不能为空"
+	req.BackgroundColor = strings.TrimSpace(req.BackgroundColor)
+	req.FontColor = strings.TrimSpace(req.FontColor)
+	if req.FontColor == "" && req.BackgroundColor == "" {
+		br.Msg = "字体颜色或背景颜色不能同时为空"
 		return
 	}
 	if req.RuleType == 3 && req.RightValue == "" {

+ 6 - 3
controllers/data_manage/future_good/future_good_chart_classify.go

@@ -124,16 +124,19 @@ func getChartClassifyListForMe(adminInfo system.Admin, resp *data_manage.ChartCl
 		chartInfoMap[v.ChartClassifyId] = append(chartInfoMap[v.ChartClassifyId], v)
 	}
 	rootChildMap := make(map[int][]*data_manage.ChartClassifyItems)
+	res := make([]*data_manage.ChartClassifyItems, 0)
 	for _, v := range rootList {
 		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
+			// items := make([]*data_manage.ChartClassifyItems, 0)
+			// v.Children = items
+			continue
 		}
+		res = append(res, v)
 	}
-	resp.AllNodes = rootList
+	resp.AllNodes = res
 
 	return
 }

+ 107 - 60
controllers/data_manage/future_good/future_good_chart_info.go

@@ -16,13 +16,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 商品价格图表管理
@@ -89,8 +90,24 @@ func (this *FutureGoodChartInfoController) ChartList() {
 		condition += " AND chart_classify_id IN(" + chartClassifyId + ") "
 		//pars = append(pars, chartClassifyId)
 	}
+
+	var keyWordArr []string
 	if keyword != "" {
-		condition += ` AND  ( chart_name LIKE '%` + keyword + `%' )`
+		newKeyWord := strings.Split(keyword, " ")
+		keywordStr := strings.Replace(keyword, " ", "", -1)
+
+		condition += " AND ( "
+		condition += ` chart_name LIKE '%` + keywordStr + `%' OR chart_name_en LIKE '%` + keywordStr + `%' OR`
+
+		keyWordArr = append(keyWordArr, newKeyWord...)
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				if v != "" {
+					condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
+				}			}
+		}
+		condition = strings.TrimRight(condition, "OR")
+		condition += " ) "
 	}
 
 	//只看我的
@@ -949,7 +966,7 @@ func (this *FutureGoodChartInfoController) ChartEnInfoEdit() {
 				return
 			}
 
-			list, _ := future_good.GetFutureGoodEdbInfoListByParentId(futureGoodEdbInfo.FutureGoodEdbInfoId)
+			/*list, _ := future_good.GetFutureGoodEdbInfoListByParentId(futureGoodEdbInfo.FutureGoodEdbInfoId)
 			for _, v := range list {
 				if v.FutureGoodEdbNameEn == `` {
 					v.FutureGoodEdbNameEn = strings.TrimPrefix(req.FutureGoodNameEn, " ")
@@ -959,7 +976,7 @@ func (this *FutureGoodChartInfoController) ChartEnInfoEdit() {
 					v.FutureGoodEdbNameEn = strings.TrimSuffix(req.FutureGoodNameEn, " ")
 				}
 				v.Update([]string{"FutureGoodEdbNameEn"})
-			}
+			}*/
 		}
 	case utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
 		err = data_manage.EditFutureGoodProfitChartEnInfoAndEdbEnInfo(req.ChartInfoId, req.ChartNameEn, edbInfo.EdbInfoId, req.EdbNameEn, req.UnitEn, req.ProfitNameEn)
@@ -1332,16 +1349,16 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 		return
 	}
 
-	maxYear := 0
+	var maxDate time.Time
 	for _, v := range edbInfoMappingList {
 		if v.LatestDate != "" {
 			latestDateT, _ := time.Parse(utils.FormatDate, v.LatestDate)
-			if maxYear < latestDateT.Year() {
-				maxYear = latestDateT.Year()
+			if latestDateT.After(maxDate) {
+				maxDate = latestDateT
 			}
 		}
 	}
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxDate)
 
 	// 商品价格曲线图的一些配置
 	var barConfig data_manage.FutureGoodBarChartInfoReq
@@ -1364,6 +1381,7 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 		// 默认取第一个现货指标
 		baseEdbInfoId = edbInfoMappingList[0].EdbInfoId
 		baseEdbInfoMapping = edbInfoMappingList[0]
+		barConfig.BaseEdbInfoId = baseEdbInfoId
 	} else {
 		baseEdbInfoMapping, err = data_manage.GetChartEdbMappingByEdbInfoId(baseEdbInfoId)
 		if err != nil {
@@ -1401,6 +1419,33 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dat
 				}
 			}
 		}
+		if v.Source == 0 {
+			name := strings.Split(v.EdbName, "(")
+			if barConfig.FutureGoodEdbName != "" {
+				name[0] = barConfig.FutureGoodEdbName
+			}
+			if len(name) > 1 {
+				if barConfig.FutureGoodEdbName == "" {
+					barConfig.FutureGoodEdbName = name[0]
+				}
+				v.EdbName = name[0] + "(" + name[1]
+			}
+			//英文
+			// 编译正则表达式,匹配一个或多个数字
+
+			if v.EdbNameEn != "" {
+				name = strings.Split(v.EdbNameEn, "(")
+				if barConfig.FutureGoodEdbNameEn != "" {
+					name[0] = barConfig.FutureGoodEdbNameEn
+				}
+				if len(name) > 1 {
+					if barConfig.FutureGoodEdbNameEn == "" {
+						barConfig.FutureGoodEdbNameEn = name[0]
+					}
+					v.EdbNameEn = name[0] + "(" + name[1]
+				}
+			}
+		}
 	}
 	if len(warnEdbList) > 0 {
 		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
@@ -1743,6 +1788,33 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 				}
 			}
 		}
+		if v.Source == 0 {
+			name := strings.Split(v.EdbName, "(")
+			if barConfig.FutureGoodEdbName != "" {
+				name[0] = barConfig.FutureGoodEdbName
+			}
+			if len(name) > 1 {
+				if barConfig.FutureGoodEdbName == "" {
+					barConfig.FutureGoodEdbName = name[0]
+				}
+				v.EdbName = name[0] + "(" + name[1]
+			}
+			//英文
+			// 编译正则表达式,匹配一个或多个数字
+
+			if v.EdbNameEn != "" {
+				name = strings.Split(v.EdbNameEn, "(")
+				if barConfig.FutureGoodEdbNameEn != "" {
+					name[0] = barConfig.FutureGoodEdbNameEn
+				}
+				if len(name) > 1 {
+					if barConfig.FutureGoodEdbNameEn == "" {
+						barConfig.FutureGoodEdbNameEn = name[0]
+					}
+					v.EdbNameEn = name[0] + "(" + name[1]
+				}
+			}
+		}
 	}
 	if len(warnEdbList) > 0 {
 		chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
@@ -1931,13 +2003,13 @@ func (this *FutureGoodChartInfoController) ChartInfoEdbInfoDetail() {
 		br.Msg = "获取失败,Err:" + err.Error()
 		return
 	}
-	maxYear := 0
+	var maxDate time.Time
 	if edbInfo.LatestDate != "" {
 		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
-		maxYear = latestDateT.Year()
+		maxDate = latestDateT
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxDate)
 	if startDate == "" {
 		br.Msg = "参数错误"
 		br.Msg = "参数错误,无效的查询日期"
@@ -3285,6 +3357,22 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			}
 			xDataList := barConfig.XDataList
 			length := len(xDataList)
+			oldFutureEdbName := barConfig.FutureGoodEdbName
+			oldFutureEdbNameEn := barConfig.FutureGoodEdbNameEn
+			if oldFutureEdbName == "" {
+				futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartItem.ChartInfoId)
+				if err != nil {
+					br.Msg = "修改失败"
+					br.ErrMsg = "获取图表现货价格指标信息失败,指标信息失败,Err:" + err.Error()
+					return
+				}
+				list, _ := future_good.GetFutureGoodEdbInfoListByParentId(futureGoodEdbInfoMapping.EdbInfoId)
+				for _, v := range list {
+					oldFutureEdbName = v.FutureGoodEdbName
+					oldFutureEdbNameEn = v.FutureGoodEdbNameEn
+					break
+				}
+			}
 			switch this.Lang {
 			case utils.EnLangVersion:
 				for k, v := range req.XDataList {
@@ -3298,6 +3386,10 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 						}
 					}
 				}
+				if req.FutureGoodName != `` {
+					barConfig.FutureGoodEdbNameEn = req.FutureGoodName
+					barConfig.FutureGoodEdbName = oldFutureEdbName
+				}
 			default:
 				for k, v := range req.XDataList {
 					v = strings.TrimPrefix(v, " ")
@@ -3310,6 +3402,10 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 						}
 					}
 				}
+				if req.FutureGoodName != `` {
+					barConfig.FutureGoodEdbName = req.FutureGoodName
+					barConfig.FutureGoodEdbNameEn = oldFutureEdbNameEn
+				}
 			}
 			barConfig.XDataList = xDataList
 			barConfigByte, e := json.Marshal(barConfig)
@@ -3321,55 +3417,6 @@ func (this *FutureGoodChartInfoController) BaseInfoEdit() {
 			chartItem.BarConfig = string(barConfigByte)
 		}
 		err = data_manage.EditBaseFutureGoodChartInfoAndEdbEnInfo(chartItem, &req, this.Lang)
-		if req.FutureGoodName != `` {
-			futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartItem.ChartInfoId)
-			if err != nil {
-				br.Msg = "修改失败"
-				br.ErrMsg = "获取图表现货价格指标信息失败,指标信息失败,Err:" + err.Error()
-				return
-			}
-			futureGoodEdbInfo, err := future_good.GetFutureGoodEdbInfo(futureGoodEdbInfoMapping.EdbInfoId)
-			if err != nil {
-				if err.Error() == utils.ErrNoRow() {
-					br.Msg = "图表不存在!"
-					br.ErrMsg = "图表指标不存在,futureGoodEdbInfo:" + strconv.Itoa(futureGoodEdbInfo.FutureGoodEdbInfoId)
-					return
-				} else {
-					br.Msg = "获取图表信息失败!"
-					br.ErrMsg = "获取图表的指标信息失败,Err:" + err.Error()
-					return
-				}
-			}
-			if futureGoodEdbInfo == nil {
-				br.Msg = "期货商品指标不存在!"
-				br.ErrMsg = "期货商品指标不存在,futureGoodEdbInfo:" + strconv.Itoa(futureGoodEdbInfo.FutureGoodEdbInfoId)
-				return
-			}
-
-			list, _ := future_good.GetFutureGoodEdbInfoListByParentId(futureGoodEdbInfo.FutureGoodEdbInfoId)
-			for _, v := range list {
-				switch this.Lang {
-				case utils.EnLangVersion:
-					if v.FutureGoodEdbNameEn == `` {
-						v.FutureGoodEdbNameEn = strings.TrimPrefix(req.FutureGoodName, " ")
-						v.FutureGoodEdbNameEn = strings.TrimSuffix(req.FutureGoodName, " ")
-					} else {
-						v.FutureGoodEdbNameEn = strings.TrimPrefix(strings.Replace(v.FutureGoodEdbNameEn, v.FutureGoodEdbNameEn, req.FutureGoodName, -1), " ")
-						v.FutureGoodEdbNameEn = strings.TrimSuffix(req.FutureGoodName, " ")
-					}
-					v.Update([]string{"FutureGoodEdbNameEn"})
-				default:
-					if v.FutureGoodEdbName == `` {
-						v.FutureGoodEdbName = strings.TrimPrefix(req.FutureGoodName, " ")
-						v.FutureGoodEdbName = strings.TrimSuffix(req.FutureGoodName, " ")
-					} else {
-						v.FutureGoodEdbName = strings.TrimPrefix(strings.Replace(v.FutureGoodEdbName, v.FutureGoodEdbName, req.FutureGoodName, -1), " ")
-						v.FutureGoodEdbName = strings.TrimSuffix(req.FutureGoodName, " ")
-					}
-					v.Update([]string{"FutureGoodEdbName"})
-				}
-			}
-		}
 	case utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
 		if len(req.XDataList) > 0 {
 			// 处理横轴名称

+ 8 - 5
controllers/data_manage/line_equation/line_chart_classify.go

@@ -129,16 +129,19 @@ func getChartClassifyListForMe(adminInfo system.Admin, resp *data_manage.ChartCl
 	for _, v := range allChartInfo {
 		chartInfoMap[v.ChartClassifyId] = append(chartInfoMap[v.ChartClassifyId], v)
 	}
-	for k, v := range rootList {
+	res := make([]*data_manage.ChartClassifyItems, 0)
+	for _, v := range rootList {
 		if existItems, ok := chartInfoMap[v.ChartClassifyId]; ok {
 			v.Children = existItems
 		} else {
-			items := make([]*data_manage.ChartClassifyItems, 0)
-			v.Children = items
+			// items := make([]*data_manage.ChartClassifyItems, 0)
+			// v.Children = items
+			continue
 		}
-		rootList[k] = v
+		res = append(res, v)
+		// rootList[k] = v
 	}
-	resp.AllNodes = rootList
+	resp.AllNodes = res
 
 	return
 }

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

@@ -595,8 +595,25 @@ func (this *LineEquationChartInfoController) List() {
 		}
 		condition += " AND chart_classify_id IN(" + chartClassifyId + ") "
 	}
+
+	var keyWordArr []string
 	if keyword != "" {
-		condition += ` AND  ( chart_name LIKE '%` + keyword + `%' )`
+		newKeyWord := strings.Split(keyword, " ")
+		keywordStr := strings.Replace(keyword, " ", "", -1)
+
+		condition += " AND ( "
+		condition += ` chart_name LIKE '%` + keywordStr + `%' OR chart_name_en LIKE '%` + keywordStr + `%' OR`
+
+		keyWordArr = append(keyWordArr, newKeyWord...)
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				if v != "" {
+					condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
+				}
+			}
+		}
+		condition = strings.TrimRight(condition, "OR")
+		condition += " ) "
 	}
 
 	//只看我的

+ 51 - 23
controllers/data_manage/line_feature/chart_info.go

@@ -15,10 +15,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 统计特征图表管理
@@ -170,12 +171,12 @@ func (this *LineFeaturesChartInfoController) MultipleGraphPreview() {
 
 	// 曲线图表信息
 	curveConf := req.Curve
-	maxYear := 0
+	var maxDate time.Time
 	if edbInfoMapping.LatestDate != "" {
 		latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
-		maxYear = latestDateT.Year()
+		maxDate = latestDateT
 	}
-	startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
+	startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxDate)
 	{
 		tmpChartInfo := *chartInfo
 		// 获取图表中的指标数据
@@ -434,12 +435,12 @@ func (this *LineFeaturesChartInfoController) MultipleGraphPreviewCurve() {
 		tmpChartInfo := *chartInfo
 
 		curveConf := req.Curve
-		maxYear := 0
+		var maxDate time.Time
 		if edbInfoMapping.LatestDate != "" {
 			latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
-			maxYear = latestDateT.Year()
+			maxDate = latestDateT
 		}
-		startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
+		startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxDate)
 
 		// 获取图表中的指标数据
 		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, []*data_manage.ChartEdbInfoMapping{edbInfoMapping}, tmpChartInfo.ExtraConfig, tmpChartInfo.SeasonExtraConfig)
@@ -584,12 +585,12 @@ func (this *LineFeaturesChartInfoController) MultipleGraphConfigSaveChart() {
 	isSendEmail := true
 
 	curveConf := req.Curve
-	maxYear := 0
+	var maxDate time.Time
 	if edbInfoMapping.LatestDate != "" {
 		latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
-		maxYear = latestDateT.Year()
+		maxDate = latestDateT
 	}
-	startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
+	startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxDate)
 	switch req.Source {
 	case utils.CHART_MULTIPLE_GRAPH_CURVE: // 曲线图
 		curveConf := req.Curve
@@ -1639,8 +1640,23 @@ func (this *LineFeaturesChartInfoController) List() {
 		}
 		condition += " AND chart_classify_id IN(" + chartClassifyId + ") "
 	}
+
+	var keyWordArr []string
 	if keyword != "" {
-		condition += ` AND  ( chart_name LIKE '%` + keyword + `%' )`
+		newKeyWord := strings.Split(keyword, " ")
+		keywordStr := strings.Replace(keyword, " ", "", -1)
+
+		condition += " AND ( "
+		condition += ` chart_name LIKE '%` + keywordStr + `%' OR chart_name_en LIKE '%` + keywordStr + `%' OR`
+
+		keyWordArr = append(keyWordArr, newKeyWord...)
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
+			}
+		}
+		condition = strings.TrimRight(condition, "OR")
+		condition += " ) "
 	}
 
 	//只看我的
@@ -1830,12 +1846,15 @@ func (this *LineFeaturesChartInfoController) Detail() {
 			br.ErrMsg = "格式化配置项失败,Err:" + err.Error()
 			return
 		}
-		maxYear := 0
+		var maxDate time.Time
 		if edbMapping.LatestDate != "" {
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
-			maxYear = latestDateT.Year()
+			maxDate = latestDateT
+		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
 		}
-		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, errMsg = lineFeatureServ.GetStandardDeviationData(0, startDate, endDate, edbMapping, calculateValue)
 	case utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE:
 		var percentileConfig request.Percentile
@@ -1845,12 +1864,15 @@ func (this *LineFeaturesChartInfoController) Detail() {
 			br.ErrMsg = "格式化配置项失败,Err:" + err.Error()
 			return
 		}
-		maxYear := 0
+		var maxDate time.Time
 		if edbMapping.LatestDate != "" {
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
-			maxYear = latestDateT.Year()
+			maxDate = latestDateT
 		}
-		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+		}
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, errMsg = lineFeatureServ.GetPercentileData(0, startDate, endDate, edbMapping, percentileConfig.CalculateValue, percentileConfig.CalculateUnit, percentileConfig.PercentType)
 	case utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
 		var frequencyDistributionConfig request.FrequencyDistribution
@@ -2459,12 +2481,15 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 			errMsg = "格式化配置项失败,Err:" + tmpErr.Error()
 			return
 		}
-		maxYear := 0
+		var maxDate time.Time
 		if edbMapping.LatestDate != "" {
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
-			maxYear = latestDateT.Year()
+			maxDate = latestDateT
+		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
 		}
-		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, msg = lineFeatureServ.GetStandardDeviationData(0, startDate, endDate, edbMapping, calculateValue)
 	case utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE:
 		var percentileConfig request.Percentile
@@ -2474,12 +2499,15 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 			errMsg = "格式化配置项失败,Err:" + err.Error()
 			return
 		}
-		maxYear := 0
+		var maxDate time.Time
 		if edbMapping.LatestDate != "" {
 			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
-			maxYear = latestDateT.Year()
+			maxDate = latestDateT
+		}
+		if chartInfo.ChartType == utils.CHART_TYPE_SEASON && chartInfo.DateType == utils.DateTypeNYears {
+			maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
 		}
-		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxDate)
 		edbList, resultResp, err, msg = lineFeatureServ.GetPercentileData(0, startDate, endDate, edbMapping, percentileConfig.CalculateValue, percentileConfig.CalculateUnit, percentileConfig.PercentType)
 	case utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
 		var frequencyDistributionConfig request.FrequencyDistribution

+ 8 - 5
controllers/data_manage/line_feature/classify.go

@@ -129,16 +129,19 @@ func getChartClassifyListForMe(adminInfo system.Admin, resp *data_manage.ChartCl
 	for _, v := range allChartInfo {
 		chartInfoMap[v.ChartClassifyId] = append(chartInfoMap[v.ChartClassifyId], v)
 	}
-	for k, v := range rootList {
+	res := make([]*data_manage.ChartClassifyItems, 0)
+	for _, v := range rootList {
 		if existItems, ok := chartInfoMap[v.ChartClassifyId]; ok {
 			v.Children = existItems
 		} else {
-			items := make([]*data_manage.ChartClassifyItems, 0)
-			v.Children = items
+			// items := make([]*data_manage.ChartClassifyItems, 0)
+			// v.Children = items
+			continue
 		}
-		rootList[k] = v
+		res = append(res, v)
+		// rootList[k] = v
 	}
-	resp.AllNodes = rootList
+	resp.AllNodes = res
 
 	return
 }

+ 8 - 4
controllers/data_manage/multiple_graph_config.go

@@ -191,7 +191,7 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
-	maxYear := 0
+	var maxDate time.Time
 	var edbInfoMappingA, edbInfoMappingB *data_manage.ChartEdbInfoMapping
 	for _, v := range mappingList {
 		if v.EdbInfoId == req.EdbInfoIdA {
@@ -202,7 +202,7 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 		}
 		if v.LatestDate != "" {
 			latestDateT, _ := time.Parse(utils.FormatDate, v.LatestDate)
-			maxYear = latestDateT.Year()
+			maxDate = latestDateT
 		}
 	}
 	if edbInfoMappingA == nil {
@@ -242,7 +242,7 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 			}
 		}
 
-		startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
+		startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxDate)
 		// 获取图表中的指标数据
 		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, mappingList, tmpChartInfo.ExtraConfig, tmpChartInfo.SeasonExtraConfig)
 		if err != nil {
@@ -362,7 +362,7 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 
 			// 数据的开始/结束日期
 			curveConf := req.Curve
-			startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
+			startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxDate)
 
 			rollingCorrelationData := make([]interface{}, 0)
 			for _, rollingCorrelationConf := range rollingCorrelationConfList {
@@ -513,6 +513,10 @@ func (this *ChartInfoController) MultipleGraphPreviewCurve() {
 			startDate = "2020-01-01"
 		case 11:
 			startDate = "2022-01-01"
+		case 12:
+			startDate = "2023-01-01"
+		case 13:
+			startDate = "2024-01-01"
 		}
 
 		edbInfoType := 0

+ 18 - 1
controllers/data_manage/my_chart.go

@@ -96,8 +96,25 @@ func (this *MyChartController) ChartList() {
 		condition += " AND chart_classify_id IN(" + utils.GetOrmInReplace(len(chartClassifyIds)) + ") "
 		pars = append(pars, chartClassifyIds)
 	}
+
+	var keyWordArr []string
 	if keyWord != "" {
-		condition += ` AND  ( chart_name LIKE '%` + keyWord + `%' OR chart_name_en LIKE '%` + keyWord + `%' )`
+		newKeyWord := strings.Split(keyWord, " ")
+		keywordStr := strings.Replace(keyWord, " ", "", -1)
+
+		condition += " AND ( "
+		condition += ` chart_name LIKE '%` + keywordStr + `%' OR chart_name_en LIKE '%` + keywordStr + `%' OR`
+
+		keyWordArr = append(keyWordArr, newKeyWord...)
+		if len(keyWordArr) > 0 {
+			for _, v := range keyWordArr {
+				if v != "" {
+					condition += ` chart_name LIKE '%` + v + `%' OR chart_name_en LIKE '%` + v + `%' OR`
+				}
+			}
+		}
+		condition = strings.TrimRight(condition, "OR")
+		condition += " ) "
 	}
 
 	//只看我的

+ 9 - 0
controllers/data_manage/predict_edb_classify.go

@@ -792,6 +792,15 @@ func (this *PredictEdbClassifyController) SimpleList() {
 		sort.Sort(sortList)
 	}
 
+	if isOnlyMe {
+		sortList, err = data.GetEdbClassifyByIsMe(sysUserId, parentId, 1, sortList)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取只看我的分类列表失败,err:" + err.Error()
+			return
+		}
+	}
+
 	// 是否允许添加一级分类
 	canOpClassify := true
 	button := data.GetPredictEdbClassifyOpButton(this.SysUser, 0, true)

+ 36 - 6
controllers/data_manage/predict_edb_info.go

@@ -504,6 +504,19 @@ func (this *PredictEdbInfoController) Add() {
 	}
 	resp := respItem.Data
 
+	edbInfoIdArr := make([]int, 0)
+	//查询相关的指标
+	edbMappingList, err := data_manage.GetAllCalculateByEdbInfoIdV2(resp.EdbInfoId)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取引用的指标信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range edbMappingList {
+		edbInfoIdArr = append(edbInfoIdArr, v.FromEdbInfoId)
+	}
+	// 添加指标引用记录
+	_ = data.SavePredictEdbInfoRelation(edbInfoIdArr, resp.EdbInfoId)
 	//添加es
 	data.AddOrEditEdbInfoToEs(resp.EdbInfoId)
 
@@ -731,6 +744,19 @@ func (this *PredictEdbInfoController) Edit() {
 	}
 	resp := respItem.Data
 
+	edbInfoIdArr := make([]int, 0)
+	//查询相关的指标
+	edbMappingList, err := data_manage.GetAllCalculateByEdbInfoIdV2(resp.EdbInfoId)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取引用的指标信息失败,Err:" + err.Error()
+		return
+	}
+	for _, v := range edbMappingList {
+		edbInfoIdArr = append(edbInfoIdArr, v.FromEdbInfoId)
+	}
+	// 添加指标引用记录
+	_ = data.SavePredictEdbInfoRelation(edbInfoIdArr, resp.EdbInfoId)
 	//修改es
 	data.AddOrEditEdbInfoToEs(resp.EdbInfoId)
 
@@ -1458,13 +1484,17 @@ func (this *PredictEdbInfoController) DataList() {
 		}
 	}
 
-	maxYear := 0
+	var maxDate time.Time
 	if edbInfo.LatestDate != "" {
 		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
-		maxYear = latestDateT.Year()
+		maxDate = latestDateT
+	}
+	if chartType == utils.CHART_TYPE_SEASON && dateType == utils.DateTypeNYears {
+		// 季节性图需要特殊处理最近N年数据
+		maxDate = time.Date(maxDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxDate)
 	if endDate == "" {
 		endDate = time.Now().Format(utils.FormatDate)
 	}
@@ -1719,13 +1749,13 @@ func (this *PredictEdbInfoController) ChartDataList() {
 		return
 	}
 	// todo 确认预测指标是否用的是来源指标的最新日期
-	maxYear := 0
+	var maxDate time.Time
 	if sourceEdbInfoItem.LatestDate != "" {
 		latestDateT, _ := time.Parse(utils.FormatDate, sourceEdbInfoItem.LatestDate)
-		maxYear = latestDateT.Year()
+		maxDate = latestDateT
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxDate)
 	if endDate == "" {
 		endDate = time.Now().Format(utils.FormatDate)
 	}

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

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/data/data_manage_permission"
+	"eta/eta_api/services/data/range_analysis"
 	"eta/eta_api/utils"
 	"fmt"
 	"sort"
@@ -127,6 +128,14 @@ func (this *RangeChartClassifyController) ChartClassifyList() {
 			nodeAll = append(nodeAll, v)
 		}
 	}
+	if isShowMe {
+		nodeAll, err = range_analysis.GetClassifyListByIsShowMe(this.SysUser.AdminId, parentId, source, nodeAll)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	// 整体排序
 	if len(nodeAll) > 0 {

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

@@ -1804,6 +1804,9 @@ func (this *RangeChartChartInfoController) SearchByEs() {
 	startSize = paging.StartIndex(currentIndex, pageSize)
 
 	keyword := this.GetString("Keyword")
+	if keyword == `` { // 兼容jp前端
+		keyword = this.GetString("KeyWord")
+	}
 
 	//只看我的
 	isShowMe, _ := this.GetBool("IsShowMe")

+ 220 - 0
controllers/data_manage/stl/stl.go

@@ -0,0 +1,220 @@
+package stl
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/services/data/stl"
+	"eta/eta_api/utils"
+
+	"eta/eta_api/models/data_manage/stl/request"
+	"eta/eta_api/models/data_manage/stl/response"
+)
+
+type STLController struct {
+	controllers.BaseAuthController
+}
+
+// Preview
+// @Title STL分解预览
+// @Description STL分解预览
+// @Success 200 {object} request.STLReq
+// @router /chart_info/preview [post]
+func (c *STLController) Preview() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req *request.StlConfigReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "请求参数错误"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	resp, msg, err := stl.GenerateStlEdbData(req, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "预览异常"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Data = resp
+	br.Msg = "预览成功"
+	br.Ret = 200
+	br.Success = true
+	return
+}
+
+// SaveConf
+// @Title STL分解配置保存
+// @Description STL分解配置保存
+// @Success 200 {object} request.STLReq
+// @router /config/save [post]
+func (c *STLController) SaveConf() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req *request.StlConfigReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "请求参数错误"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	configId, msg, err := stl.SaveStlConfig(req, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "保存异常"
+		}
+		br.Msg = msg
+		br.ErrMsg = "保存失败,err:" + err.Error()
+		return
+	}
+	resp := new(response.SaveStlConfigResp)
+	resp.CalculateStlConfigId = configId
+
+	br.Data = resp
+	br.Msg = "保存成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// ConfigDetail
+// @Title STL分解配置保存
+// @Description STL分解配置保存
+// @Success 200 {object} request.STLReq
+// @router /config/detail [get]
+func (c *STLController) ConfigDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	edbInfoId, err := c.GetInt("EdbInfoId")
+	if err != nil {
+		br.Msg = "请求参数错误"
+		br.ErrMsg = err.Error()
+		return
+	}
+	resp, msg, err := stl.GetStlConfig(edbInfoId)
+	if err != nil {
+		if msg == "" {
+			msg = "获取异常"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// SaveStlEdbInfo
+// @Title STL分解指标保存到指标库
+// @Description STL分解指标保存到指标库
+// @Success 200 {object} request.STLReq
+// @router /edb_info/save [post]
+func (c *STLController) SaveStlEdbInfo() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req *request.SaveStlEdbInfoReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "请求参数错误"
+		br.ErrMsg = err.Error()
+		return
+	}
+	sysUser := c.SysUser
+
+	edbInfoId, IsSendEmail, msg, err := stl.SaveStlEdbInfo(req, sysUser.AdminId, sysUser.RealName, c.Lang)
+	if err != nil {
+		if msg == "" {
+			msg = "保存异常"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+	resp := new(response.SaveStlEdbInfoResp)
+	resp.EdbInfoId = edbInfoId
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.IsSendEmail = IsSendEmail
+}
+
+// EdbInfoFilterByEs
+// @Title 指标筛选接口
+// @Description 指标筛选接口
+// @Param   KeyWord   query   string  false       "搜索关键词:指标ID/指标名称"
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Success 200 {object} data_manage.EdbInfoList
+// @router /edb_info/search [get]
+func (this *STLController) EdbInfoFilterByEs() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	keyWord := this.GetString("KeyWord")
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	resp, msg, err := stl.SearchEdbInfoWithStl(this.SysUser.AdminId, keyWord, currentIndex, pageSize)
+	if err != nil {
+		if msg == "" {
+			msg = "获取异常"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 9 - 5
controllers/data_manage/supply_analysis/variety_edb.go

@@ -11,12 +11,13 @@ import (
 	supply_analysisServ "eta/eta_api/services/data/supply_analysis"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/tealeg/xlsx"
 	"os"
 	"path/filepath"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/tealeg/xlsx"
 )
 
 // EdbList
@@ -96,7 +97,7 @@ func (this *VarietyController) EdbList() {
 
 		if tmpItem, ok := edbInfoAndClassifyMap[v.EdbInfoId]; ok {
 			classifyNameList := make([]string, 0)
- 
+
 			// 所属分类
 			{
 				classifyList, tmpErr, errMsg := data.GetFullClassifyByClassifyId(tmpItem.ClassifyId)
@@ -620,7 +621,7 @@ func (this *VarietyController) EdbInfoDataTb() {
 		Calendar:         "",
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate.Year())
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate)
 
 	var startDateTime time.Time
 	if startDate != `` {
@@ -749,7 +750,10 @@ func (this *VarietyController) EdbInfoDataSeasonal() {
 		Calendar:         "",
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate.Year())
+	// 特殊处理季节性图的日期
+	maxDate := time.Date(varietyEdbInfo.EndDate.Year()+1, 1, 1, 0, 0, 0, 0, time.Local)
+
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxDate)
 
 	dataList, minVal, maxVal, err := supply_analysisServ.GetChartEdbSeasonalData(varietyEdbId, calendar, startDate, endDate, edbInfo.LatestDate)
 	if err != nil {
@@ -848,7 +852,7 @@ func (this *VarietyController) EdbDataListV2() {
 		Calendar:         "",
 	}
 
-	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate.Year())
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate)
 
 	var startDateTime time.Time
 	if startDate != `` {

+ 1394 - 0
controllers/data_manage/usda_fas_data.go

@@ -0,0 +1,1394 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+)
+
+type BaseFromUsdaFasController struct {
+	controllers.BaseAuthController
+}
+
+// UsdaFasClassify
+// @Title 美国农业部数据分类
+// @Description 美国农业部数据分类接口
+// @Success 200 {object} data_manage.BaseFromUsdaFasClassify
+// @router /usda_fas/classify [get]
+func (this *BaseFromUsdaFasController) UsdaFasClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	classifyAll, err := data_manage.GetAllBaseFromUsdaFasClassify()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	//组装一级分类
+	rootMap := make(map[int][]*data_manage.BaseFromUsdaFasClassifyItems)
+	list := make([]*data_manage.BaseFromUsdaFasClassifyItems, 0)
+	for _, classify := range classifyAll {
+		classify.UniqueCode = strconv.Itoa(classify.ClassifyId)
+		if classify.ParentId == 0 {
+			if _, ok := rootMap[classify.ClassifyId]; !ok {
+				rootMap[classify.ClassifyId] = make([]*data_manage.BaseFromUsdaFasClassifyItems, 0)
+				list = append(list, classify)
+
+			}
+		} else {
+			child, ok := rootMap[classify.ParentId]
+			if ok {
+				child = append(child, classify)
+				rootMap[classify.ParentId] = child
+			}
+		}
+	}
+
+	for k, v := range list {
+		child, ok := rootMap[v.ClassifyId]
+		if ok {
+			list[k].Children = child
+		}
+	}
+	//组装二级分类
+	var ret data_manage.BaseFromUsdaFasClassifyResp
+	ret.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// UsdaFasIndexData
+// @Title 获取美国农业部数据
+// @Description 获取美国农业部数据接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   string  true       "分类id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /usda_fas/index/data [get]
+func (this *BaseFromUsdaFasController) UsdaFasIndexData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+	// 增加频度请求入参
+	frequency := this.GetString("Frequency")
+
+	//获取指标
+	var condition string
+	var pars []interface{}
+
+	if classifyId >= 0 {
+		classifyInfo, err := data_manage.GetBaseFromUsdaFasClassifyById(classifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "分类不存在"
+				return
+			}
+			br.Msg = "获取分类信息失败"
+			br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+			return
+		}
+		if classifyInfo.Level == 2 || classifyInfo.ParentId > 0 {
+			condition += ` AND classify_id=? `
+			pars = append(pars, classifyId)
+		} else if classifyInfo.Level == 1 {
+			childClassify, err := data_manage.GetBaseFromUsdaFasClassifyByParentId(classifyId)
+			if err != nil {
+				br.Msg = "获取分类信息失败"
+				br.ErrMsg = "获取子分类信息失败,Err:" + err.Error()
+				return
+			}
+			var classifyList []int
+			for _, v := range childClassify {
+				classifyList = append(classifyList, v.ClassifyId)
+			}
+			condition += ` AND classify_id IN (` + utils.GetOrmInReplace(len(classifyList)) + `) `
+			pars = append(pars, classifyList)
+		}
+	}
+	if frequency != "" {
+		condition += ` AND frequency=? `
+		pars = append(pars, frequency)
+	}
+
+	UsdaFasList, err := data_manage.GetUsdaFasIndex(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	edbCodeList := make([]string, 0)
+	for _, v := range UsdaFasList {
+		edbCodeList = append(edbCodeList, v.IndexCode)
+	}
+	edbInfoMap := make(map[string]*data_manage.EdbInfo)
+	dataMap := make(map[string][]*data_manage.BaseFromUsdaFasData)
+	total := 0
+	if len(edbCodeList) > 0 {
+		edbInfoList, err := data_manage.GetEdbInfoByEdbCodeList(utils.DATA_SOURCE_USDA_FAS, edbCodeList)
+		if err != nil {
+			br.Msg = "获取数据源失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range edbInfoList {
+			edbInfoMap[v.EdbCode] = v
+		}
+		// 首先对分类下的指标按照日期进行分页,再针对日期,进行排序
+		dataTimes, err := data_manage.GetUsdaFasIndexDataTimePageByCodes(edbCodeList, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据日期信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataTimes) > 0 {
+			startDate := dataTimes[len(dataTimes)-1]
+			endDate := dataTimes[0]
+			// 把截止日往后加1天
+			endDateT, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+			endDate = endDateT.AddDate(0, 0, 1).Format(utils.FormatDate)
+			dataList, e := data_manage.GetUsdaFasIndexDataByDataTime(edbCodeList, startDate, endDate)
+			if e != nil {
+				br.Msg = "获取数据失败"
+				br.ErrMsg = "获取指标数据失败,Err:" + e.Error()
+				return
+			}
+			//将数据按照指标进行分类
+			for _, v := range dataList {
+				dataMap[v.IndexCode] = append(dataMap[v.IndexCode], v)
+			}
+		}
+
+		total, err = data_manage.GetUsdaFasIndexDataTimePageCount(edbCodeList)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	resultList := make([]*data_manage.BaseFromUsdaFasIndexList, 0)
+
+	for _, v := range UsdaFasList {
+		product := new(data_manage.BaseFromUsdaFasIndexList)
+		product.BaseFromUsdaFasIndexId = v.BaseFromUsdaFasIndexId
+		product.Unit = v.Unit
+		product.IndexCode = v.IndexCode
+		product.IndexName = v.IndexName
+		product.Frequency = v.Frequency
+		product.ModifyTime = v.ModifyTime
+		if edb, ok := edbInfoMap[v.IndexCode]; ok {
+			product.EdbInfoId = edb.EdbInfoId
+			product.EdbExist = 1
+		}
+
+		/*total, err := data_manage.GetUsdaFasIndexDataCount(v.IndexCode)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+		page := paging.GetPaging(currentIndex, pageSize, total)
+		dataList, err := data_manage.GetUsdaFasIndexData(v.IndexCode, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}*/
+		dataListTmp, ok := dataMap[v.IndexCode]
+		if !ok {
+			dataListTmp = make([]*data_manage.BaseFromUsdaFasData, 0)
+		}
+		product.DataList = dataListTmp
+		product.Paging = page
+		resultList = append(resultList, product)
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resultList
+}
+
+// UsdaFasSearchList
+// @Title UsdaFas模糊搜索
+// @Description UsdaFas模糊搜索
+// @Param   Keyword   query   string  ture       "关键字搜索"
+// @Success 200 {object} models.BaseResponse
+// @router /usda_fas/search_list [get]
+func (this *BaseFromUsdaFasController) UsdaFasSearchList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	list := make([]*data_manage.BaseFromUsdaFasIndexSearchItem, 0)
+	var err error
+	//关键字
+	keyword := this.GetString("Keyword")
+	if keyword != "" {
+		keyWordArr := strings.Split(keyword, " ")
+
+		if len(keyWordArr) > 0 {
+			condition := ""
+			for _, v := range keyWordArr {
+				condition += ` AND CONCAT(index_name,index_code) LIKE '%` + v + `%'`
+			}
+			list, err = data_manage.GetUsdaFasItemList(condition)
+			if err != nil {
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				br.Msg = "获取失败"
+				return
+			}
+		}
+
+	} else {
+		// todo es 模糊搜索
+		list, err = data_manage.GetUsdaFasItemList("")
+		if err != nil {
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			br.Msg = "获取失败"
+			return
+		}
+	}
+	classifyIds := make([]int, 0)
+	for _, v := range list {
+		classifyIds = append(classifyIds, v.ClassifyId)
+	}
+	classifyList, err := data_manage.GetBaseFromUsdaFasClassifyByIds(classifyIds)
+	if err != nil {
+		br.Msg = "搜索失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	classifyMap := make(map[int]int)
+	for _, v := range classifyList {
+		classifyMap[v.ClassifyId] = v.ParentId
+	}
+	for _, v := range list {
+		v.ParentClassifyId = classifyMap[v.ClassifyId]
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// UsdaFasSingleData
+// @Title 获取UsdaFas数据
+// @Description 获取UsdaFas单条数据接口
+// @Param   IndexCode   query   string  true       "指标唯一编码"
+// @Success 200 {object} models.BaseResponse
+// @router /usda_fas/single_data [get]
+func (this *BaseFromUsdaFasController) UsdaFasSingleData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	indexCode := this.GetString("IndexCode")
+	indexInfo, err := data_manage.GetBaseFromUsdaFasIndexByIndexCode(indexCode)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+	dataTmpList, err := data_manage.GetUsdaFasIndexDataByCode(indexCode)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_USDA_FAS, indexCode)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取数据源失败"
+		br.ErrMsg = "获取数据源失败,Err:" + err.Error()
+		return
+	}
+
+	var ret data_manage.UsdaFasSingleDataResp
+	var dataList []*data_manage.UsdaFasSingleData
+
+	if edbInfo != nil {
+		ret.EdbInfoId = edbInfo.EdbInfoId
+		ret.EdbExist = 1
+	}
+	ret.ClassifyId = indexInfo.ClassifyId
+	ret.BaseFromUsdaFasIndexId = indexInfo.BaseFromUsdaFasIndexId
+	ret.IndexCode = indexInfo.IndexCode
+	ret.IndexName = indexInfo.IndexName
+	ret.Frequency = indexInfo.Frequency
+	ret.CreateTime = indexInfo.CreateTime.Format(utils.FormatDateTime)
+	ret.ModifyTime = indexInfo.ModifyTime.Format(utils.FormatDateTime)
+	ret.Unit = indexInfo.Unit
+	for _, v := range dataTmpList {
+		tmp := &data_manage.UsdaFasSingleData{
+			Value:    v.Value,
+			DataTime: v.DataTime,
+		}
+		dataList = append(dataList, tmp)
+	}
+	ret.Data = dataList
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// UsdaFasIndexList
+// @Title 美国农业部指标列表
+// @Description 美国农业部指标列表
+// @Param   ClassifyId   query   int  true       "分类id"
+// @Success 200 {object} data_manage.BaseFromUsdaFasClassifyResp
+// @router /usda_fas/classify/index/list [get]
+func (this *BaseFromUsdaFasController) UsdaFasIndexList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+	indexList, err := data_manage.GetUsdaFasIndexByClassifyId(classifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+
+	var ret data_manage.BaseFromUsdaFasClassifyResp
+	list := make([]*data_manage.BaseFromUsdaFasClassifyItems, 0)
+	for _, v := range indexList {
+		classify := new(data_manage.BaseFromUsdaFasClassifyItems)
+		classify.ClassifyId = classifyId
+		classify.BaseFromUsdaFasIndexId = v.BaseFromUsdaFasIndexId
+		classify.IndexCode = v.IndexCode
+		classify.ClassifyName = v.IndexName
+		classify.UniqueCode = fmt.Sprintf("%d_%d", classifyId, v.BaseFromUsdaFasIndexId)
+		list = append(list, classify)
+	}
+	ret.List = list
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// UsdaFasBatchSearch
+// @Title 美国农业部指标查询
+// @Description 美国农业部指标查询
+// @Param   ClassifyIds   query   string  true       "分类id, 多个分类用英文"
+// @Param   Keyword   query   string  true       "关键词, 指标ID/指标名称"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /usda_fas/batch_search [get]
+func (this *BaseFromUsdaFasController) UsdaFasBatchSearch() {
+	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
+	}
+	classifyIdStr := this.GetString("ClassifyIds")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+	resp := data_manage.BaseFromUsdaFasIndexSearchList{}
+	total := 0
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	var list = make([]*data_manage.BaseFromUsdaFasIndexList, 0)
+	var condition string
+	var pars []interface{}
+	classifyIds := strings.Split(classifyIdStr, ",")
+	if len(classifyIds) > 0 && classifyIds[0] != `` {
+		condition += " AND classify_id IN (" + utils.GetOrmInReplace(len(classifyIds)) + " ) "
+		pars = append(pars, classifyIds)
+	}
+	keyword := this.GetString("Keyword")
+	if keyword != `` {
+		condition += " AND (index_name like ? OR index_code like ?) "
+		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+	}
+	frequencies := this.GetString("Frequencies")
+	if frequencies != "" {
+		frequencyList := strings.Split(frequencies, ",")
+		condition += " AND frequency IN (" + utils.GetOrmInReplace(len(frequencyList)) + " ) "
+		pars = append(pars, frequencyList)
+	}
+	/*if classifyIdStr == `` && keyword == `` && frequencies == `` {
+		resp.Paging = page
+		resp.List = list
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		return
+	}*/
+	condition += ` AND index_code not in (SELECT edb_code FROM edb_info WHERE source=?) `
+	pars = append(pars, utils.DATA_SOURCE_USDA_FAS)
+
+	list, err := data_manage.GetUsdaFasIndexPage(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	total, err = data_manage.GetUsdaFasIndexPageCount(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, total)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// UsdaFasBatchAdd
+// @Title 美国农业部批量新增
+// @Description 美国农业部批量新增
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /usda_fas/batch_add [post]
+func (this *BaseFromUsdaFasController) UsdaFasBatchAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_BATCH_ADD_UsdaFas_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		v.Frequency = strings.TrimSpace(v.Frequency)
+		if v.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		v.Unit = strings.TrimSpace(v.Unit)
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+	}
+
+	// 限定同一时间最多批量新增30个指标
+	for _, v := range req {
+		var r data_manage.UsdaFasIndexSource2EdbReq
+		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.UsdaFasIndexSource2Edb(r, this.Lang)
+		if e != nil {
+			br.Msg = "操作失败"
+			if errMsg != "" {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = e.Error()
+			return
+		}
+		if skip {
+			continue
+		}
+
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(this.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = this.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// UsdaFasNameCheck
+// @Title 加入指标库的重名检测
+// @Description 加入指标库的重名检测
+// @Param   ClassifyIds   query   string  true       "分类id, 多个分类用英文"
+// @Param   Keyword   query   string  true       "关键词, 指标ID/指标名称"
+// @Success 200 {object} NameCheckResult
+// @router /usda_fas/edb_info/name_check [post]
+func (this *BaseFromUsdaFasController) UsdaFasNameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req []*data_manage.NameCheckEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req)
+	if codeLen > codeMaxT {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+
+	indexNames := make([]string, 0)
+	resp := make([]*data_manage.EdbNameCheckResult, 0)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &data_manage.EdbNameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+		dataItems, err := data_manage.GetEdbDataAllByEdbCode(v.EdbCode, utils.DATA_SOURCE_USDA_FAS, 0, utils.EDB_DATA_LIMIT)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取钢联已存在信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataItems) <= 0 {
+			respItem, err := data.AddEdbData(utils.DATA_SOURCE_USDA_FAS, v.EdbCode, v.Frequency)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				return
+			}
+			if respItem.Ret != 200 {
+				br.Msg = "未搜索到该指标"
+				br.ErrMsg = respItem.ErrMsg + ";EdbCode:" + v.EdbCode
+				return
+			}
+		}
+	}
+
+	// 重名校验
+	edbList, e := data_manage.GetEdbInfoByNameArr(indexNames, utils.EDB_INFO_TYPE)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名指标失败, Err: " + e.Error()
+		return
+	}
+	nameExists := make(map[string]bool)
+	for _, v := range edbList {
+		nameExists[v.EdbName] = true
+	}
+	if len(nameExists) > 0 {
+		for _, v := range resp {
+			v.Exist = nameExists[v.EdbName]
+		}
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// UsdaFasAddCheck
+// @Title 加入指标库指标Id检测
+// @Description 加入指标库指标Id检测
+// @Param	request	body request.BatchAddCheckReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /usda_fas/edb_info/add_check [post]
+func (c *BaseFromUsdaFasController) UsdaFasAddCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.BatchAddCheckReq
+	if e := json.Unmarshal(c.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req.IndexCodes)
+
+	// 获取指标库已有指标
+	existsEdb, e := data_manage.GetEdbCodesBySource(utils.DATA_SOURCE_USDA_FAS)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取自有数据已添加的指标失败, Err: " + e.Error()
+		return
+	}
+	existMap := make(map[string]*data_manage.EdbInfo)
+	for _, v := range existsEdb {
+		existMap[v.EdbCode] = v
+	}
+
+	if codeLen == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if codeLen > codeMaxT {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMaxT)
+		return
+	}
+
+	// 查询选中的指标
+	cond := fmt.Sprintf(` AND index_code IN (%s)`, utils.GetOrmInReplace(codeLen))
+	pars := make([]interface{}, 0)
+	pars = append(pars, req.IndexCodes)
+	list, err := data_manage.GetUsdaFasIndex(cond, pars)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取钢联已存在信息失败,Err:" + err.Error()
+		return
+	}
+
+	if len(list) > codeMaxT {
+		br.Msg = fmt.Sprintf("最多只能选择%d个指标", codeMaxT)
+		return
+	}
+
+	resp := make([]*data_manage.BaseFromUsdaFasIndexList, 0)
+	for _, v := range list {
+		if edb, ok := existMap[v.IndexCode]; ok {
+			v.EdbInfoId = edb.EdbInfoId
+			v.EdbClassifyId = edb.ClassifyId
+			v.EdbUniqueCode = edb.UniqueCode
+			v.EdbExist = 1
+		}
+		resp = append(resp, v)
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// UsdaFasEdbInfoAdd
+// @Title 新增指标接口
+// @Description 新增指标接口
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /usda_fas/edb_info/add [post]
+func (this *BaseFromUsdaFasController) UsdaFasEdbInfoAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := "CACHE_EDB_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req data_manage.AddEdbInfoReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	req.EdbName = strings.Trim(req.EdbName, " ")
+	req.EdbCode = strings.Trim(req.EdbCode, " ")
+
+	if req.EdbCode == "" {
+		br.Msg = "指标ID不能为空"
+		return
+	}
+
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	count, err := data_manage.GetUsdaFasIndexDataCount(req.EdbCode)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	if count == 0 {
+		br.Msg = "指标不存在"
+	}
+
+	// 指标入库
+	edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(utils.DATA_SOURCE_USDA_FAS, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, req.StartDate, req.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	// 试用平台更新用户累计新增指标数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + e.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserIndexNum(r)
+		}()
+	}
+
+	//新增操作日志
+	{
+		edbLog := new(data_manage.EdbInfoLog)
+		edbLog.EdbInfoId = edbInfo.EdbInfoId
+		edbLog.SourceName = edbInfo.SourceName
+		edbLog.Source = edbInfo.Source
+		edbLog.EdbCode = edbInfo.EdbCode
+		edbLog.EdbName = edbInfo.EdbName
+		edbLog.ClassifyId = edbInfo.ClassifyId
+		edbLog.SysUserId = sysUser.AdminId
+		edbLog.SysUserRealName = sysUser.RealName
+		edbLog.CreateTime = time.Now()
+		edbLog.Content = string(this.Ctx.Input.RequestBody)
+		edbLog.Status = "新增指标"
+		edbLog.Method = this.Ctx.Input.URI()
+		go data_manage.AddEdbInfoLog(edbLog)
+	}
+
+	// 更新es
+	go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+
+	resp := new(data_manage.AddEdbInfoResp)
+	resp.EdbInfoId = edbInfo.EdbInfoId
+	resp.UniqueCode = edbInfo.UniqueCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// ExportUsdaFasList
+// @Title 导出美国农业部数据
+// @Description 导出美国农业部数据
+// @Param   ClassifyId   query   int  true       "关键字搜索"
+// @Param   IndexCode   query   string  true       "指标编码"
+// @Success 200  导出成功
+// @router /usda_fas/export [get]
+func (this *BaseFromUsdaFasController) ExportUsdaFasList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	indexCode := this.GetString("IndexCode")
+
+	if classifyId <= 0 && indexCode == "" {
+		br.Msg = "请选择分类或者指标"
+		return
+	}
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downLoadnFilePath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	var condition string
+	var pars []interface{}
+	var classifyName string
+	if classifyId > 0 {
+		classifyInfo, err := data_manage.GetBaseFromUsdaFasClassifyById(classifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "分类不存在"
+				return
+			}
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取分类失败,Err:" + err.Error()
+			return
+		}
+		classifyName = classifyInfo.ClassifyName
+		childClassify, err := data_manage.GetBaseFromUsdaFasClassifyByParentId(classifyId)
+		if err != nil {
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取分类失败,Err:" + err.Error()
+			return
+		}
+
+		if len(childClassify) > 0 {
+			condition += `AND classify_id IN (` + utils.GetOrmInReplace(len(childClassify)) + `)`
+			for _, child := range childClassify {
+				pars = append(pars, child.ClassifyId)
+			}
+		} else {
+			condition += ` AND classify_id=?`
+			pars = append(pars, classifyId)
+		}
+	}
+	if indexCode != "" {
+		condition += ` AND index_code=? `
+		pars = append(pars, indexCode)
+	}
+
+	indexList, err := data_manage.GetUsdaFasIndex(condition, pars)
+	if err != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "获取指标失败,Err:" + err.Error()
+		fmt.Println("获取数据失败,Err:" + err.Error())
+		return
+	}
+	if len(indexList) <= 0 {
+		fmt.Println("indexList 为空")
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "success"
+		return
+	}
+
+	codeList := make([]string, 0)
+	frequenciesMap := make(map[string][]*data_manage.BaseFromUsdaFasIndexList)
+	for _, v := range indexList {
+		codeList = append(codeList, v.IndexCode)
+		frequenciesMap[v.Frequency] = append(frequenciesMap[v.Frequency], v)
+	}
+	dataListMap := make(map[string][]*data_manage.BaseFromUsdaFasData)
+	if len(indexList) > 0 {
+		allDataList, e := data_manage.GetUsdaFasIndexDataByCodes(codeList)
+		if e != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取数据失败,Err:" + e.Error()
+			return
+		}
+		for _, v := range allDataList {
+			dataListMap[v.IndexCode] = append(dataListMap[v.IndexCode], v)
+		}
+	}
+	// 按照频率分组排序
+	frequencies := []string{
+		"日度", "周度", "旬度", "月度", "季度", "半年度", "年度",
+	}
+	for _, frequency := range frequencies {
+		//获取指标
+		indexCodeList, ok := frequenciesMap[frequency]
+		if !ok {
+			continue
+		}
+		if len(indexCodeList) <= 0 {
+			fmt.Printf("sheet:%s, 不存在指标", frequency)
+			return
+		}
+		var sheetName string
+		switch frequency {
+		case "日度":
+			sheetName = "日度(Daily)"
+		case "周度":
+			sheetName = "周度(Weekly)"
+		case "旬度":
+			sheetName = "旬度(ten-day)"
+		case "月度":
+			sheetName = "月度(Monthly)"
+		case "季度":
+			sheetName = "季度(Quarterly)"
+		case "半年度":
+			sheetName = "半年度(Semi-annual)"
+		case "年度":
+			sheetName = "年度(Annual)"
+		default:
+			sheetName = "其他数据"
+		}
+		sheetNew, err := xlsxFile.AddSheet(sheetName)
+		if err != nil {
+			fmt.Println("新增Sheet失败", err.Error())
+			return
+		}
+		secNameRow := sheetNew.AddRow()
+		frequencyRow := sheetNew.AddRow()
+		unitRow := sheetNew.AddRow()
+		lastModifyDateRow := sheetNew.AddRow()
+
+		var indexIdList []int
+		for _, idx := range frequenciesMap[frequency] {
+			indexIdList = append(indexIdList, idx.BaseFromUsdaFasIndexId)
+		}
+		dataTimeList, err := data_manage.GetUsdaFasDataDataTimeByIndexId(indexIdList)
+		if err != nil {
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取数据时间失败,Err:" + err.Error()
+			fmt.Println("获取数据时间失败", err.Error())
+			return
+		}
+
+		// 添加excel左侧指标日期
+		setRowIndex := 4
+		for rk, dv := range dataTimeList {
+			rowIndex := setRowIndex + rk
+			row := sheetNew.Row(rowIndex)
+			displayDate, _ := time.Parse(utils.FormatDate, dv)
+			displayDateCell := row.AddCell()
+			style := new(xlsx.Style)
+			style.ApplyAlignment = true
+			style.Alignment.WrapText = true
+			displayDateCell.SetStyle(style)
+			displayDateCell.SetDate(displayDate)
+
+		}
+		for k, icl := range indexCodeList {
+			// 获取数据
+			dataList, ok := dataListMap[icl.IndexCode]
+			if !ok {
+				continue
+			}
+			if k == 0 {
+				secNameRow.AddCell().SetValue("指标名称/Metric Name")
+				frequencyRow.AddCell().SetValue("频度/Frequency")
+				unitRow.AddCell().SetValue("单位/Unit")
+				lastModifyDateRow.AddCell().SetValue("更新时间/Update Time")
+				min := k * 3
+				sheetNew.SetColWidth(min, min, 15)
+			}
+			if len(dataList) == 0 {
+				continue
+			}
+			secNameRow.AddCell().SetValue(icl.IndexName)
+			frequencyRow.AddCell().SetValue(icl.Frequency)
+			unitRow.AddCell().SetValue(icl.Unit)
+
+			timeDate, err := time.Parse(utils.FormatDateTime, dataList[0].ModifyTime)
+			if err != nil {
+				continue
+			}
+			lastModifyDateRow.AddCell().SetValue(timeDate.Format(utils.FormatDate))
+			dataInfoMap := make(map[string]*data_manage.BaseFromUsdaFasData)
+			for _, v := range dataList {
+				dataInfoMap[v.DataTime] = v
+			}
+
+			for rk, dtv := range dataTimeList {
+				rowIndex := setRowIndex + rk
+				row := sheetNew.Row(rowIndex)
+				displayDateCell := row.AddCell()
+				tmpData, ok := dataInfoMap[dtv]
+				if ok {
+					displayDateCell.SetValue(tmpData.Value)
+				}
+			}
+		}
+	}
+
+	err = xlsxFile.Save(downLoadnFilePath)
+	if err != nil {
+		//有指标无数据时先导出一遍空表
+		sheet, err := xlsxFile.AddSheet("无数据")
+		if err != nil {
+			br.Msg = "新增Sheet失败"
+			br.ErrMsg = "新增Sheet失败,Err:" + err.Error()
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		e := xlsxFile.Save(downLoadnFilePath)
+		if e != nil {
+			br.Msg = "保存文件失败"
+			br.ErrMsg = "保存文件失败"
+			return
+		}
+	}
+
+	fileName := classifyName
+	if indexCode != "" && len(indexList) == 1 {
+		fileName = indexList[0].IndexName
+	}
+	fileName = strings.Replace(fileName, ": ", "_", -1)
+	fileName = strings.Replace(fileName, ", ", "_", -1)
+	fileName = strings.Replace(fileName, " ", "_", -1)
+	fileName += time.Now().Format("06.01.02") + `.xlsx` //文件名称
+	fmt.Println(fileName)
+	this.Ctx.Output.Download(downLoadnFilePath, fileName)
+	defer func() {
+		os.Remove(downLoadnFilePath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+}
+
+// GetFrequency
+// @Title 美国农业部数据频度
+// @Description 美国农业部数据频度接口
+// @Param   ClassifyId   query   string  true       "分类Id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /usda_fas/frequency [get]
+func (this *BaseFromUsdaFasController) GetFrequency() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+
+	frequencyList, err := data_manage.GetUsdaFasFrequencyByClassifyId(classifyId)
+	if err != nil {
+		br.Msg = "获取频度失败"
+		br.ErrMsg = "获取频度失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = frequencyList
+}
+
+// BatchAddEdbCheck
+// @Title 新增校验
+// @Description 新增校验
+// @Param	request	body data_manage.BatchManualEdbReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /usda_fas/batch/add/check [post]
+func (c *BaseFromUsdaFasController) 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
+	}
+
+	// 最大批量添加的数量
+	codeMaxT := 31
+
+	var req data_manage.BatchCheckUsdaFasEdbReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,Err:" + err.Error()
+		return
+	}
+	req.Keyword = strings.TrimSpace(req.Keyword)
+	classifyIdStr := req.ClassifyIds
+
+	var list = make([]*data_manage.BaseFromUsdaFasIndexList, 0)
+	var condition string
+	var pars []interface{}
+
+	if req.ListAll {
+		classifyIds := strings.Split(classifyIdStr, ",")
+		if len(classifyIds) > 0 && classifyIds[0] != `` {
+			condition += " AND classify_id IN (" + utils.GetOrmInReplace(len(classifyIds)) + " ) "
+			pars = append(pars, classifyIds)
+		}
+		keyword := req.Keyword
+		if keyword != `` {
+			condition += " AND (index_name like ? OR index_code like ?) "
+			pars = utils.GetLikeKeywordPars(pars, keyword, 2)
+		}
+		frequencies := req.Frequencies
+		if frequencies != "" {
+			frequencyList := strings.Split(frequencies, ",")
+			condition += " AND frequency IN (" + utils.GetOrmInReplace(len(frequencyList)) + " ) "
+			pars = append(pars, frequencyList)
+		}
+		codes := req.TradeCodeList
+		codeList := make([]string, 0)
+		if codes != "" {
+			codeList = strings.Split(codes, ",")
+		}
+		if len(codeList) > 0 {
+			condition += ` AND index_code not in (` + utils.GetOrmInReplace(len(codeList)) + `) `
+			pars = append(pars, codeList)
+		}
+	} else {
+		codes := req.TradeCodeList
+		codeList := make([]string, 0)
+		if codes != "" {
+			codeList = strings.Split(codes, ",")
+		}
+		if len(codeList) <= 0 {
+			br.Msg = "请选择指标"
+			br.ErrMsg = "请选择指标"
+			return
+		}
+		// 指标
+		condition += ` AND index_code in (` + utils.GetOrmInReplace(len(codeList)) + `) `
+		pars = append(pars, codeList)
+
+	}
+	condition += ` AND index_code not in (SELECT edb_code FROM edb_info WHERE source=?) `
+	pars = append(pars, utils.DATA_SOURCE_USDA_FAS)
+
+	list, err = data_manage.GetUsdaFasIndexPage(condition, pars, 0, codeMaxT)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	if len(list) >= codeMaxT {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+
+	br.Data = list
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 42 - 0
controllers/data_stat/edb_terminal.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/services/data_stat"
 	"eta/eta_api/utils"
+	"fmt"
 )
 
 // EdbTerminalController 数据源终端管理
@@ -198,3 +199,44 @@ func (this *EdbTerminalController) TerminalCodeList() {
 	br.Msg = "获取成功"
 	br.Data = resp
 }
+
+// TerminalIndexDirInfo
+// @Title 获取指标终端文件夹信息
+// @Description 获取指标终端文件夹信息
+// @Success 200 {object} data_manage.EdbTerminalDirInfo
+// @router /terminal/index_dir [get]
+func (this *EdbTerminalController) TerminalIndexDirInfo() {
+	br := new(models.BaseResponse).Init()
+	br.IsSendEmail = false
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	source, _ := this.GetInt("Source")
+	indexId, _ := this.GetInt("IndexId")
+	if source <= 0 || indexId <= 0 {
+		br.Msg = "请选择数据源和指标ID"
+		return
+	}
+	info := new(data_manage.EdbTerminalDirInfo)
+	var err error
+	info, err = data_stat.GetEdbTerminalDirInfo(indexId, source)
+	if err != nil {
+		utils.FileLog.Info(fmt.Sprintf("获取终端文件夹信息失败indexId:%d,source:%d,Err:%s", indexId, source, err.Error()))
+		//br.Msg = "获取终端文件夹信息失败"
+		//br.ErrMsg = "获取终端文件夹信息失败 ErrMsg:" + err.Error()
+		//return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = info
+}

+ 398 - 0
controllers/edb_monitor/edb_monitor.go

@@ -0,0 +1,398 @@
+package edb_monitor
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/edb_monitor/request"
+	edbmonitor "eta/eta_api/services/edb_monitor"
+	"strings"
+)
+
+type EdbMonitorController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 预警管理列表
+// @Description 预警管理列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   string  true       "分类ID,多选用逗号分隔"
+// @Param   Level   query   string  true       "预警等级,多选用逗号分隔"
+// @Param   State   query   string  true       "预警状态,多选用逗号分隔"
+// @Param   UserId   query   string  true       "创建人ID,多选用逗号分隔"
+// @Success 200 {object} response.EdbMonitorInfoListResp
+// @router /list [get]
+func (m *EdbMonitorController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := m.GetInt("PageSize")
+	currentIndex, _ := m.GetInt("CurrentIndex")
+	classifyId := m.GetString("ClassifyId")
+	level := m.GetString("Level")
+	state := m.GetString("State")
+	userId := m.GetString("UserId")
+
+	resp, msg, err := edbmonitor.GetMonitorList(classifyId, level, state, userId, pageSize, currentIndex)
+	if err != nil {
+		if msg == "" {
+			msg = "获取列表失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Data = resp
+	br.Msg = "获取列表成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Add
+// @Title 预警管理添加
+// @Description 预警管理添加
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /add [post]
+func (m *EdbMonitorController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req request.EdbMonitorInfoSaveReq
+	if err := json.Unmarshal(m.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	switch req.MonitorType {
+	case edbmonitor.EDB_MONITOR_TYPE_DOWN, edbmonitor.EDB_MONITOR_TYPE_UP:
+	default:
+		br.Msg = "请选择预警等级"
+		return
+	}
+	req.MonitorData = strings.TrimSpace(req.MonitorData)
+	if req.MonitorData == "" {
+		br.Msg = "请输入预警值"
+		return
+	}
+	req.EdbMonitorName = strings.TrimSpace(req.EdbMonitorName)
+	if req.EdbMonitorName == "" {
+		br.Msg = "请输入预警名称"
+		return
+	}
+
+	msg, err := edbmonitor.SaveEdbMonitorInfo(req, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "添加失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "添加成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// edit
+// @Title 预警管理编辑
+// @Description 预警管理编辑
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /edit [post]
+func (m *EdbMonitorController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req request.EdbMonitorInfoSaveReq
+	if err := json.Unmarshal(m.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+	if req.EdbInfoId <= 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if req.EdbMonitorId <= 0 {
+		br.Msg = "请选择预警"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	switch req.MonitorType {
+	case edbmonitor.EDB_MONITOR_TYPE_DOWN, edbmonitor.EDB_MONITOR_TYPE_UP:
+	default:
+		br.Msg = "请选择预警等级"
+		return
+	}
+	req.MonitorData = strings.TrimSpace(req.MonitorData)
+	if req.MonitorData == "" {
+		br.Msg = "请输入预警值"
+		return
+	}
+	req.EdbMonitorName = strings.TrimSpace(req.EdbMonitorName)
+	if req.EdbMonitorName == "" {
+		br.Msg = "请输入预警名称"
+		return
+	}
+
+	msg, err := edbmonitor.SaveEdbMonitorInfo(req, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "编辑失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "编辑成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// delete
+// @Title 预警管理编辑
+// @Description 预警管理编辑
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /delete [post]
+func (m *EdbMonitorController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req request.EdbMonitorInfoDeleteReq
+	if err := json.Unmarshal(m.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+	if req.EdbMonitorId <= 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+
+	msg, err := edbmonitor.DeleteEdbMonitorInfo(req, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "删除失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "删除成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Close
+// @Title 预警管理关闭
+// @Description 预警管理关闭
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /close [post]
+func (m *EdbMonitorController) Close() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req request.EdbMonitorInfoCloseReq
+	if err := json.Unmarshal(m.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+	if req.EdbMonitorId <= 0 {
+		br.Msg = "请选择预警"
+		return
+	}
+
+	msg, err := edbmonitor.CloseEdbMonitorInfo(req, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "关闭失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "关闭成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Restart
+// @Title 预警管理重启
+// @Description 预警管理重启
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /restart [post]
+func (m *EdbMonitorController) Restart() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req request.EdbMonitorInfoRestartReq
+	if err := json.Unmarshal(m.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+	if req.EdbMonitorId <= 0 {
+		br.Msg = "请选择预警"
+		return
+	}
+
+	msg, err := edbmonitor.RestartEdbMonitorInfo(req, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "重启失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "重启成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// GetMonitorLevel
+// @Title 预警管理等级列表
+// @Description 预警管理等级列表
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /monitor_level/list [get]
+func (m *EdbMonitorController) GetMonitorLevel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	list, msg, err := edbmonitor.GetEdbMonitorLevelList()
+	if err != nil {
+		if msg == "" {
+			msg = "获取预警等级失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Data = list
+	br.Msg = "获取成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// GetMonitorUser
+// @Title 预警管理用户列表
+// @Description 预警管理用户列表
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /monitor_user/list [get]
+func (m *EdbMonitorController) GetMonitorUser() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	resp, msg, err := edbmonitor.GetEdbMonitorInfoUserList()
+	if err != nil {
+		if msg == "" {
+			msg = "获取用户列表失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Data = resp
+	br.Msg = "获取成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 240 - 0
controllers/edb_monitor/edb_monitor_classify.go

@@ -0,0 +1,240 @@
+package edb_monitor
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/edb_monitor/request"
+	edbmonitor "eta/eta_api/services/edb_monitor"
+	"strings"
+)
+
+type EdbMonitorClassifyController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 预警管理分类列表
+// @Description 预警管理分类列表
+// @Success 200 {object} response.EdbMonitorClassifyTree
+// @router /classify/list [get]
+func (c *EdbMonitorClassifyController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	resp, msg, err := edbmonitor.GetEdbMonitorClassifyTree()
+	if err != nil {
+		if msg == "" {
+			msg = "获取分类列表失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Data = resp
+	br.Msg = "获取成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Add
+// @Title 预警管理分类添加
+// @Description 预警管理分类添加
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /classify/add [post]
+func (c *EdbMonitorClassifyController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.EdbMonitorClassifySaveReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "分类名称不能为空"
+		return
+	}
+	if req.Level <= 0 || req.ClassifyId > 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	msg, err := edbmonitor.SaveEdbMonitorClassify(req)
+	if err != nil {
+		if msg == "" {
+			msg = "分类添加失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "分类添加成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Edit
+// @Title 预警管理分类编辑
+// @Description 预警管理分类编辑
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @router /classify/edit [post]
+func (c *EdbMonitorClassifyController) Edit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.EdbMonitorClassifySaveReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "分类不存在,请刷新页面"
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "分类名称不能为空"
+		return
+	}
+	if req.Level <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	msg, err := edbmonitor.SaveEdbMonitorClassify(req)
+	if err != nil {
+		if msg == "" {
+			msg = "分类添加失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "分类编辑成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Delete
+// @Title 预警管理分类删除
+// @Description 预警管理分类删除
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @router /classify/delete [post]
+func (c *EdbMonitorClassifyController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req request.EdbMonitorClassifyDeleteReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "分类不存在,请刷新页面"
+		return
+	}
+
+	msg, err := edbmonitor.DeleteEdbMonitorClassify(req)
+	if err != nil {
+		if msg == "" {
+			msg = "分类删除失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "分类删除成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Move
+// @Title 预警分类移动接口
+// @Description 预警分类移动接口
+// @Param   request body request.MoveEdbMonitorClassifyReq  true  "每页数据条数"
+// @router /classify/move [post]
+func (c *EdbMonitorClassifyController) Move() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	var req request.MoveEdbMonitorClassifyReq
+	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id小于等于0"
+		return
+	}
+	msg, err := edbmonitor.MoveEdbMonitorClassify(req)
+	if err != nil {
+		if msg == "" {
+			msg = "分类移动失败"
+		}
+		br.Msg = msg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Msg = "分类移动成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 251 - 0
controllers/edb_monitor/edb_monitor_message.go

@@ -0,0 +1,251 @@
+package edb_monitor
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/edb_monitor/request"
+	edbmonitor "eta/eta_api/services/edb_monitor"
+	"eta/eta_api/utils"
+	"net/http"
+	"strconv"
+	"time"
+
+	"github.com/gorilla/websocket"
+)
+
+type EdbMonitorMessageController struct {
+	controllers.BaseAuthController
+}
+
+var upgrader = websocket.Upgrader{
+	ReadBufferSize:  1024,
+	WriteBufferSize: 1024,
+	CheckOrigin: func(r *http.Request) bool {
+		return true
+	},
+}
+
+// GetMonitorLevel
+// @Title 预警管理消息
+// @Description 预警管理消息
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /message/connect [get]
+func (m *EdbMonitorMessageController) Connect() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var conn *websocket.Conn
+	connKey := edbmonitor.EDB_MONITOR_MESSAGE_CONNECT_CACHE + strconv.Itoa(sysUser.AdminId)
+	ok := utils.Rc.IsExist(connKey)
+	if !ok {
+		conn = edbmonitor.MonitorMessageConn[sysUser.AdminId]
+		if conn != nil {
+			conn.Close()
+		}
+	}
+	err := utils.Rc.Put(connKey, "1", time.Minute*1)
+	if err != nil {
+		br.Msg = "系统错误"
+		br.ErrMsg = "连接失败,err:" + err.Error()
+		return
+	}
+	conn, err = upgrader.Upgrade(m.Ctx.ResponseWriter, m.Ctx.Request, nil)
+	if err != nil {
+		br.Msg = "连接失败"
+		br.ErrMsg = "连接失败,err:" + err.Error()
+		return
+	}
+	defer conn.Close()
+
+	edbmonitor.MonitorMessageConn[sysUser.AdminId] = conn
+	conn.SetCloseHandler(func(code int, text string) error {
+		delete(edbmonitor.MonitorMessageConn, sysUser.AdminId)
+		utils.Rc.Delete(connKey)
+		return nil
+	})
+
+	go func() {
+		// 心跳检测
+		for {
+			isClose, err := edbmonitor.EdbMonitorMessageHealth(sysUser.AdminId)
+			if err != nil {
+				utils.FileLog.Error("指标预警信息健康检查失败,err:%s, adminId:%d", err.Error(), sysUser.AdminId)
+				return
+			}
+			if isClose {
+				return
+			}
+		}
+	}()
+
+	messageList, err := edbmonitor.GetHistoryMessages(sysUser.AdminId)
+	if err != nil {
+		utils.FileLog.Error("获取指标预警信息历史失败,err:%s, adminId:%d", err.Error(), sysUser.AdminId)
+	}
+	success := make(chan int, 10)
+	go func() {
+		defer close(success)
+		for i, msg := range messageList {
+			if i == 0 {
+				// 多条消息仅发送最新一条
+				err = edbmonitor.SendMessages(sysUser.AdminId, msg.EdbInfoId, msg.EdbInfoType, msg.EdbClassifyId, msg.EdbUniqueCode, msg.Message, msg.TriggerTime)
+				if err != nil {
+					utils.FileLog.Error("指标预警信息发送失败,err:%s, adminId:%d", err.Error(), sysUser.AdminId)
+				} else {
+					success <- msg.EdbMonitorMessageId
+				}
+			} else {
+				success <- msg.EdbMonitorMessageId
+			}
+		}
+	}()
+	go func() {
+		readList := make([]int, 0)
+		for {
+			msgId, ok := <-success
+			if !ok {
+				break
+			}
+			readList = append(readList, msgId)
+		}
+		_, err = edbmonitor.ReadEdbMonitorMessageList(readList, sysUser.AdminId)
+		if err != nil {
+			utils.FileLog.Error("指标预警信息已读失败,err:%s, adminId:%d", err.Error(), sysUser.AdminId)
+		}
+	}()
+
+	for {
+		ok = utils.Rc.IsExist(connKey)
+		if !ok {
+			br.Msg = "连接已断开"
+			br.Ret = 200
+			br.Success = true
+			return
+		}
+		time.Sleep(10 * time.Second)
+	}
+}
+
+// Close
+// @Title 预警管理消息
+// @Description 预警管理消息
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /message/close [post]
+func (m *EdbMonitorMessageController) Close() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	conn := edbmonitor.MonitorMessageConn[sysUser.AdminId]
+	if conn != nil {
+		conn.Close()
+	}
+
+	br.Msg = "关闭成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// List
+// @Title 预警管理消息列表
+// @Description 预警管理消息列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Success 200 {object} response.EdbMonitorClassifyTree
+// @router /message/list [get]
+func (c *EdbMonitorMessageController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	resp, err := edbmonitor.GetMessageList(sysUser.AdminId, currentIndex, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Data = resp
+	br.Msg = "获取成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// Read
+// @Title 预警管理消息已读
+// @Description 预警管理消息已读
+// @Param   request body request.EdbMonitorSaveRequest  true  "每页数据条数"
+// @Success 200 {object} models.EnglishReportEmailPageListResp
+// @router /message/read [post]
+func (m *EdbMonitorMessageController) Read() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		m.Data["json"] = br
+		m.ServeJSON()
+	}()
+
+	sysUser := m.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req request.EdbMonitorMessageReadReq
+	if err := json.Unmarshal(m.Ctx.Input.RequestBody, &req); err != nil {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,err:" + err.Error()
+		return
+	}
+	if req.EdbMonitorMessageId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	msg, err := edbmonitor.ReadEdbMonitorMessage(req.EdbMonitorMessageId, sysUser.AdminId)
+	if err != nil {
+		if msg == "" {
+			msg = "系统错误"
+		}
+		br.Msg = msg
+		br.ErrMsg = "读取消息失败,err:" + err.Error()
+		return
+	}
+
+	br.Msg = "已读成功"
+	br.Ret = 200
+	br.Success = true
+}

+ 22 - 13
controllers/english_report/english_classify.go

@@ -79,6 +79,7 @@ func (this *EnglishReportController) ListClassify() {
 	// 三级分类-品种权限
 	permissionMap := make(map[int][]int)
 
+	var secondClassify []*models.EnglishClassifyList
 	if len(idList) > 0 {
 		childIdMap := make(map[int]struct{}, 0)
 		for _, v := range idList {
@@ -97,6 +98,7 @@ func (this *EnglishReportController) ListClassify() {
 			br.ErrMsg = "获取二级分类失败,Err:" + err.Error()
 			return
 		}
+		secondClassify = tmpList
 		for _, v := range tmpList {
 			if _, ok := rootMap[v.ParentId]; !ok {
 				thirdIds = append(thirdIds, v.Id)
@@ -145,6 +147,13 @@ func (this *EnglishReportController) ListClassify() {
 			}
 		}
 	}
+	// 标记可删除的英文分类
+	err = services.MarkEnableDeleteEnlishClassify(rootList, secondClassify)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "标记可删除分类失败,Err:" + err.Error()
+		return
+	}
 
 	//处理一级分类
 	var sortList models.RSClassifyList
@@ -483,18 +492,18 @@ func (this *EnglishReportController) DelClassify() {
 		return
 	}
 
-	count, err := models.GetEnglishClassifyChildCounts(classifyId)
-	if err != nil && err.Error() != utils.ErrNoRow() {
-		br.Msg = "获取信息失败"
-		br.ErrMsg = "获取失败,Err:" + err.Error()
-		return
-	}
+	// count, err := models.GetEnglishClassifyChildCounts(classifyId)
+	// if err != nil && err.Error() != utils.ErrNoRow() {
+	// 	br.Msg = "获取信息失败"
+	// 	br.ErrMsg = "获取失败,Err:" + err.Error()
+	// 	return
+	// }
 
-	if count > 0 {
-		br.Msg = "请先删除该分类下关联分类"
-		br.Ret = 403
-		return
-	}
+	// if count > 0 {
+	// 	br.Msg = "请先删除该分类下关联分类"
+	// 	br.Ret = 403
+	// 	return
+	// }
 	reportCount, e := models.GetEnglishReportCounts(classifyId, classifyInfo.ParentId)
 	if e != nil && e.Error() != utils.ErrNoRow() {
 		br.Msg = "获取信息失败"
@@ -536,8 +545,8 @@ func (this *EnglishReportController) DelClassify() {
 		br.Ret = 403
 		return
 	}
-
-	if err = models.DeleteEnglishClassify(classifyId); err != nil {
+	err = classifyInfo.Delete()
+	if err != nil {
 		br.Msg = "删除失败"
 		br.ErrMsg = "删除报告失败, Err: " + err.Error()
 		return

+ 36 - 5
controllers/english_report/report.go

@@ -12,11 +12,12 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"html"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // EnglishReportController 研报活动模块
@@ -90,9 +91,19 @@ func (this *EnglishReportController) Add() {
 		br.ErrMsg = "期数获取失败,Err:" + err.Error()
 		return
 	}
+	reportClassify, e := models.GetEnglishReportClassifyById(req.ClassifyIdFirst)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告所在分类异常"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取分类失败, Err: " + e.Error()
+		return
+	}
 
 	// 根据审批开关及审批流判断当前报告状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird, models.ReportOperateAdd)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportClassify.RootId, req.ClassifyIdFirst, req.ClassifyIdSecond, models.ReportOperateAdd)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告当前状态失败, Err: " + e.Error()
@@ -1299,9 +1310,19 @@ func (this *EnglishReportController) SubmitApprove() {
 		br.ErrMsg = "获取报告失败, Err: " + e.Error()
 		return
 	}
+	reportClassify, e := models.GetEnglishReportClassifyById(reportItem.ClassifyIdFirst)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告所在分类异常"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取分类失败, Err: " + e.Error()
+		return
+	}
 
 	// 校验当前审批配置, 返回下一个状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, models.ReportOperateSubmitApprove)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportClassify.RootId, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateSubmitApprove)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()
@@ -1324,7 +1345,7 @@ func (this *EnglishReportController) SubmitApprove() {
 	}
 
 	// 提交审批
-	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.Title, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, sysUser.AdminId, sysUser.RealName)
+	approveId, e := services.SubmitReportApprove(report_approve.FlowReportTypeEnglish, reportItem.Id, reportItem.Title, reportClassify.RootId, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, sysUser.AdminId, sysUser.RealName)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "提交审批失败, Err: " + e.Error()
@@ -1390,9 +1411,19 @@ func (this *EnglishReportController) CancelApprove() {
 		br.ErrMsg = "获取报告失败, Err: " + e.Error()
 		return
 	}
+	reportClassify, e := models.GetEnglishReportClassifyById(reportItem.ClassifyIdFirst)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "报告所在分类异常"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取分类失败, Err: " + e.Error()
+		return
+	}
 
 	// 校验当前审批配置, 返回下一个状态
-	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, 0, models.ReportOperateCancelApprove)
+	state, e := services.CheckReportCurrState(report_approve.FlowReportTypeEnglish, reportClassify.RootId, reportItem.ClassifyIdFirst, reportItem.ClassifyIdSecond, models.ReportOperateCancelApprove)
 	if e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = "校验报告状态失败, Err: " + e.Error()

+ 19 - 2
controllers/message.go

@@ -3,6 +3,7 @@ package controllers
 import (
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage/data_manage_permission"
+	edbmonitor "eta/eta_api/models/edb_monitor"
 	"eta/eta_api/models/report_approve"
 	"fmt"
 )
@@ -35,7 +36,7 @@ func (c *MessageController) UnReadMessageNum() {
 		return
 	}
 
-	var unReadReportNum, unReadDataPermissionNum int
+	var unReadReportNum, unReadDataPermissionNum, unReadEdbMonitorNum int
 
 	// 获取报告审批消息
 	{
@@ -75,9 +76,25 @@ func (c *MessageController) UnReadMessageNum() {
 		}
 		unReadDataPermissionNum = unreadTotal
 	}
+	// 获取预警消息
+	{
+		pars := make([]interface{}, 0)
+		cond := ` AND admin_id = ? AND is_read = ?`
+		pars = append(pars, sysUser.AdminId, 0)
+
+		// 未读消息数
+		messageOb := new(edbmonitor.EdbMonitorMessage)
+		unreadTotal, e := messageOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取资产消息列表总数失败, Err: " + e.Error()
+			return
+		}
+		unReadEdbMonitorNum = unreadTotal
+	}
 
 	// 汇总数
-	num := unReadReportNum + unReadDataPermissionNum
+	num := unReadReportNum + unReadDataPermissionNum + unReadEdbMonitorNum
 
 	br.Data = num
 	br.Ret = 200

+ 94 - 0
controllers/report_chapter.go

@@ -372,6 +372,13 @@ func (this *ReportController) EditDayWeekChapter() {
 		br.IsSendEmail = false
 		return
 	}
+	if reportChapterInfo.PublishState == 2 {
+		br.Msg = "该报告章节已发布,不允许编辑"
+		br.ErrMsg = "该报告章节已发布,不允许编辑"
+		br.IsSendEmail = false
+		return
+	}
+
 	// 报告的最后编辑人
 	reportInfo.LastModifyAdminId = sysUser.AdminId
 	reportInfo.LastModifyAdminName = sysUser.RealName
@@ -1632,3 +1639,90 @@ func (this *ReportController) EditChapterTitle() {
 	br.Success = true
 	br.Msg = "保存成功"
 }
+
+// CancelPublishReportChapter
+// @Title 取消发布章节
+// @Description 取消发布章节
+// @Param	request	body models.PublishReportChapterReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /chapter/publish/cancel [post]
+func (this *ReportController) CancelPublishReportChapter() {
+	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 models.PublishReportChapterReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ReportChapterId <= 0 {
+		br.Msg = "参数有误"
+		return
+	}
+
+	// 获取章节详情
+	chapterInfo, err := models.GetReportChapterInfoById(req.ReportChapterId)
+	if err != nil {
+		br.Msg = "章节信息有误"
+		br.ErrMsg = "获取章节信息失败, Err: " + err.Error()
+		return
+	}
+	// 章节发布状态校验
+	if chapterInfo.PublishState == 1 {
+		br.Msg = "该章节未发布,不能重复撤销"
+		br.ErrMsg = "该章节未发布,不能重复撤销"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 获取报告详情
+	reportInfo, err := models.GetReportByReportId(chapterInfo.ReportId)
+	if err != nil {
+		br.Msg = "查询报告有误"
+		br.ErrMsg = "查询报告信息失败, Err: " + err.Error()
+		return
+	}
+	if reportInfo.State == 2 {
+		br.Msg = "该报告已发布,不允许撤销章节"
+		br.ErrMsg = "该报告已发布,不允许撤销章节"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 更新章节信息
+	chapterInfo.PublishState = 1
+	chapterInfo.PublishTime = time.Time{}
+	chapterInfo.LastModifyAdminId = this.SysUser.AdminId
+	chapterInfo.LastModifyAdminName = this.SysUser.RealName
+	chapterInfo.ModifyTime = time.Now()
+
+	updateCols := make([]string, 0)
+	updateCols = append(updateCols, "PublishState", "PublishTime", "LastModifyAdminId", "LastModifyAdminName", "ModifyTime")
+	err = chapterInfo.UpdateChapter(updateCols)
+	if err != nil {
+		br.Msg = "发布失败"
+		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+		return
+	}
+
+	// 更新章节ES
+	{
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "撤销成功"
+}

+ 1 - 0
controllers/report_chapter_type.go

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

+ 15 - 1
controllers/report_v2.go

@@ -481,7 +481,12 @@ func (this *ReportController) Add() {
 	item.IsPublicPublish = req.IsPublicPublish
 	item.ReportCreateTime = time.Now()
 
-	err, errMsg := services.AddReportAndChapter(item, req.InheritReportId, req.GrantAdminIdList)
+	reportDate := time.Now()
+	t, _ := time.ParseInLocation(utils.FormatDate, req.CreateTime, time.Local)
+	if !t.IsZero() {
+		reportDate = t
+	}
+	err, errMsg := services.AddReportAndChapter(item, req.InheritReportId, req.GrantAdminIdList, reportDate)
 	if err != nil {
 		br.Msg = "保存失败"
 		if errMsg != "" {
@@ -1316,7 +1321,11 @@ func (this *ReportController) PublishCancelReport() {
 	}
 	go func() {
 		_, _ = models.AddReportStateRecord(recordItem)
+
+		// 重置小程序详情页海报
+		_ = services.ResetMiniProgramReportDetailCover(reportInfo.Id)
 	}()
+
 	br.Ret = 200
 	br.Success = true
 }
@@ -1645,6 +1654,11 @@ func (this *ReportController) CancelApprove() {
 	//	return
 	//}
 
+	// 重置小程序详情页海报
+	go func() {
+		_ = services.ResetMiniProgramReportDetailCover(reportItem.Id)
+	}()
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 284 - 0
controllers/residual_analysis/residual_analysis_controller.go

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

+ 10 - 1
controllers/sandbox/sandbox.go

@@ -12,8 +12,9 @@ import (
 	sandboxService "eta/eta_api/services/sandbox"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // versionSize 版本列表第一页数据约定是:3条
@@ -847,6 +848,14 @@ func (this *SandboxController) SandboxClassifyItems() {
 		//allNodes := sandboxService.HandleNoPermissionSandbox(resp.AllNodes, nil)
 		//resp.AllNodes = allNodes
 
+		nodeAll, err := sandboxService.GetSandboxClassifyByIsShowMe(resp.AllNodes, sandboxClassifyId, this.SysUser.AdminId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		resp.AllNodes = nodeAll
+
 		br.Ret = 200
 		br.Success = true
 		br.Msg = "获取成功"

+ 18 - 1
controllers/sys_admin.go

@@ -120,6 +120,7 @@ func (this *SysAdminController) ListSysuser() {
 	}
 
 	var list []*system.AdminItem
+	var roleIdList []int
 	adminIdArr := make([]int, 0)
 	if teamId <= 0 {
 		//if groupId > 0 {
@@ -151,6 +152,7 @@ func (this *SysAdminController) ListSysuser() {
 			item := list[i]
 			//adminIdArr = append(adminIdArr, strconv.Itoa(item.AdminId))
 			adminIdArr = append(adminIdArr, item.AdminId)
+			roleIdList = append(roleIdList, item.RoleId)
 			var secondName *string
 			if item.GroupId != 0 {
 				pid, err := company.GetParentIdFromGroup(item.GroupId)
@@ -225,7 +227,7 @@ func (this *SysAdminController) ListSysuser() {
 			item := list[i]
 			//adminIdArr = append(adminIdArr, strconv.Itoa(item.AdminId))
 			adminIdArr = append(adminIdArr, item.AdminId)
-
+			roleIdList = append(roleIdList, item.RoleId)
 			if item.DepartmentName != "" {
 				if item.GroupName == "" {
 					list[i].DepartmentGroup = item.DepartmentName
@@ -289,6 +291,21 @@ func (this *SysAdminController) ListSysuser() {
 		}
 	}
 
+	roles, err := system.GetSysRoleByIdList(roleIdList)
+	if err != nil {
+		br.ErrMsg = "获取角色信息失败,Err:" + err.Error()
+	} else {
+		roleMap := make(map[int]string)
+		for _, v := range roles {
+			roleMap[v.RoleId] = v.RoleName
+		}
+		for _, v := range list {
+			if mv, ok := roleMap[v.RoleId]; ok {
+				v.RoleName = mv
+			}
+		}
+	}
+
 	fmt.Println("teamId:", teamId)
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := new(system.SysuserListResp)

+ 26 - 18
controllers/target.go

@@ -767,34 +767,42 @@ func (this *TargetController) ClassifyList() {
 		return
 	}
 
-	classifyIdStrList := make([]string, 0)
+	classifyIdList := make([]int, 0)
 	for _, classifyList := range list {
 		if classifyList.Child != nil {
 			for _, classify := range classifyList.Child {
-				classifyIdStrList = append(classifyIdStrList, strconv.Itoa(classify.ClassifyId))
+				classifyIdList = append(classifyIdList, classify.ClassifyId)
 			}
 		}
 	}
-	if len(classifyIdStrList) > 0 {
-		edbInfoGroupCountList, err := models.GetEdbInfoGroupCountByClassifyIds(strings.Join(classifyIdStrList, ","))
-		if err != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取分类下有数据的指标数量失败,Err:" + err.Error()
-			return
-		}
-
-		edbInfoGroupCountMap := make(map[int]int)
-		for _, edbInfoGroupCount := range edbInfoGroupCountList {
-			edbInfoGroupCountMap[edbInfoGroupCount.ClassifyId] = edbInfoGroupCount.Count
-		}
+	if len(classifyIdList) > 0 {
+		//edbInfoGroupCountList, err := models.GetEdbInfoGroupCountByClassifyIds(classifyIdList)
+		//if err != nil {
+		//	br.Msg = "获取失败"
+		//	br.ErrMsg = "获取分类下有数据的指标数量失败,Err:" + err.Error()
+		//	return
+		//}
+		//
+		//edbInfoGroupCountMap := make(map[int]int)
+		//for _, edbInfoGroupCount := range edbInfoGroupCountList {
+		//	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))
+		//			}
+		//		}
+		//	}
+		//}
 		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))
-					}
+					classify.UniqueCode = utils.MD5(fmt.Sprint(classify.ClassifyId))
 				}
 			}
 		}

+ 20 - 3
go.mod

@@ -20,10 +20,12 @@ require (
 	github.com/beevik/etree v1.3.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-ldap/ldap v3.0.3+incompatible
+	github.com/go-mysql-org/go-mysql v1.9.1
 	github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc
-	github.com/go-sql-driver/mysql v1.7.0
+	github.com/go-sql-driver/mysql v1.7.1
 	github.com/go-xorm/xorm v0.7.9
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
+	github.com/google/uuid v1.6.0
 	github.com/gorilla/websocket v1.5.1
 	github.com/h2non/filetype v1.1.3
 	github.com/jung-kurt/gofpdf v1.16.2
@@ -51,6 +53,8 @@ require (
 )
 
 require (
+	github.com/BurntSushi/toml v1.3.2 // indirect
+	github.com/Masterminds/semver v1.5.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
@@ -69,6 +73,7 @@ require (
 	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/clbanning/mxj/v2 v2.5.5 // indirect
+	github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
 	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
@@ -76,6 +81,7 @@ require (
 	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.2 // 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
@@ -85,7 +91,6 @@ require (
 	github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
 	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/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
@@ -94,7 +99,7 @@ require (
 	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/compress v1.17.8 // indirect
 	github.com/klauspost/cpuid/v2 v2.2.6 // indirect
 	github.com/leodido/go-urn v1.2.0 // indirect
 	github.com/magiconair/properties v1.8.1 // indirect
@@ -110,16 +115,23 @@ require (
 	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
 	github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
 	github.com/pelletier/go-toml v1.9.2 // indirect
+	github.com/pingcap/errors v0.11.5-0.20221009092201-b66cddb77c32 // indirect
+	github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c // indirect
+	github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22 // indirect
+	github.com/pingcap/tidb/pkg/parser v0.0.0-20231103042308-035ad5ccbe67 // 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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
 	github.com/richardlehane/mscfb v1.0.4 // indirect
 	github.com/richardlehane/msoleps v1.0.3 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/rs/xid v1.5.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
+	github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
+	github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 // 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
@@ -136,7 +148,11 @@ require (
 	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
+	go.uber.org/atomic v1.11.0 // indirect
+	go.uber.org/multierr v1.11.0 // indirect
+	go.uber.org/zap v1.26.0 // indirect
 	golang.org/x/crypto v0.19.0 // indirect
+	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // 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
@@ -146,6 +162,7 @@ require (
 	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/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	xorm.io/builder v0.3.6 // indirect

+ 56 - 6
go.sum

@@ -16,9 +16,13 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 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/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
 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=
@@ -115,6 +119,7 @@ github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkY
 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/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 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=
@@ -149,6 +154,8 @@ github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:sr
 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/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
+github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
 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=
@@ -191,6 +198,8 @@ github.com/go-ldap/ldap v3.0.3+incompatible h1:HTeSZO8hWMS1Rgb2Ziku6b8a7qRIZZMHj
 github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-mysql-org/go-mysql v1.9.1 h1:W2ZKkHkoM4mmkasJCoSYfaE4RQNxXTb6VqiaMpKFrJc=
+github.com/go-mysql-org/go-mysql v1.9.1/go.mod h1:+SgFgTlqjqOQoMc98n9oyUWEgn2KkOL1VmXDoq2ONOs=
 github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
@@ -206,14 +215,16 @@ github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25m
 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.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
+github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 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.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
 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=
@@ -352,8 +363,8 @@ github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53/go.mod h1
 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.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
+github.com/klauspost/compress v1.17.8/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=
@@ -458,6 +469,16 @@ github.com/pelletier/go-toml v1.9.2/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
 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=
+github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
+github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
+github.com/pingcap/errors v0.11.5-0.20221009092201-b66cddb77c32 h1:m5ZsBa5o/0CkzZXfXLaThzKuR85SnHHetqBCpzQ30h8=
+github.com/pingcap/errors v0.11.5-0.20221009092201-b66cddb77c32/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
+github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c h1:CgbKAHto5CQgWM9fSBIvaxsJHuGP0uM74HXtv3MyyGQ=
+github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c/go.mod h1:4qGtCB0QK0wBzKtFEGDhxXnSnbQApw1gc9siScUl8ew=
+github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22 h1:2SOzvGvE8beiC1Y4g9Onkvu6UmuBBOeWRGQEjJaT/JY=
+github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4=
+github.com/pingcap/tidb/pkg/parser v0.0.0-20231103042308-035ad5ccbe67 h1:m0RZ583HjzG3NweDi4xAcK54NBBPJh+zXp5Fp60dHtw=
+github.com/pingcap/tidb/pkg/parser v0.0.0-20231103042308-035ad5ccbe67/go.mod h1:yRkiqLFwIqibYg2P7h4bclHjHcJiIFRLKhGRyBcKYus=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -498,6 +519,8 @@ github.com/qiniu/qmgo v1.1.8/go.mod h1:QvZkzWNEv0buWPx0kdZsSs6URhESVubacxFPlITmv
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/rdlucklib/rdluck_tools v1.0.3 h1:iOtK2QPlPQ6CL6c1htCk5VnFCHzyG6DCfJtunrMswK0=
 github.com/rdlucklib/rdluck_tools v1.0.3/go.mod h1:9Onw9o4w19C8KE5lxb8GyxgRBbZweRVkQSc79v38EaA=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
 github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
 github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
@@ -517,6 +540,7 @@ github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfF
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 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/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
 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=
@@ -524,6 +548,10 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
 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/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
+github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM=
+github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
+github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 h1:oI+RNwuC9jF2g2lP0u0cVEEZrc/AYBCuFdvwrLWM/6Q=
+github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4=
 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=
 github.com/silenceper/wechat/v2 v2.1.6 h1:2br2DxNzhksmvIBJ+PfMqjqsvoZmd/5BnMIfjKYUBgc=
@@ -554,8 +582,9 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q
 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.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 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=
@@ -631,10 +660,23 @@ 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.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
+go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
+go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
+go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
+go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
+go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
 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=
@@ -660,6 +702,8 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
 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-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
+golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
 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=
@@ -820,6 +864,8 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
 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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/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=
@@ -892,6 +938,9 @@ 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/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 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=
@@ -905,6 +954,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 356 - 0
models/ai_predict_model/ai_predict_model_classify.go

@@ -0,0 +1,356 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// AiPredictModelClassify 同花顺高频数据-分类
+type AiPredictModelClassify struct {
+	AiPredictModelClassifyId int       `orm:"column(ai_predict_model_classify_id);pk"`
+	UniqueCode               string    `description:"唯一编码"`
+	ClassifyName             string    `description:"分类名称"`
+	ClassifyNameEn           string    `description:"英文分类名称"`
+	ParentId                 int       `description:"父级ID"`
+	Level                    int       `description:"层级"`
+	LevelPath                string    `description:"层级路径"`
+	Sort                     int       `description:"排序"`
+	RootId                   int       `description:"顶级分类ID"`
+	SysUserId                int       `description:"创建人ID"`
+	SysUserRealName          string    `description:"创建人姓名"`
+	CreateTime               time.Time `description:"创建时间"`
+	ModifyTime               time.Time `description:"修改时间"`
+}
+
+func (m *AiPredictModelClassify) TableName() string {
+	return "ai_predict_model_classify"
+}
+
+type AiPredictModelClassifyCols struct {
+	PrimaryId       string
+	UniqueCode      string
+	ClassifyName    string
+	ClassifyNameEn  string
+	ParentId        string
+	Level           string
+	LevelPath       string
+	Sort            string
+	RootId          string
+	SysUserId       string
+	SysUserRealName string
+	CreateTime      string
+	ModifyTime      string
+}
+
+func (m *AiPredictModelClassify) Cols() AiPredictModelClassifyCols {
+	return AiPredictModelClassifyCols{
+		PrimaryId:       "ai_predict_model_classify_id",
+		UniqueCode:      "unique_code",
+		ClassifyName:    "classify_name",
+		ClassifyNameEn:  "classify_name_en",
+		ParentId:        "parent_id",
+		Level:           "level",
+		LevelPath:       "level_path",
+		Sort:            "sort",
+		RootId:          "root_id",
+		SysUserId:       "sys_user_id",
+		SysUserRealName: "sys_user_real_name",
+		CreateTime:      "create_time",
+		ModifyTime:      "modify_time",
+	}
+}
+
+func (m *AiPredictModelClassify) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.AiPredictModelClassifyId = int(id)
+	return
+}
+
+func (m *AiPredictModelClassify) CreateMulti(items []*AiPredictModelClassify) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AiPredictModelClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AiPredictModelClassify) 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.AiPredictModelClassifyId).Exec()
+	return
+}
+
+func (m *AiPredictModelClassify) 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 *AiPredictModelClassify) 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 *AiPredictModelClassify) GetItemById(id int) (item *AiPredictModelClassify, 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 *AiPredictModelClassify) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *AiPredictModelClassify, 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 *AiPredictModelClassify) 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 *AiPredictModelClassify) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AiPredictModelClassify, 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 *AiPredictModelClassify) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AiPredictModelClassify, 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
+}
+
+// AiPredictModelClassifyItem 同花顺高频数据信息
+type AiPredictModelClassifyItem struct {
+	ClassifyId     int                           `description:"分类ID"`
+	ClassifyName   string                        `description:"分类名称"`
+	ClassifyNameEn string                        `description:"英文分类名称"`
+	ParentId       int                           `description:"父级ID"`
+	Level          int                           `description:"层级"`
+	Sort           int                           `description:"排序"`
+	LevelPath      string                        `description:"层级路径"`
+	UniqueCode     string                        `description:"唯一编码"`
+	Children       []*AiPredictModelClassifyItem `description:"子分类"`
+}
+
+func (m *AiPredictModelClassify) Format2Item() (item *AiPredictModelClassifyItem) {
+	item = new(AiPredictModelClassifyItem)
+	item.ClassifyId = m.AiPredictModelClassifyId
+	item.ClassifyName = m.ClassifyName
+	item.ClassifyNameEn = m.ClassifyNameEn
+	item.ParentId = m.ParentId
+	item.Level = m.Level
+	item.Sort = m.Sort
+	item.LevelPath = m.LevelPath
+	item.UniqueCode = m.UniqueCode
+	item.Children = make([]*AiPredictModelClassifyItem, 0)
+	return
+}
+
+type AiPredictModelClassifyAddReq struct {
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级ID, 第一级传0"`
+	Level        int    `description:"层级, 第一级传0, 其余传上一级的层级"`
+}
+
+type AiPredictModelClassifyEditReq struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+}
+
+func (m *AiPredictModelClassify) GetSortMax(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT MAX(%s) FROM %s WHERE %s = ?`, m.Cols().Sort, m.TableName(), m.Cols().ParentId)
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type AiPredictModelClassifyRemoveReq struct {
+	ClassifyId int `description:"分类ID"`
+	IndexId    int `description:"标的ID"`
+}
+
+type AiPredictModelClassifyListResp struct {
+	AllNodes []*AiPredictModelClassifyListItem `description:"分类节点"`
+}
+
+type AiPredictModelClassifyListItem struct {
+	NodeType     int                               `description:"节点类型: 0-分类; 1-指标"`
+	NodeName     string                            `description:"节点名称"`
+	ClassifyId   int                               `description:"分类ID"`
+	ClassifyName string                            `description:"分类名称"`
+	IndexId      int                               `description:"指标ID"`
+	IndexCode    string                            `description:"指标编码"`
+	IndexName    string                            `description:"指标名称"`
+	ParentId     int                               `description:"父级ID"`
+	Level        int                               `description:"层级"`
+	Sort         int                               `description:"排序"`
+	UniqueCode   string                            `description:"唯一编码, 指标为IndexCode"`
+	Children     []*AiPredictModelClassifyListItem `description:"子分类"`
+}
+
+type AiPredictModelClassifyMoveReq struct {
+	ClassifyId       int `description:"分类ID"`
+	ParentClassifyId int `description:"父级分类ID"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类ID"`
+	NextClassifyId   int `description:"下一个兄弟节点分类ID"`
+	ItemId           int `description:"指标ID, 如果指标ID有值,则移动对象为指标,否则认为移动对象为分类"`
+	PrevItemId       int `description:"上一个指标ID"`
+	NextItemId       int `description:"下一个指标ID"`
+}
+
+func GetAiPredictModelClassifyById(classifyId int) (item *AiPredictModelClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM ai_predict_model_classify WHERE ai_predict_model_classify_id = ?`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func GetAiPredictModelClassifyByRootIdLevel(rootId int, orderStr string) (items []*AiPredictModelClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM ai_predict_model_classify WHERE root_id = ? `
+	if orderStr != "" {
+		sql += orderStr
+	} else {
+		sql += ` order by level desc, sort asc, ai_predict_model_classify_id asc`
+	}
+	_, err = o.Raw(sql, rootId).QueryRows(&items)
+	return
+}
+
+// UpdateAiPredictModelClassifySortByParentId 根据父类id更新排序
+func UpdateAiPredictModelClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update ai_predict_model_classify set sort = ` + updateSort + ` WHERE parent_id = ? AND sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( ai_predict_model_classify_id > ` + fmt.Sprint(classifyId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// GetFirstAiPredictModelClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func GetFirstAiPredictModelClassifyByParentId(parentId int) (item *AiPredictModelClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM ai_predict_model_classify WHERE parent_id = ? order by sort asc,ai_predict_model_classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+func UpdateAiPredictModelClassifyChildByParentClassifyId(classifyIds []int, rootId int, levelStep int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	var pars []interface{}
+	pars = append(pars, rootId, levelStep)
+	pars = append(pars, classifyIds)
+	// 更新相关联的二级分类的parentId,和classify_name_second
+	sql := `UPDATE ai_predict_model_classify SET root_id = ?, level = level+? where ai_predict_model_classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	_, err = o.Raw(sql, pars).Exec()
+	if err != nil {
+		return
+	}
+	return
+}
+
+// GetAiPredictModelIndexCountByClassifyId 获取目录下(包含子目录)的指标数量
+func GetAiPredictModelIndexCountByClassifyId(classifyId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM ai_predict_model_index AS a
+				WHERE a.classify_id IN(
+				SELECT t.ai_predict_model_classify_id FROM 
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM ai_predict_model_classify WHERE parent_id IS NOT NULL) rd,
+					 (SELECT @pid := ?) pd 
+				WHERE FIND_IN_SET(parent_id, @pid) > 0 
+				  AND @pid := CONCAT(@pid, ',', ai_predict_model_classify_id) 
+				UNION SELECT * FROM ai_predict_model_classify WHERE ai_predict_model_classify_id = @pid
+				)AS t
+			)`
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+// GetAiPredictModelClassifyCountByClassifyId 获取目录下子目录数量
+func GetAiPredictModelClassifyCountByClassifyId(chartClassifyId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM (
+			SELECT rd.*
+			FROM (SELECT * FROM ai_predict_model_classify WHERE parent_id IS NOT NULL) rd,
+				 (SELECT @pid := ?) pd 
+			WHERE FIND_IN_SET(parent_id, @pid) > 0 
+			  AND @pid := CONCAT(@pid, ',', ai_predict_model_classify_id) 
+			UNION SELECT * FROM ai_predict_model_classify WHERE ai_predict_model_classify_id = @pid
+			)AS t
+			WHERE t.ai_predict_model_classify_id <> ?`
+	err = o.Raw(sql, chartClassifyId, chartClassifyId).QueryRow(&count)
+	return
+}
+
+// RemoveAiPredictModelClassify 删除分类及子分类
+func RemoveAiPredictModelClassify(classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `DELETE FROM ai_predict_model_classify
+				WHERE ai_predict_model_classify_id IN(
+				SELECT t.ai_predict_model_classify_id FROM
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM ai_predict_model_classify WHERE parent_id IS NOT NULL) rd,
+				(SELECT @pid := ?) pd
+				WHERE FIND_IN_SET(parent_id, @pid) > 0
+				AND @pid := CONCAT(@pid, ',', ai_predict_model_classify_id)
+				UNION SELECT * FROM ai_predict_model_classify WHERE ai_predict_model_classify_id = @pid
+				)AS t
+			)`
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}

+ 261 - 0
models/ai_predict_model/ai_predict_model_dashboard.go

@@ -0,0 +1,261 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// AiPredictModelDashboard AI预测模型-BI看板
+type AiPredictModelDashboard struct {
+	AiPredictModelDashboardId int       `orm:"column(ai_predict_model_dashboard_id);pk"`
+	AiPredictModelIndexId     int       `description:"标的ID"`
+	DashboardName             string    `description:"看板名称"`
+	Sort                      int       `description:"排序"`
+	SysUserId                 int       `description:"创建人ID"`
+	SysUserRealName           string    `description:"创建人姓名"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"修改时间"`
+}
+
+func (m *AiPredictModelDashboard) TableName() string {
+	return "ai_predict_model_dashboard"
+}
+
+type AiPredictModelDashboardCols struct {
+	PrimaryId             string
+	AiPredictModelIndexId string
+	DashboardName         string
+	Sort                  string
+	SysUserId             string
+	SysUserRealName       string
+	CreateTime            string
+	ModifyTime            string
+}
+
+func (m *AiPredictModelDashboard) Cols() AiPredictModelDashboardCols {
+	return AiPredictModelDashboardCols{
+		PrimaryId:             "ai_predict_model_dashboard_id",
+		AiPredictModelIndexId: "ai_predict_model_index_id",
+		DashboardName:         "dashboard_name",
+		Sort:                  "sort",
+		SysUserId:             "sys_user_id",
+		SysUserRealName:       "sys_user_real_name",
+		CreateTime:            "create_time",
+		ModifyTime:            "modify_time",
+	}
+}
+
+func (m *AiPredictModelDashboard) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.AiPredictModelDashboardId = int(id)
+	return
+}
+
+func (m *AiPredictModelDashboard) CreateMulti(items []*AiPredictModelDashboard) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AiPredictModelDashboard) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AiPredictModelDashboard) 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.AiPredictModelDashboardId).Exec()
+	return
+}
+
+func (m *AiPredictModelDashboard) 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 *AiPredictModelDashboard) 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 *AiPredictModelDashboard) GetItemById(id int) (item *AiPredictModelDashboard, 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 *AiPredictModelDashboard) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *AiPredictModelDashboard, 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 *AiPredictModelDashboard) 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 *AiPredictModelDashboard) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AiPredictModelDashboard, 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 *AiPredictModelDashboard) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AiPredictModelDashboard, 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
+}
+
+// AiPredictModelDashboardItem AI预测模型-BI看板
+type AiPredictModelDashboardItem struct {
+	DashboardId     int    `description:"看板ID"`
+	IndexId         int    `description:"标的ID"`
+	DashboardName   string `description:"看板名称"`
+	Sort            int    `description:"排序"`
+	SysUserId       int    `description:"创建人ID"`
+	SysUserRealName string `description:"创建人姓名"`
+	CreateTime      string `description:"创建时间"`
+	ModifyTime      string `description:"修改时间"`
+}
+
+func (m *AiPredictModelDashboard) Format2Item() (item *AiPredictModelDashboardItem) {
+	item = new(AiPredictModelDashboardItem)
+	item.DashboardId = m.AiPredictModelDashboardId
+	item.IndexId = m.AiPredictModelIndexId
+	item.DashboardName = m.DashboardName
+	item.Sort = m.Sort
+	item.SysUserId = m.SysUserId
+	item.SysUserRealName = m.SysUserRealName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+// AiPredictModelDashboardDetailResp BI看板详情响应
+type AiPredictModelDashboardDetailResp struct {
+	*AiPredictModelDashboardItem
+	CreateUserId       int    `description:"标的创建人ID"`
+	CreateUserRealName string `description:"标的创建人姓名"`
+	List               []*AiPredictModelDashboardDetailItem
+}
+
+// AiPredictModelDashboardSaveReq BI看板保存请求
+type AiPredictModelDashboardSaveReq struct {
+	IndexId       int    `description:"标的ID"`
+	DashboardName string `description:"看板名称"`
+	List          []*AiPredictModelDashboardDetailReq
+}
+
+type AiPredictModelDashboardDetailReq struct {
+	Type       int
+	UniqueCode string
+	Sort       int
+}
+
+// SaveIndexDashboard 保存标的看板
+func (m *AiPredictModelDashboard) SaveIndexDashboard(dashboardItem *AiPredictModelDashboard, dashboardDetails []*AiPredictModelDashboardDetail, isUpdate bool, updateCols []string) (err error) {
+	if dashboardItem == nil {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("trans begin err: %v", e)
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	// 新增/更新看板
+	var dashboardId int
+	if !isUpdate {
+		newId, e := tx.Insert(dashboardItem)
+		if e != nil {
+			err = fmt.Errorf("insert dashboard err: %v", e)
+			return
+		}
+		dashboardId = int(newId)
+	} else {
+		_, e = tx.Update(dashboardItem, updateCols...)
+		if e != nil {
+			err = fmt.Errorf("update dashboard err: %v", e)
+			return
+		}
+		dashboardId = dashboardItem.AiPredictModelDashboardId
+	}
+
+	// 清空详情并新增
+	if isUpdate {
+		sql := `DELETE FROM ai_predict_model_dashboard_detail WHERE ai_predict_model_dashboard_id = ?`
+		_, e = tx.Raw(sql, dashboardItem.AiPredictModelDashboardId).Exec()
+		if e != nil {
+			err = fmt.Errorf("clear dashboard detail err: %v", e)
+			return
+		}
+	}
+	if len(dashboardDetails) > 0 {
+		for _, v := range dashboardDetails {
+			v.AiPredictModelDashboardId = dashboardId
+		}
+		_, e = tx.InsertMulti(utils.MultiAddNum, dashboardDetails)
+		if e != nil {
+			err = fmt.Errorf("insert dashboard detail err: %v", e)
+			return
+		}
+	}
+	return
+}

+ 176 - 0
models/ai_predict_model/ai_predict_model_dashboard_detail.go

@@ -0,0 +1,176 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+// AiPredictModelDashboardDetail AI预测模型-BI看板
+type AiPredictModelDashboardDetail struct {
+	AiPredictModelDashboardDetailId int       `orm:"column(ai_predict_model_dashboard_detail_id);pk"`
+	AiPredictModelDashboardId       int       `description:"看板ID"`
+	Type                            int       `description:"类型:1-图表;2-表格"`
+	UniqueCode                      string    `description:"图表/表格唯一编码"`
+	Sort                            int       `description:"排序"`
+	CreateTime                      time.Time `description:"创建时间"`
+	ModifyTime                      time.Time `description:"修改时间"`
+}
+
+func (m *AiPredictModelDashboardDetail) TableName() string {
+	return "ai_predict_model_dashboard_detail"
+}
+
+type AiPredictModelDashboardDetailCols struct {
+	PrimaryId                 string
+	AiPredictModelDashboardId string
+	Type                      string
+	UniqueCode                string
+	Sort                      string
+	CreateTime                string
+	ModifyTime                string
+}
+
+func (m *AiPredictModelDashboardDetail) Cols() AiPredictModelDashboardDetailCols {
+	return AiPredictModelDashboardDetailCols{
+		PrimaryId:                 "ai_predict_model_dashboard_detail_id",
+		AiPredictModelDashboardId: "ai_predict_model_dashboard_id",
+		Type:                      "type",
+		UniqueCode:                "unique_code",
+		Sort:                      "sort",
+		CreateTime:                "create_time",
+		ModifyTime:                "modify_time",
+	}
+}
+
+func (m *AiPredictModelDashboardDetail) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.AiPredictModelDashboardDetailId = int(id)
+	return
+}
+
+func (m *AiPredictModelDashboardDetail) CreateMulti(items []*AiPredictModelDashboardDetail) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AiPredictModelDashboardDetail) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AiPredictModelDashboardDetail) 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.AiPredictModelDashboardDetailId).Exec()
+	return
+}
+
+func (m *AiPredictModelDashboardDetail) 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 *AiPredictModelDashboardDetail) 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 *AiPredictModelDashboardDetail) GetItemById(id int) (item *AiPredictModelDashboardDetail, 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 *AiPredictModelDashboardDetail) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *AiPredictModelDashboardDetail, 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 *AiPredictModelDashboardDetail) 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 *AiPredictModelDashboardDetail) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AiPredictModelDashboardDetail, 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 *AiPredictModelDashboardDetail) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AiPredictModelDashboardDetail, 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
+}
+
+// AiPredictModelDashboardDetailItem AI预测模型-BI看板详情
+type AiPredictModelDashboardDetailItem struct {
+	DashboardDetailId int    `description:"详情ID"`
+	DashboardId       int    `description:"看板ID"`
+	Type              int    `description:"类型:1-图表;2-表格"`
+	UniqueCode        string `description:"图表/表格唯一编码"`
+	Sort              int    `description:"排序"`
+	CreateTime        string `description:"创建时间"`
+	ModifyTime        string `description:"修改时间"`
+}
+
+func (m *AiPredictModelDashboardDetail) Format2Item() (item *AiPredictModelDashboardDetailItem) {
+	item = new(AiPredictModelDashboardDetailItem)
+	item.DashboardDetailId = m.AiPredictModelDashboardDetailId
+	item.DashboardId = m.AiPredictModelDashboardId
+	item.Type = m.Type
+	item.UniqueCode = m.UniqueCode
+	item.Sort = m.Sort
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}

+ 205 - 0
models/ai_predict_model/ai_predict_model_data.go

@@ -0,0 +1,205 @@
+package data_manage
+
+import (
+	"database/sql"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+const (
+	ModelDataSourceMonthly = 1 // 月度预测数据
+	ModelDataSourceDaily   = 2 // 日度预测数据
+)
+
+// AiPredictModelData AI预测模型标的数据
+type AiPredictModelData struct {
+	AiPredictModelDataId  int             `orm:"column(ai_predict_model_data_id);pk"`
+	AiPredictModelIndexId int             `description:"标的ID"`
+	IndexCode             string          `description:"标的编码"`
+	DataTime              time.Time       `description:"数据日期"`
+	Value                 sql.NullFloat64 `description:"实际值"`
+	PredictValue          sql.NullFloat64 `description:"预测值"`
+	Direction             string          `description:"方向"`
+	DeviationRate         string          `description:"偏差率"`
+	CreateTime            time.Time       `description:"创建时间"`
+	ModifyTime            time.Time       `description:"修改时间"`
+	DataTimestamp         int64           `description:"数据日期时间戳"`
+	Source                int             `description:"来源:1-月度预测(默认);2-日度预测"`
+}
+
+func (m *AiPredictModelData) TableName() string {
+	return "ai_predict_model_data"
+}
+
+type AiPredictModelDataCols struct {
+	PrimaryId             string
+	AiPredictModelIndexId string
+	IndexCode             string
+	DataTime              string
+	Value                 string
+	PredictValue          string
+	Direction             string
+	DeviationRate         string
+	CreateTime            string
+	ModifyTime            string
+	DataTimestamp         string
+	Source                string
+}
+
+func (m *AiPredictModelData) Cols() AiPredictModelDataCols {
+	return AiPredictModelDataCols{
+		PrimaryId:             "ai_predict_model_data_id",
+		AiPredictModelIndexId: "ai_predict_model_index_id",
+		IndexCode:             "index_code",
+		DataTime:              "data_time",
+		Value:                 "value",
+		PredictValue:          "predict_value",
+		Direction:             "direction",
+		DeviationRate:         "deviation_rate",
+		CreateTime:            "create_time",
+		ModifyTime:            "modify_time",
+		DataTimestamp:         "data_timestamp",
+		Source:                "source",
+	}
+}
+
+func (m *AiPredictModelData) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.AiPredictModelDataId = int(id)
+	return
+}
+
+func (m *AiPredictModelData) CreateMulti(items []*AiPredictModelData) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AiPredictModelData) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AiPredictModelData) Remove() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sqlRun := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	_, err = o.Raw(sqlRun, m.AiPredictModelDataId).Exec()
+	return
+}
+
+func (m *AiPredictModelData) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sqlRun := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	_, err = o.Raw(sqlRun, ids).Exec()
+	return
+}
+
+func (m *AiPredictModelData) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sqlRun := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	_, err = o.Raw(sqlRun, pars).Exec()
+	return
+}
+
+func (m *AiPredictModelData) GetItemById(id int) (item *AiPredictModelData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sqlRun := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sqlRun, id).QueryRow(&item)
+	return
+}
+
+func (m *AiPredictModelData) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *AiPredictModelData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sqlRun := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sqlRun, pars).QueryRow(&item)
+	return
+}
+
+func (m *AiPredictModelData) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sqlRun := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sqlRun, pars).QueryRow(&count)
+	return
+}
+
+func (m *AiPredictModelData) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AiPredictModelData, 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
+	}
+	sqlRun := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sqlRun, pars).QueryRows(&items)
+	return
+}
+
+func (m *AiPredictModelData) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AiPredictModelData, 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
+	}
+	sqlRun := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sqlRun, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// AiPredictModelDataItem AI预测模型标的数据
+type AiPredictModelDataItem struct {
+	DataId        int      `description:"ID"`
+	IndexId       int      `description:"标的ID"`
+	IndexCode     string   `description:"标的编码"`
+	DataTime      string   `description:"数据日期"`
+	Value         *float64 `description:"实际值"`
+	PredictValue  *float64 `description:"预测值"`
+	Direction     string   `description:"方向"`
+	DeviationRate string   `description:"偏差率"`
+	DataTimestamp int64    `description:"数据日期时间戳"`
+}
+
+func (m *AiPredictModelData) Format2Item() (item *AiPredictModelDataItem) {
+	item = new(AiPredictModelDataItem)
+	item.DataId = m.AiPredictModelDataId
+	item.IndexId = m.AiPredictModelIndexId
+	item.IndexCode = m.IndexCode
+	item.DataTime = utils.TimeTransferString(utils.FormatDate, m.DataTime)
+	if m.Value.Valid {
+		item.Value = &m.Value.Float64
+	}
+	if m.PredictValue.Valid {
+		item.PredictValue = &m.PredictValue.Float64
+	}
+	item.Direction = m.Direction
+	item.DeviationRate = m.DeviationRate
+	item.DataTimestamp = m.DataTimestamp
+	return
+}

+ 390 - 0
models/ai_predict_model/ai_predict_model_index.go

@@ -0,0 +1,390 @@
+package data_manage
+
+import (
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"strings"
+	"time"
+)
+
+// AiPredictModelIndex AI预测模型标的
+type AiPredictModelIndex struct {
+	AiPredictModelIndexId int       `orm:"column(ai_predict_model_index_id);pk"`
+	IndexName             string    `description:"标的名称"`
+	IndexCode             string    `description:"自生成的指标编码"`
+	ClassifyId            int       `description:"分类ID"`
+	ModelFramework        string    `description:"模型框架"`
+	PredictDate           time.Time `description:"预测日期"`
+	PredictValue          float64   `description:"预测值"`
+	PredictFrequency      string    `description:"预测频度"`
+	DirectionAccuracy     string    `description:"方向准确度"`
+	AbsoluteDeviation     string    `description:"绝对偏差"`
+	ExtraConfig           string    `description:"模型参数"`
+	Sort                  int       `description:"排序"`
+	SysUserId             int       `description:"创建人ID"`
+	SysUserRealName       string    `description:"创建人姓名"`
+	LeftMin               string    `description:"图表左侧最小值"`
+	LeftMax               string    `description:"图表左侧最大值"`
+	CreateTime            time.Time `description:"创建时间"`
+	ModifyTime            time.Time `description:"修改时间"`
+}
+
+func (m *AiPredictModelIndex) TableName() string {
+	return "ai_predict_model_index"
+}
+
+type AiPredictModelIndexCols struct {
+	PrimaryId         string
+	IndexName         string
+	IndexCode         string
+	ClassifyId        string
+	ModelFramework    string
+	PredictDate       string
+	PredictValue      string
+	DirectionAccuracy string
+	AbsoluteDeviation string
+	ExtraConfig       string
+	Sort              string
+	SysUserId         string
+	SysUserRealName   string
+	LeftMin           string
+	LeftMax           string
+	CreateTime        string
+	ModifyTime        string
+}
+
+func (m *AiPredictModelIndex) Cols() AiPredictModelIndexCols {
+	return AiPredictModelIndexCols{
+		PrimaryId:         "ai_predict_model_index_id",
+		IndexName:         "index_name",
+		IndexCode:         "index_code",
+		ClassifyId:        "classify_id",
+		ModelFramework:    "model_framework",
+		PredictDate:       "predict_date",
+		PredictValue:      "predict_value",
+		DirectionAccuracy: "direction_accuracy",
+		AbsoluteDeviation: "absolute_deviation",
+		ExtraConfig:       "extra_config",
+		Sort:              "sort",
+		SysUserId:         "sys_user_id",
+		SysUserRealName:   "sys_user_real_name",
+		LeftMin:           "left_min",
+		LeftMax:           "left_max",
+		CreateTime:        "create_time",
+		ModifyTime:        "modify_time",
+	}
+}
+
+func (m *AiPredictModelIndex) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.AiPredictModelIndexId = int(id)
+	return
+}
+
+func (m *AiPredictModelIndex) CreateMulti(items []*AiPredictModelIndex) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AiPredictModelIndex) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AiPredictModelIndex) 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.AiPredictModelIndexId).Exec()
+	return
+}
+
+func (m *AiPredictModelIndex) 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 *AiPredictModelIndex) 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 *AiPredictModelIndex) GetItemById(id int) (item *AiPredictModelIndex, 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 *AiPredictModelIndex) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *AiPredictModelIndex, 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 *AiPredictModelIndex) 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 *AiPredictModelIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AiPredictModelIndex, 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 *AiPredictModelIndex) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AiPredictModelIndex, 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
+}
+
+// AiPredictModelIndexItem AI预测模型标的信息
+type AiPredictModelIndexItem struct {
+	IndexId           int     `description:"标的ID"`
+	IndexName         string  `description:"标的名称"`
+	IndexCode         string  `description:"自生成的指标编码"`
+	ClassifyId        int     `description:"分类ID"`
+	ClassifyName      string  `description:"分类名称"`
+	ModelFramework    string  `description:"模型框架"`
+	PredictDate       string  `description:"预测日期"`
+	PredictValue      float64 `description:"预测值"`
+	PredictFrequency  string  `description:"预测频度"`
+	DirectionAccuracy string  `description:"方向准确度"`
+	AbsoluteDeviation string  `description:"绝对偏差"`
+	ExtraConfig       string  `description:"模型参数"`
+	SysUserId         int     `description:"创建人ID"`
+	SysUserRealName   string  `description:"创建人姓名"`
+	CreateTime        string  `description:"创建时间"`
+	ModifyTime        string  `description:"修改时间"`
+}
+
+func (m *AiPredictModelIndex) Format2Item() (item *AiPredictModelIndexItem) {
+	item = new(AiPredictModelIndexItem)
+	item.IndexId = m.AiPredictModelIndexId
+	item.IndexName = m.IndexName
+	item.IndexCode = m.IndexCode
+	item.ClassifyId = m.ClassifyId
+	item.ModelFramework = m.ModelFramework
+	item.PredictDate = utils.TimeTransferString(utils.FormatDate, m.PredictDate)
+	item.PredictValue = m.PredictValue
+	item.PredictFrequency = m.PredictFrequency
+	item.DirectionAccuracy = m.DirectionAccuracy
+	item.AbsoluteDeviation = m.AbsoluteDeviation
+	item.ExtraConfig = m.ExtraConfig
+	item.SysUserId = m.SysUserId
+	item.SysUserRealName = m.SysUserRealName
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+type AiPredictModelIndexPageListResp struct {
+	Paging *paging.PagingItem
+	List   []*AiPredictModelIndexItem `description:"列表"`
+}
+
+// RemoveIndexAndData 删除标的及数据
+func (m *AiPredictModelIndex) RemoveIndexAndData(indexId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("trans begin err: %v", e)
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := `DELETE FROM ai_predict_model_index WHERE ai_predict_model_index_id = ? LIMIT 1`
+	_, e = tx.Raw(sql, indexId).Exec()
+	if e != nil {
+		err = fmt.Errorf("remove index err: %v", e)
+		return
+	}
+	sql = ` DELETE FROM ai_predict_model_data WHERE ai_predict_model_index_id = ?`
+	_, e = tx.Raw(sql, indexId).Exec()
+	if e != nil {
+		err = fmt.Errorf("remove index data err: %v", e)
+		return
+	}
+	return
+}
+
+// UpdateAiPredictModelIndexSortByClassifyId 根据分类id更新排序
+func UpdateAiPredictModelIndexSortByClassifyId(classifyId, nowSort int, prevEdbInfoId int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` UPDATE ai_predict_model_index SET sort = ` + updateSort + ` WHERE classify_id = ?`
+	if prevEdbInfoId > 0 {
+		sql += ` AND ( sort > ? or ( ai_predict_model_index_id > ` + fmt.Sprint(prevEdbInfoId) + ` and sort=` + fmt.Sprint(nowSort) + ` )) `
+	} else {
+		sql += ` AND ( sort > ? )`
+	}
+	_, err = o.Raw(sql, classifyId, nowSort).Exec()
+	return
+}
+
+// GetFirstAiPredictModelIndexByClassifyId 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstAiPredictModelIndexByClassifyId(classifyId int) (item *AiPredictModelIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM ai_predict_model_index WHERE classify_id = ? order by sort asc,ai_predict_model_index_id asc limit 1`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+type AiPredictModelImportData struct {
+	Index *AiPredictModelIndex
+	Data  []*AiPredictModelData
+}
+
+// ImportIndexAndData 导入数据
+func (m *AiPredictModelIndex) ImportIndexAndData(createIndexes, updateIndexes []*AiPredictModelImportData, updateCols []string) (err error) {
+	if len(createIndexes) == 0 && len(updateIndexes) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	tx, e := o.Begin()
+	if e != nil {
+		err = fmt.Errorf("trans begin err: %v", e)
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	if len(updateIndexes) > 0 {
+		for _, v := range updateIndexes {
+			// 更新指标
+			_, e = tx.Update(v.Index, updateCols...)
+			if e != nil {
+				err = fmt.Errorf("update index err: %v", e)
+				return
+			}
+			for _, d := range v.Data {
+				d.AiPredictModelIndexId = v.Index.AiPredictModelIndexId
+				d.IndexCode = v.Index.IndexCode
+				d.DataTimestamp = d.DataTime.UnixNano() / 1e6
+			}
+
+			// 清空指标并新增
+			sql := `DELETE FROM ai_predict_model_data WHERE ai_predict_model_index_id = ?`
+			_, e = tx.Raw(sql, v.Index.AiPredictModelIndexId).Exec()
+			if e != nil {
+				err = fmt.Errorf("clear index data err: %v", e)
+				return
+			}
+			_, e = tx.InsertMulti(utils.MultiAddNum, v.Data)
+			if e != nil {
+				err = fmt.Errorf("insert index data err: %v", e)
+				return
+			}
+		}
+	}
+
+	if len(createIndexes) > 0 {
+		for _, v := range createIndexes {
+			indexId, e := tx.Insert(v.Index)
+			if e != nil {
+				err = fmt.Errorf("insert index err: %v", e)
+				return
+			}
+			for _, d := range v.Data {
+				d.AiPredictModelIndexId = int(indexId)
+				d.IndexCode = v.Index.IndexCode
+				d.DataTimestamp = d.DataTime.UnixNano() / 1e6
+			}
+			_, e = tx.InsertMulti(utils.MultiAddNum, v.Data)
+			if e != nil {
+				err = fmt.Errorf("insert index data err: %v", e)
+				return
+			}
+		}
+	}
+	return
+}
+
+type AiPredictModelDetailResp struct {
+	TableData      []*AiPredictModelDataItem        `description:"表格数据"`
+	ChartView      *data_manage.ChartInfoDetailResp `description:"月度预测数据图表"`
+	DailyChartView *data_manage.ChartInfoDetailResp `description:"日度预测数据图表"`
+}
+
+type AiPredictModelIndexSaveReq struct {
+	IndexId      int                           `description:"指标ID"`
+	MonthlyChart *AiPredictModelIndexSaveChart `description:"月度图表信息"`
+	DailyChart   *AiPredictModelIndexSaveChart `description:"日度图表信息"`
+}
+
+type AiPredictModelIndexSaveChart struct {
+	LeftMin string `description:"图表左侧最小值"`
+	LeftMax string `description:"图表左侧最大值"`
+	Unit    string `description:"单位"`
+}
+
+type AiPredictModelIndexExtraConfig struct {
+	MonthlyChart struct {
+		LeftMin string `description:"图表左侧最小值"`
+		LeftMax string `description:"图表左侧最大值"`
+		Unit    string `description:"单位"`
+	}
+	DailyChart struct {
+		LeftMin           string `description:"图表左侧最小值"`
+		LeftMax           string `description:"图表左侧最大值"`
+		Unit              string `description:"单位"`
+		PredictLegendName string `description:"预测图例的名称(通常为Predicted)"`
+	}
+}

+ 9 - 2
models/ai_summary/ai_summary_classify.go

@@ -2,9 +2,10 @@ package ai_summary
 
 import (
 	"fmt"
+	"time"
+
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"time"
 )
 
 type AiSummaryClassify struct {
@@ -29,6 +30,7 @@ func AddAiSummaryClassify(item *AiSummaryClassify) (lastId int64, err error) {
 
 type AiSummaryClassifyItems struct {
 	AiSummaryClassifyId int
+	Title               string    `description:"标题" json:"-"`
 	ClassifyName        string    `description:"分类名称"`
 	ParentId            int       `description:"父级id"`
 	HasData             int       `description:"是否含有指标数据"`
@@ -328,4 +330,9 @@ func GetAiSummaryClassifyAll() (items []*AiSummaryClassifyItems, err error) {
 	return
 }
 
-
+func GetAiSummaryClassifyAllIncludeParent() (items []*AiSummaryClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM ai_summary_classify order by sort asc,ai_summary_classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}

+ 169 - 0
models/bi_dashboard/bi_dashboard.go

@@ -0,0 +1,169 @@
+package bi_dashboard
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BiDashboard struct {
+	BiDashboardId         int       `orm:"column(bi_dashboard_id);pk"`         // bi看板id
+	BiDashboardClassifyId int       `gorm:"column:bi_dashboard_classify_id" `  // 看板分类id
+	BiDashboardName       string    `gorm:"column:bi_dashboard_name;size:255"` // 看板名称
+	SysAdminId            int       `gorm:"column:sys_admin_id" `              // 创建人ID
+	SysAdminName          string    `gorm:"column:sys_admin_name;size:128" `   // 创建人姓名
+	Sort                  int       `gorm:"column:sort" `                      // 排序字段
+	CreateTime            time.Time `gorm:"column:create_time" `               // 创建时间
+	ModifyTime            time.Time `gorm:"column:modify_time"`                // 更新时间
+	State                 int       `gorm:"column:state"`                      // 状态 1:未公开; 4-待审批;5-已驳回;6-已通过
+}
+
+// tableName
+func (m *BiDashboard) TableName() string {
+	return "bi_dashboard"
+}
+
+func (m *BiDashboard) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+// AddBiDashboard 新增看板
+func AddBiDashboard(item *BiDashboard) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// GetShareDashboard 获取公开分享的看板
+func GetShareDashboard() (items []*BiDashboard, err error) {
+	//o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM bi_dashboard WHERE 1=1 AND state = 6 `
+
+	sql += `ORDER BY create_time DESC`
+	//sql += `ORDER BY create_time DESC LIMIT ?,?`
+	//_, err = o.Raw(sql).QueryRows(&items)
+	//err = global.DEFAULT_DmSQL.Raw(sql).Find(&items).Error
+	return
+}
+
+func GetBiDashboardList(condition string, pars []interface{}) (items []*BiDashboard, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM bi_dashboard WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	//
+	sql += `ORDER BY modify_time DESC `
+	//sql += `ORDER BY create_time DESC LIMIT ?,?`
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+// GetDashboardById 获取看板
+func GetDashboardById(id int) (item *BiDashboard, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM bi_dashboard WHERE bi_dashboard_id = ? limit 1`
+
+	//sql += `ORDER BY create_time DESC LIMIT ?,?`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// del
+func DelDashboard(id int) (err error) {
+	o := orm.NewOrm()
+	sql := `DELETE FROM bi_dashboard WHERE bi_dashboard_id = ?`
+	_, err = o.Raw(sql, id).Exec()
+	return
+}
+
+// BiDashboardEditingCache PPT编辑缓存信息
+type BiDashboardEditingCache struct {
+	IsEditing bool   `description:"是否有人编辑"`
+	AdminId   int    `description:"编辑者ID"`
+	Editor    string `description:"编辑者姓名"`
+	Tips      string `description:"提示信息"`
+}
+
+// DashboardDetailResp 详情响应体
+type DashboardDetailResp struct {
+	*BiDashboard
+	IsGrant int                     `description:"是否共享,0:不是,1:是"`
+	Editor  BiDashboardEditingCache `description:"编辑人信息"`
+	List    []*BiDashboardDetail
+}
+
+type AddDashboardReq struct {
+	List            []*AddDashboardListReq
+	BiDashboardName string `description:"看板名称"`
+}
+
+type AddDashboardListReq struct {
+	Type       int
+	UniqueCode string
+	Sort       int
+}
+
+type EditDashboardReq struct {
+	List            []*AddDashboardListReq
+	BiDashboardId   int    `description:"看板id"`
+	BiDashboardName string `description:"看板名称"`
+}
+
+// update
+func EditDashboard(item *BiDashboard) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE bi_dashboard SET bi_dashboard_name=?,modify_time=?,state=? WHERE bi_dashboard_id=?`
+	_, err = o.Raw(sql, item.BiDashboardName, time.Now(), item.State, item.BiDashboardId).Exec()
+	return
+}
+
+type DelDashboardReq struct {
+	BiDashboardId int `description:"看板id"`
+}
+
+func GetDashboradByIds(dashboradIds []int) (list []*BiDashboard, err error) {
+
+	//err = global.DEFAULT_DmSQL.Table("bi_dashboard").Where("bi_dashboard_id IN ?", dashboradIds).Find(&list).Error
+
+	return
+}
+
+// GetAllGrantList 获取已经有权限的看板列表
+func GetAllGrantList(sysUserId int) (list []*BiDashboard, err error) {
+	sql := `SELECT a.* FROM bi_dashboard a JOIN bi_dashboard_grant b on a.bi_dashboard_id=b.bi_dashboard_id 
+ WHERE b.grant_admin_id=?`
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, sysUserId).QueryRows(&list)
+	return
+}
+
+func SaveDashboard(item *BiDashboard) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(item)
+	return
+}
+
+// BiDashboardEditingReq 标记编辑中请求体
+type BiDashboardEditingReq struct {
+	BiDashboardId int `description:"看板主键ID"`
+	Status        int `description:"标记状态: 1-编辑中; 2-编辑完成"`
+}
+
+// GetAllMyShareList 获取我共享的看板
+func GetAllMyShareList(sysUserId int) (list []*BiDashboard, err error) {
+	sql := `SELECT a.* FROM bi_dashboard a JOIN bi_dashboard_grant b on a.bi_dashboard_id=b.bi_dashboard_id 
+WHERE a.sys_admin_id = ? GROUP BY a.bi_dashboard_id   `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, sysUserId).QueryRows(&list)
+	return
+}
+
+// getByName
+func GetDashboardByName(name string, adminId int) (item *BiDashboard, err error) {
+	sql := `SELECT * FROM bi_dashboard WHERE bi_dashboard_name = ? and sys_admin_id =? limit 1`
+	o := orm.NewOrm()
+	err = o.Raw(sql, name, adminId).QueryRow(&item)
+	return
+}

+ 136 - 0
models/bi_dashboard/bi_dashboard_classify.go

@@ -0,0 +1,136 @@
+package bi_dashboard
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BiDashboardClassify struct {
+	BiDashboardClassifyId   int       `orm:"column(bi_dashboard_classify_id);pk"` // bi看板分类id
+	BiDashboardClassifyName string    `gorm:"column:bi_dashboard_classify_name;size:255;not null" `     // 看板分类名称
+	Sort                    int       `gorm:"column:sort" `                                             // 排序字段
+	CreateTime              time.Time `gorm:"column:create_time" `                                      // 创建时间
+	ModifyTime              time.Time `gorm:"column:modify_time"`                                       // 更新时间
+}
+type BiDashboardClassifyItem struct {
+	BiDashboardClassifyId   int    // bi看板分类id
+	BiDashboardClassifyName string // 看板分类名称
+	Sort                    int    // 排序字段
+	CreateTime              string // 创建时间
+	ModifyTime              string // 更新时间
+}
+
+// tableName
+func (m *BiDashboardClassify) TableName() string {
+	return "bi_dashboard_classify"
+}
+
+// add
+func AddBiDashboardClassify(item *BiDashboardClassify) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// update
+func EditDashboardClassify(item *BiDashboardClassify) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Raw("UPDATE bi_dashboard_classify SET bi_dashboard_classify_name=?,sort=?,modify_time=? WHERE bi_dashboard_classify_id=?", item.BiDashboardClassifyName, item.Sort, item.ModifyTime, item.BiDashboardClassifyId).Exec()
+	return
+}
+
+type RespGroupList struct {
+	MyList    []*BiDashboard
+	OtherList []*RespOtherGroupListItem
+}
+
+type RespMyGroupListItem struct {
+	GroupId       int64  `description:"目录id"`
+	GroupName     string `description:"目录名称"`
+	AdminId       int    `description:"目录创建者账号ID"`
+	DashboardList []*BiDashboard
+}
+
+type RespOtherGroupListItem struct {
+	GroupId       int64  `description:"目录id"`
+	GroupName     string `description:"目录名称"`
+	AdminId       int    `description:"目录创建者账号ID"`
+	DashboardList []*BiDashboard
+}
+
+type RespGroupDashboardListItem struct {
+	GroupPptId    int64  `description:"目录和ppt绑定序号"`
+	PptId         int64  `description:"ppt ID"`
+	Title         string `description:"标题"`
+	AdminId       int    `description:"移动ppt到该目录的系统用户id"`
+	AdminRealName string `description:"系统用户名称"`
+	PptVersion    int8   `description:"是否ppt的旧版本;1:旧的,2:新的"`
+	IsSingleShare int8   `description:"是否是单个共享ppt,0未单个共享,1共享"`
+	PptxUrl       string `description:"pptx下载地址"`
+	ReportId      int    `description:"关联的报告ID"`
+	ReportCode    string `description:"关联的报告code"`
+	PptCreateTime string `description:"ppt创建时间"`
+	PptModifyTime string `description:"ppt修改时间"`
+	PublishTime   string `description:"发布时间"`
+	PptPage       int    `description:"PPT总页数"`
+	IsReceived    int8   `description:"是否收到的共享,0:不是,1:是"`
+	IsGrant       int8   `description:"是否分配了权限,0:不是,1:是"`
+	TitleSetting  string `description:"PPT标题设置"`
+}
+
+func GetBiDashboardClassifyAllList() (list []*BiDashboardClassify, err error) {
+	//err = global.DEFAULT_DmSQL.Select("*").Find(&list).Error
+	o := orm.NewOrm()
+	_, err = o.Raw("SELECT * FROM bi_dashboard_classify").QueryRows(&list)
+	return
+}
+
+func GetBiDashboardClassifyById(id int) (item *BiDashboardClassify, err error) {
+	//err = global.DEFAULT_DmSQL.Where("bi_dashboard_classify_id = ?", id).First(&item).Error
+	o := orm.NewOrm()
+	err = o.Raw("SELECT * FROM bi_dashboard_classify where bi_dashboard_classify_id=?", id).QueryRow(&item)
+	return
+}
+
+type AddDashboardClassifyReq struct {
+	ClassifyName string `description:"看板名称"`
+}
+
+type EditDashboardClassifyReq struct {
+	BiDashboardClassifyId int    `description:"看板id"`
+	ClassifyName          string `description:"看板名称"`
+}
+
+type DelDashboardClassifyReq struct {
+	BiDashboardClassifyId int `description:"看板id"`
+}
+
+// GetBiDashboardClassifyMaxSort 获取看板分类下最大的排序数
+func GetBiDashboardClassifyMaxSort() (sort int, err error) {
+	sql := `SELECT COALESCE(Max(sort), 0) AS sort FROM bi_dashboard_classify `
+	//err = global.DEFAULT_DmSQL.Raw(sql).First(&sort).Error
+	o := orm.NewOrm()
+	err = o.Raw(sql).QueryRow(&sort)
+	return
+}
+
+// del
+//func DelBiDashboardClassify(id int) (err error) {
+//	return global.DEFAULT_DmSQL.Where("bi_dashboard_classify_id = ?", id).Delete(&BiDashboardClassify{}).Error
+//}
+
+type RespPublicGroupListItem struct {
+	GroupId       int64  `description:"目录id"`
+	GroupName     string `description:"目录名称"`
+	AdminId       int    `description:"目录创建者账号ID"`
+	DashboardList []*BiDashboard
+	Children      []*RespPublicGroupListItem
+}
+
+// getByName
+func GetBiDashboardClassifyByName(classifyName string) (count int64, err error) {
+	//err = global.DEFAULT_DmSQL.Model(&BiDashboardClassify{}).Where("bi_dashboard_classify_name = ?", classifyName).Count(&count).Error
+	o := orm.NewOrm()
+	err = o.Raw("SELECT count(1) FROM bi_dashboard_classify where bi_dashboard_classify_name=?", classifyName).QueryRow(&count)
+	return
+}

+ 77 - 0
models/bi_dashboard/bi_dashboard_detail.go

@@ -0,0 +1,77 @@
+package bi_dashboard
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BiDashboardDetail struct {
+	BiDashboardDetailId int       `orm:"column(bi_dashboard_detail_id);pk" ` // bi看板id
+	BiDashboardId       int       `gorm:"column:bi_dashboard_id" `                                 // 看板id
+	Type                int       `gorm:"column:type" `                                            // 1图表 2表格
+	UniqueCode          string    `gorm:"column:unique_code;size:32;not null" `                    // 报告唯一编码
+	Sort                int       `gorm:"column:sort" `                                            // 排序字段
+	CreateTime          time.Time `gorm:"column:create_time" `                                     // 创建时间
+	ModifyTime          time.Time `gorm:"column:modify_time" `                                     // 更新时间
+}
+
+// tableName
+func (m *BiDashboardDetail) TableName() string {
+	return "bi_dashboard_detail"
+}
+
+// add
+//func AddBiDashboardDetail(item *BiDashboardDetail) (err error) {
+	//err = global.DEFAULT_DmSQL.Create(item).Error
+	//return
+//}
+
+func GetBiDashboardDetailById(id int) (list []*BiDashboardDetail, err error) {
+	//err = global.DEFAULT_DmSQL.Table("bi_dashboard_detail").Where("bi_dashboard_id IN ?", id).Find(&list).Error
+	o := orm.NewOrm()
+	_, err = o.Raw("SELECT * FROM bi_dashboard_detail where bi_dashboard_id = ?", id).QueryRows(&list)
+	return
+}
+
+// multiAdd
+func AddBiDashboardDetailMulti(items []*BiDashboardDetail) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// del
+func DeleteBiDashboardDetail(id int) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Raw("DELETE from bi_dashboard_detail where bi_dashboard_id=?", id).Exec()
+	return
+}
+
+type MoveDashboardDetailReq struct {
+	BiDashboardId       int `description:"看板id"`
+	BiDashboardDetailId int `description:"看板详情id"`
+	Sort                int `description:"排序"`
+	OtherDetailId       int `description:"交换的详情id"`
+	OtherSort           int `description:"交换的排序"`
+}
+
+// update
+func EditBiDashboardDetail(item *BiDashboardDetail) (err error){
+	//return global.DEFAULT_DmSQL.Model(item).Where("bi_dashboard_detail_id = ?", item.BiDashboardDetailId).Updates(item).Error
+	o := orm.NewOrm()
+	_, err = o.Raw("UPDATE bi_dashboard_detail SET bi_dashboard_id=?,type=?,unique_code=?,sort=?,modify_time=? WHERE bi_dashboard_detail_id=?", item.BiDashboardId, item.Type, item.UniqueCode, item.Sort, item.ModifyTime, item.BiDashboardDetailId).Exec()
+	return
+}
+
+type DelDashboardDetailReq struct {
+	BiDashboardDetailId int `description:"看板详情id"`
+}
+
+
+// del
+func DeleteBiDashboardDetailByDetailId(id int) (err error) {
+	//return global.DEFAULT_DmSQL.Where("bi_dashboard_detail_id = ?", id).Delete(&BiDashboardDetail{}).Error
+	o := orm.NewOrm()
+	_, err = o.Raw("DELETE from bi_dashboard_detail where bi_dashboard_detail_id=?", id).Exec()
+	return
+}

+ 75 - 0
models/bi_dashboard/bi_dashboard_grant.go

@@ -0,0 +1,75 @@
+package bi_dashboard
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BiDashboardGrant struct {
+	GrantId       int       `orm:"column(grant_id);pk"` // 授权id
+	BiDashboardId int       `gorm:"column:bi_dashboard_id" `                  // 看板id
+	GrantAdminId  int       `gorm:"column:grant_admin_id"`                    // 授权的用户id
+	CreateTime    time.Time `gorm:"column:create_time"`                       // 授权时间
+}
+
+// tableName
+func (m *BiDashboardGrant) TableName() string {
+	return "bi_dashboard_grant"
+}
+
+// GrantDashboardReq 分配看板权限
+type GrantDashboardReq struct {
+	BiDashboardId int    `description:"看板id" `
+	AdminIdStr    string `description:"指定成员id,多个成员用英文,隔开"`
+}
+
+// MultiAddDashboardGrant 批量添加授权记录
+func MultiAddDashboardGrant(boardId int, list []*BiDashboardGrant) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := "DELETE from bi_dashboard_grant where bi_dashboard_id=?"
+	_, err = to.Raw(sql, boardId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 新增授权记录
+	if len(list) > 0 {
+		_, tmpErr := to.InsertMulti(len(list), list)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+	}
+
+	return
+}
+
+// PublicDashboardReq 设置公共看板权限
+type PublicDashboardReq struct {
+	BiDashboardId int `description:"看板id" `
+}
+
+// del
+func DeleteDashboardGrant(biDashboardId int) (err error) {
+	//return global.DEFAULT_DmSQL.Where("bi_dashboard_id=?", biDashboardId).Delete(&BiDashboardGrant{}).Error
+	o := orm.NewOrm()
+	_, err = o.Raw("DELETE from bi_dashboard_grant where bi_dashboard_id=?", biDashboardId).Exec()
+	return
+}
+
+// get
+func GetDashboardGrantInfo(biDashboardId int) (list []*BiDashboardGrant, err error) {
+	//return list, global.DEFAULT_DmSQL.Where("bi_dashboard_id=?", biDashboardId).Find(&list).Error
+	o := orm.NewOrm()
+	_, err = o.Raw("SELECT * FROM bi_dashboard_grant where bi_dashboard_id=?", biDashboardId).QueryRows(&list)
+	return
+}

+ 46 - 0
models/bi_dashboard/bi_dashboard_home_page.go

@@ -0,0 +1,46 @@
+package bi_dashboard
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type BiDashboardHomePage struct {
+	BiDashboardHomePageId int       `orm:"column(bi_dashboard_home_page_id);pk"`
+	BiDashboardId         int       `gorm:"type:int(10);default:null;comment:'看板id'"`
+	AdminId               int       `gorm:"type:int(10);default:null;"`
+	CreateTime            time.Time `gorm:"type:datetime;comment:'创建时间'"`
+	ModifyTime            time.Time `gorm:"type:datetime;comment:'更新时间'"`
+	FromType              int       `gorm:"type:int(10);default:null;comment:'来源,前端跳转用 1我的 2共享 3公共"`
+}
+
+// tableName
+func (m *BiDashboardHomePage) TableName() string {
+	return "bi_dashboard_home_page"
+}
+
+// get
+func GetBiDashboardHomePageById(id int) (item *BiDashboardHomePage, err error) {
+	sql := ` SELECT a.* FROM bi_dashboard_home_page AS a INNER JOIN bi_dashboard AS b 
+ON a.bi_dashboard_id = b.bi_dashboard_id WHERE a.admin_id = ? `
+	o := orm.NewOrm()
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+// save
+func SaveBiDashboardHomePage(item *BiDashboardHomePage) (err error) {
+	//return global.DEFAULT_DmSQL.Save(item).Error
+	o := orm.NewOrm()
+	if item.BiDashboardHomePageId > 0 {
+		_, err = o.Raw("UPDATE bi_dashboard_home_page SET bi_dashboard_id=?,modify_time=? WHERE bi_dashboard_home_page_id=?", item.BiDashboardId, item.ModifyTime, item.BiDashboardHomePageId).Exec()
+	} else {
+		_, err = o.Insert(item)
+	}
+	return
+}
+
+type SaveHomePageReq struct {
+	BiDashboardId int `description:"看板id"`
+	FromType      int `description:"来源,前端跳转用 1我的 2共享 3公共"`
+}

+ 37 - 0
models/binlog/binlog.go

@@ -0,0 +1,37 @@
+package binlog
+
+import "github.com/beego/beego/v2/client/orm"
+
+// BinlogFormatStruct
+// @Description: 数据库的binlog格式
+type BinlogFormatStruct struct {
+	VariableName string `orm:"column(Variable_name)"`
+	Value        string `orm:"column(Value)"`
+}
+
+// GetBinlogFormat
+// @Description: 获取数据库的binlog格式
+// @return item
+// @return err
+func GetBinlogFormat() (item *BinlogFormatStruct, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SHOW VARIABLES LIKE 'binlog_format'`
+	err = o.Raw(sql).QueryRow(&item)
+	return
+}
+
+type BinlogFileStruct struct {
+	File     string `orm:"column(File)"`
+	Position uint32 `orm:"column(Position)"`
+}
+
+// GetShowMaster
+// @Description: 获取master的状态
+// @return item
+// @return err
+func GetShowMaster() (item *BinlogFileStruct, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `show master status`
+	err = o.Raw(sql).QueryRow(&item)
+	return
+}

+ 64 - 0
models/binlog/business_sys_interaction_log.go

@@ -0,0 +1,64 @@
+package binlog
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// BusinessSysInteractionLog 商家系统交互记录表
+type BusinessSysInteractionLog struct {
+	ID             uint32    `orm:"column(id);pk"`
+	InteractionKey string    // 记录Key
+	InteractionVal string    // 记录值
+	Remark         string    // 备注
+	ModifyTime     time.Time // 修改日期
+	CreateTime     time.Time // 创建时间
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *BusinessSysInteractionLog) TableName() string {
+	return "business_sys_interaction_log"
+}
+
+// BusinessSysInteractionLogColumns get sql column name.获取数据库列名
+var BusinessSysInteractionLogColumns = struct {
+	ID             string
+	InteractionKey string
+	InteractionVal string
+	Remark         string
+	ModifyTime     string
+	CreateTime     string
+}{
+	ID:             "id",
+	InteractionKey: "interaction_key",
+	InteractionVal: "interaction_val",
+	Remark:         "remark",
+	ModifyTime:     "modify_time",
+	CreateTime:     "create_time",
+}
+
+// Create 添加数据
+func (m *BusinessSysInteractionLog) Create() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(m)
+	return
+}
+
+// Update 更新数据
+func (m *BusinessSysInteractionLog) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+var BinlogFileNameKey = "binlog_edb_info_filename" // binlog文件名
+var BinlogPositionKey = "binlog_edb_info_position" // binlog位置
+
+// GetBusinessSysInteractionLogByKey 根据记录key获取数据
+func GetBusinessSysInteractionLogByKey(key string) (item *BusinessSysInteractionLog, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM business_sys_interaction_log WHERE interaction_key = ?"
+	err = o.Raw(sql, key).QueryRow(&item)
+	return
+}

+ 11 - 0
models/business_conf.go

@@ -9,6 +9,9 @@ import (
 	"time"
 )
 
+var (
+	BusinessConfMap map[string]string
+)
 const (
 	BusinessConfUseXf                     = "UseXf"
 	BusinessConfXfAppid                   = "XfAppid"
@@ -255,3 +258,11 @@ func InitUseMongoConf() {
 		utils.UseMongo = true
 	}
 }
+
+func InitBusinessConf() {
+	var e error
+	BusinessConfMap, e = GetBusinessConf()
+	if e != nil {
+		return
+	}
+}

+ 60 - 1
models/classify.go

@@ -3,9 +3,10 @@ package models
 import (
 	"eta/eta_api/utils"
 	"fmt"
+	"time"
+
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"time"
 )
 
 type Classify struct {
@@ -90,6 +91,55 @@ type ClassifyAddReq struct {
 	RelateVideo           int                    `description:"是否在路演视频中可选: 0-否; 1-是"`*/
 }
 
+func (c *Classify) Delete(childIds []int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+	// 删除自身
+	_, err = tx.Delete(c)
+	if err != nil {
+		return
+	}
+	// 删除子分类
+	if len(childIds) > 0 {
+		sql := `DELETE FROM classify WHERE id IN (` + utils.GetOrmInReplace(len(childIds)) + `)`
+		_, err = tx.Raw(sql, childIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+
+	// 检查是否需要更新父类
+	if c.ParentId > 0 {
+		var count int
+		sql := `SELECT COUNT(*) FROM classify WHERE parent_id=? `
+		err = tx.Raw(sql, c.ParentId).QueryRow(&count)
+		if err != nil {
+			return
+		}
+		if count == 0 {
+			sql = `UPDATE classify SET has_child=0 WHERE id=? `
+			_, err = tx.Raw(sql, c.ParentId).Exec()
+			if err != nil {
+				return
+			}
+		}
+	}
+
+	deleteImgSql := `DELETE FROM banner WHERE classify_id=? `
+	_, err = tx.Raw(deleteImgSql, c.Id).Exec()
+	return
+}
+
 func GetClassifyByName(classifyName string, parentId int) (item *Classify, err error) {
 	sql := `SELECT * FROM classify WHERE classify_name=? AND parent_id=? `
 	o := orm.NewOrmUsingDB("rddp")
@@ -216,6 +266,7 @@ type ClassifyList struct {
 	ChartPermissionIdList []int `description:"绑定的权限ID"`
 	Level                 int   `description:"层级"`
 	HasChild              int   `description:"是否有子级别,0:下面没有子分类,1:下面有子分类;默认:0"`
+	IsEnableDelete        int   `description:"是否允许删除: 0-否; 1-是"`
 }
 
 type ClassifyItem struct {
@@ -376,6 +427,14 @@ func GetAllClassify() (list []*Classify, err error) {
 	return
 }
 
+// GetAllClassifyWithDesc 获取所有倒序分类
+func GetAllClassifyWithDesc() (list []*Classify, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT * FROM classify ORDER BY id DESC `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
 // GetClassifyByKeyword 名称获取分类
 func GetClassifyByKeyword(keyword string) (item Classify, err error) {
 	o := orm.NewOrmUsingDB("rddp")

+ 97 - 0
models/data_manage/base_from_ccf.go

@@ -21,6 +21,7 @@ type BaseFromCCFIndex struct {
 
 type BaseFromCCFIndexList struct {
 	BaseFromCcfIndexId int `orm:"column(base_from_ccf_index_id);pk"`
+	EdbInfoId          int
 	ClassifyId         int
 	IndexCode          string
 	IndexName          string
@@ -29,6 +30,9 @@ type BaseFromCCFIndexList struct {
 	Sort               int
 	CreateTime         string
 	ModifyTime         string
+	EndDate            string
+	EndValue           string
+	EdbExist           int `description:"指标库是否已添加:0-否;1-是"`
 	DataList           []*BaseFromCCFData
 	Paging             *paging.PagingItem `description:"分页数据"`
 }
@@ -142,3 +146,96 @@ func GetBaseFromCCFIndexByIndexCode(indexCode string) (list *BaseFromCCFIndex, e
 	err = o.Raw(sql, indexCode).QueryRow(&list)
 	return
 }
+
+func GetCCFIndexPage(condition string, pars interface{}, startSize, pageSize int) (items []*BaseFromCCFIndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_ccf_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_ccf_index_id asc LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexPageCount(condition string, pars interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_ccf_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type BaseFromCCFIndexSearchList struct {
+	List   []*BaseFromCCFIndexList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type CCFIndexSource2EdbReq struct {
+	EdbCode       string
+	EdbName       string
+	Frequency     string
+	Unit          string
+	ClassifyId    int
+	AdminId       int
+	AdminRealName string
+}
+
+// BatchCheckCCFEdbReq 指标数据结构体
+type BatchCheckCCFEdbReq struct {
+	//IsJoinEdb      int      `form:"IsJoinEdb" description:"是否加到指标库,0:未加到指标库"`
+	Frequencies   string `description:"频度;枚举值:日度、周度、月度、季度、半年度、年度"`
+	Keyword       string `description:"关键字"`
+	ClassifyIds   string `description:"所选品种id列表"`
+	ListAll       bool   `form:"ListAll" json:"ListAll" description:"列表全选"`
+	TradeCodeList string `form:"TradeCodeList" json:"TradeCodeList" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
+}
+
+func GetCCFFrequencyByClassifyId(classifyId int) (items []*GlFrequency, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT frequency FROM base_from_ccf_index WHERE classify_id = ? `
+	sql += ` GROUP BY frequency ORDER BY frequency ASC `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexDataByDataTime(indexCodes []string, startDate, endDate string) (items []*BaseFromCCFData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_ccf_data WHERE  index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) and data_time >=? and data_time <? ORDER BY data_time DESC `
+	_, err = o.Raw(sql, indexCodes, startDate, endDate).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexDataTimePageByCodes(indexCodes []string, startSize, pageSize int) (dataTimes []string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT data_time FROM base_from_ccf_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) GROUP BY data_time ORDER BY data_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, indexCodes, startSize, pageSize).QueryRows(&dataTimes)
+	return
+}
+
+func GetCCFIndexDataTimePageCount(indexCodes []string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(DISTINCT data_time) AS count  FROM base_from_ccf_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `)`
+	err = o.Raw(sql, indexCodes).QueryRow(&count)
+	return
+}
+
+// GetCCFDataDataTimeByIndexId 根据指标id获取指标数据的日期列表
+func GetCCFDataDataTimeByIndexId(indexIdList []int) (items []string, err error) {
+	if len(indexIdList) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT DISTINCT data_time FROM base_from_ccf_data WHERE base_from_ccf_index_id IN (` + utils.GetOrmInReplace(len(indexIdList)) + `) ORDER BY data_time DESC`
+	_, err = o.Raw(sql, indexIdList).QueryRows(&items)
+	return
+}
+
+func GetCCFIndexDataByIndexIdAndDataTime(indexId []int, dataTimeList []string) (items []*BaseFromCCFData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_ccf_data WHERE  base_from_ccf_index_id in (` + utils.GetOrmInReplace(len(indexId)) + `) and data_time in (` + utils.GetOrmInReplace(len(dataTimeList)) + `)  `
+	_, err = o.Raw(sql, indexId, dataTimeList).QueryRows(&items)
+	return
+}

+ 60 - 0
models/data_manage/base_from_rzd_classify.go

@@ -0,0 +1,60 @@
+// Package data_manage @Author gmy 2024/10/21 9:26:00
+package data_manage
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromRzdClassify struct {
+	BaseFromRzdClassifyId int    `orm:"column(base_from_rzd_classify_id);pk"`
+	CreateTime            string `orm:"column(create_time)"`
+	ModifyTime            string `orm:"column(modify_time)"`
+	ClassifyName          string `orm:"column(classify_name)"`
+	ParentId              int    `orm:"column(parent_id)"`
+	Sort                  int    `orm:"column(sort)"`
+	ClassifyNameEn        string `orm:"column(classify_name_en)"`
+}
+
+type BaseFromRzdClassifyResponse struct {
+	BaseFromRzdClassifyId int    `orm:"column(base_from_rzd_classify_id);pk"`
+	CreateTime            string `orm:"column(create_time)"`
+	ModifyTime            string `orm:"column(modify_time)"`
+	ClassifyName          string `orm:"column(classify_name)"`
+	ParentId              int    `orm:"column(parent_id)"`
+	Sort                  int    `orm:"column(sort)"`
+	ClassifyNameEn        string `orm:"column(classify_name_en)"`
+	IndexInfo             []*BaseFromRzdIndex
+	Child                 []*BaseFromRzdClassifyResponse
+}
+
+func init() {
+	orm.RegisterModel(new(BaseFromRzdClassify))
+}
+
+// GetAllRzdClassify 查询所有分类
+func GetAllRzdClassify() (items []*BaseFromRzdClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_rzd_classify ORDER BY sort asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetRzdClassifyItemByClassifyId 根据分类id查询分类信息
+func GetRzdClassifyItemByClassifyId(classifyId int) (item *BaseFromRzdClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_rzd_classify WHERE base_from_rzd_classify_id = ?`
+
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	if err != nil {
+		return
+	}
+	return item, nil
+}
+
+func GetRzdClassifyItemByParentId(parentId int) (items []*BaseFromRzdClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_rzd_classify WHERE parent_id = ?`
+
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}

+ 115 - 0
models/data_manage/base_from_rzd_data.go

@@ -0,0 +1,115 @@
+// Package data_manage @Author gmy 2024/8/7 9:50:00
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type BaseFromRzdData struct {
+	BaseFromRzdDataId  int     `orm:"column(base_from_rzd_data_id);pk"`
+	BaseFromRzdIndexId int     `orm:"column(base_from_rzd_index_id)"`
+	CreateTime         string  `orm:"column(create_time)"`
+	DataTime           string  `orm:"column(data_time)"`
+	IndexCode          string  `orm:"column(index_code)"`
+	ModifyTime         string  `orm:"column(modify_time)"`
+	Value              float64 `orm:"column(value)"`
+}
+
+// RzdIndexAddReq 指标添加vo
+type RzdIndexAddReq struct {
+	EdbCode       string `description:"指标编码"`
+	EdbName       string `description:"指标名称"`
+	Frequency     string `description:"频度"`
+	Unit          string `description:"单位"`
+	ClassifyId    int    `description:"分类ID"`
+	AdminId       int    `description:"管理员ID"`
+	AdminRealName string `description:"管理员名称"`
+}
+
+type RzdIndexDataCountGroup struct {
+	IndexCode string
+	Count     int
+}
+
+func init() {
+	orm.RegisterModel(new(BaseFromRzdData))
+}
+
+func GetRzdIndexDataCountGroup(indexCodes []string) (items []*RzdIndexDataCountGroup, err error) {
+	if len(indexCodes) <= 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count, index_code FROM base_from_rzd_data WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodes)) + `) GROUP BY index_code`
+	_, err = o.Raw(sql, indexCodes).QueryRows(&items)
+	return
+}
+
+func GetRzdIndexData(indexCode string, startSize, pageSize int) (items []*BaseFromRzdData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_rzd_data WHERE index_code=? ORDER BY data_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, indexCode, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetBaseFormRzdDataByIndexCode 根据指标编码查询
+func GetBaseFormRzdDataByIndexCode(indexCode string) (items []*BaseFromRzdData, err error) {
+	sql := `SELECT * FROM base_from_rzd_data WHERE index_code=? ORDER BY data_time desc`
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+	return
+}
+
+func GetBaseFormRzdDataByConditionCount(condition string, pars interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT count(1) FROM base_from_rzd_data WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	if err != nil {
+		return 0, err
+	}
+	return
+}
+
+func GetBaseFormRzdDataByCondition(condition string, pars interface{}) (items []*BaseFromRzdData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_rzd_data WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetRzdDataListByIndexCodes 根据指标编码查询
+func GetRzdDataListByIndexCodes(IndexCodes string) (items []string, err error) {
+	sql := ` SELECT data_time FROM base_from_rzd_data WHERE index_code IN(` + IndexCodes + `)  GROUP BY data_time DESC `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// GetRzdLastUpdateTimeLastByIndexCode 根据指标编码查询 返回ModifyTime最后一条数据
+func GetRzdLastUpdateTimeLastByIndexCode(indexCodes []string) (items []*BaseFromRzdData, err error) {
+	o := orm.NewOrmUsingDB("data")
+
+	// 构造 SQL 查询
+	sql := `SELECT t1.index_code, t1.data_time, t2.value
+			FROM (
+    			SELECT index_code, MAX(data_time) AS data_time
+   				 FROM base_from_rzd_data
+    			WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodes)) + `)
+    			GROUP BY index_code
+			) AS t1
+			JOIN base_from_rzd_data AS t2 ON t1.index_code = t2.index_code AND t1.data_time = t2.data_time`
+
+	// 执行 SQL 查询
+	_, err = o.Raw(sql, indexCodes).QueryRows(&items)
+	if err != nil {
+		return nil, err
+	}
+	return items, nil
+}

+ 213 - 0
models/data_manage/base_from_rzd_index.go

@@ -0,0 +1,213 @@
+// Package data_manage
+// @Author gmy 2024/8/7 9:38:00
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type BaseFromRzdIndex struct {
+	BaseFromRzdIndexId    int    `orm:"column(base_from_rzd_index_id);pk"`
+	CreateTime            string `orm:"column(create_time)"`
+	ModifyTime            string `orm:"column(modify_time)"`
+	BaseFromRzdClassifyId int    `orm:"column(base_from_rzd_classify_id)"`
+	IndexCode             string `orm:"column(index_code)"`
+	IndexName             string `orm:"column(index_name)"`
+	Frequency             string `orm:"column(frequency)"`
+	Unit                  string `orm:"column(unit)"`
+}
+
+type BaseFromRzdIndexAndData struct {
+	BaseFromRzdIndexId    int     `orm:"column(base_from_rzd_index_id);pk"`
+	CreateTime            string  `orm:"column(create_time)"`
+	ModifyTime            string  `orm:"column(modify_time)"`
+	BaseFromRzdClassifyId int     `orm:"column(base_from_rzd_classify_id)"`
+	IndexCode             string  `orm:"column(index_code)"`
+	IndexName             string  `orm:"column(index_name)"`
+	Frequency             string  `orm:"column(frequency)"`
+	Unit                  string  `orm:"column(unit)"`
+	ModifyTimeMax         string  `json:"modify_time_max" description:"最后修改时间"`
+	Value                 float64 `orm:"column(value)" description:"数据值"`
+}
+
+type BaseFromRzdIndexPage struct {
+	List   []*BaseFromRzdIndexAndData `description:"指标列表"`
+	Paging *paging.PagingItem         `description:"分页数据"`
+}
+
+type BaseFromRzdIndexList struct {
+	BaseFromRzdIndexId    int    `orm:"column(base_from_rzd_index_id);pk"`
+	CreateTime            string `orm:"column(create_time)"`
+	ModifyTime            string `orm:"column(modify_time)"`
+	BaseFromRzdClassifyId int    `orm:"column(base_from_rzd_classify_id)"`
+	IndexCode             string `orm:"column(index_code)"`
+	IndexName             string `orm:"column(index_name)"`
+	Frequency             string `orm:"column(frequency)"`
+	Unit                  string `orm:"column(unit)"`
+	DataList              []*BaseFromRzdData
+	Paging                *paging.PagingItem `description:"分页数据"`
+	EdbInfoId             int                `description:"指标库主键id"`
+}
+
+// RzdNameCheckResult 校验名称是否存在
+type RzdNameCheckResult struct {
+	IndexCode string `from:"EdbCode" description:"edb编码"`
+	IndexName string `from:"EdbName" description:"edb名称"`
+	Exist     bool
+}
+
+// RzdIndexCheckData 校验编码是否存在
+type RzdIndexCheckData struct {
+	IndexCode  string `orm:"column(index_code)" description:"指标编码"`
+	IndexName  string `orm:"column(index_name)" description:"指标名称"`
+	Frequency  string `orm:"column(frequency)" description:"频度"`
+	Unit       string `orm:"column(unit)" description:"单位"`
+	EdbInfoId  int    `json:"edb_info_id" description:"指标库主键id"`
+	UniqueCode string `json:"unique_code" description:"指标库唯一编码"`
+	ClassifyId int    `json:"classify_id" description:"分类id"`
+}
+
+// BaseFromRzdIndexBatchAddCheckReq 校验编码是否存在请求参数
+type BaseFromRzdIndexBatchAddCheckReq struct {
+	IndexCodes     []string `form:"IndexCodes" description:"指标编码列表"`
+	ClassifyIdList []int    `description:"分类id"`
+	FrequencyList  []string `description:"频度"`
+	SearchParams   string   `description:"搜索参数 指标编码/指标名称"`
+	IsCheckAll     bool     `description:"是否全选"`
+}
+
+func init() {
+	orm.RegisterModel(new(BaseFromRzdIndex))
+}
+
+// GetRzdIndexByClassifyIds 根据分类id获取指标信息
+func GetRzdIndexByClassifyIds(classifyIds []int) (items []*BaseFromRzdIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+
+	sql := fmt.Sprintf(`SELECT * FROM base_from_rzd_index WHERE base_from_rzd_classify_id IN (`+utils.GetOrmInReplace(len(classifyIds))) + `)`
+	_, err = o.Raw(sql, classifyIds).QueryRows(&items)
+	if err != nil {
+		return nil, err
+	}
+
+	return items, nil
+}
+
+func GetRzdIndex(condition string, pars interface{}) (items []*BaseFromRzdIndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_rzd_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY base_from_rzd_index_id asc`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func GetRzdIndexNotExistEdbInfoCount(condition string, pars interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT count(1) FROM base_from_rzd_index WHERE index_code not in (select edb_code from edb_info) `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetRzdIndexNotExistEdbInfoPage(condition string, pars interface{}) (items []*BaseFromRzdIndexAndData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_rzd_index WHERE index_code not in (select edb_code from edb_info) `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetRzdIndexFrequency 获取指标频度
+func GetRzdIndexFrequency(classifyIdList []int) (items []*string, err error) {
+	sql := `SELECT DISTINCT frequency 
+        FROM base_from_rzd_index 
+        WHERE frequency is not null`
+
+	// 如果 classifyId > 0,则添加该条件
+	if len(classifyIdList) > 0 {
+		sql += ` AND base_from_rzd_classify_id in (` + utils.GetOrmInReplace(len(classifyIdList)) + `)`
+	}
+
+	sql += ` ORDER BY FIELD(frequency, '日度', '周度', '月度', '季度', '半年度', '年度')`
+
+	o := orm.NewOrmUsingDB("data")
+	if len(classifyIdList) > 0 {
+		_, err = o.Raw(sql, classifyIdList).QueryRows(&items)
+	} else {
+		_, err = o.Raw(sql).QueryRows(&items)
+	}
+
+	return items, err
+}
+
+// GetRzdIndexByCodeAndClassify 根据指标编码和分类查询 indexCode非必传
+func GetRzdIndexByCodeAndClassify(indexCode string, classifyIdList []int, frequency *string) (items []*BaseFromRzdIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+
+	// SQL 查询语句
+	sql := `SELECT a.index_code, a.index_name, a.frequency, a.unit, MAX(b.modify_time) AS modify_time
+			FROM base_from_rzd_index AS a
+			INNER JOIN base_from_rzd_data AS b ON a.base_from_rzd_index_id = b.base_from_rzd_index_id
+			WHERE 1=1`
+
+	var params []interface{}
+
+	if len(classifyIdList) > 0 {
+		sql += ` AND a.base_from_rzd_classify_id in (` + utils.GetOrmInReplace(len(classifyIdList)) + `)`
+		for _, id := range classifyIdList {
+			params = append(params, id)
+		}
+	}
+
+	// 如果 indexCode 不为空,增加过滤条件
+	if indexCode != "" {
+		sql += ` AND a.index_code = ?`
+		params = append(params, indexCode)
+	}
+
+	if frequency != nil {
+		sql += ` AND a.frequency = ?`
+		params = append(params, *frequency)
+	}
+
+	sql += ` GROUP BY a.index_code, a.index_name, a.frequency, a.unit`
+
+	_, err = o.Raw(sql, params...).QueryRows(&items)
+	if err != nil {
+		return nil, err
+	}
+	return
+}
+
+// GetRzdIndexInfoCount 分页查询指标信息行数
+func GetRzdIndexInfoCount(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT count(1) FROM base_from_rzd_index WHERE index_code not in (select edb_code from edb_info) `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetRzdIndexInfoPage 分页查询指标信息
+func GetRzdIndexInfoPage(condition string, pars []interface{}) (items []*BaseFromRzdIndexAndData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_rzd_index WHERE index_code not in (select edb_code from edb_info) `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+
+}

+ 2 - 0
models/data_manage/base_from_sci.go

@@ -65,6 +65,8 @@ type BaseFromSciIndex struct {
 	Sort               int
 	CreateTime         time.Time
 	ModifyTime         time.Time
+	FilePath           string
+	TerminalCode       string `description:"终端编码"`
 }
 
 func AddBaseFromSciIndex(item *BaseFromSciIndex) (lastId int64, err error) {

+ 8 - 6
models/data_manage/base_from_smm.go

@@ -77,6 +77,8 @@ type BaseFromSmmIndex struct {
 	ReleaseTime        string
 	StartDate          string
 	EndDate            string
+	RenameFileName     string
+	TerminalCode       string `description:"终端编码"`
 }
 
 func AddBaseFromSmmIndex(item *BaseFromSmmIndex) (lastId int64, err error) {
@@ -440,10 +442,10 @@ type SmmIndexListReq struct {
 
 // SmmIndexExistCheckReq
 type SmmIndexExistCheckReq struct {
-	Types      []string `description:"分类"`
-	Frequency  string   `description:"频度"`
-	DataState  string   `description:"数据状态"`
-	EdbCode    string   `description:"指标代码"`
-	SelectAll  bool     `description:"是否全选"`
-	Keyword    string   `description:"关键字"`
+	Types     []string `description:"分类"`
+	Frequency string   `description:"频度"`
+	DataState string   `description:"数据状态"`
+	EdbCode   string   `description:"指标代码"`
+	SelectAll bool     `description:"是否全选"`
+	Keyword   string   `description:"关键字"`
 }

+ 272 - 0
models/data_manage/base_from_usda_fas.go

@@ -0,0 +1,272 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type BaseFromUsdaFasIndex struct {
+	BaseFromUsdaFasIndexId int `orm:"column(base_from_usda_fas_index_id);pk"`
+	ClassifyId             int
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	Sort                   int
+	StartDate              string `description:"开始日期"`
+	EndDate                string `description:"结束日期"`
+	EndValue               float64
+	CreateTime             time.Time
+	ModifyTime             time.Time
+}
+
+type BaseFromUsdaFasIndexList struct {
+	BaseFromUsdaFasIndexId int `orm:"column(base_from_usda_fas_index_id);pk"`
+	ClassifyId             int
+	Interface              string
+	EdbInfoId              int
+	EdbUniqueCode          string `description:"指标库唯一编码"`
+	EdbClassifyId          int    `description:"指标库分类ID"`
+	StartDate              string
+	EndDate                string
+	EndValue               float64
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	Sort                   int
+	CreateTime             string
+	ModifyTime             string
+	EdbExist               int `description:"指标库是否已添加:0-否;1-是"`
+	DataList               []*BaseFromUsdaFasData
+	Paging                 *paging.PagingItem `description:"分页数据"`
+}
+type BaseFromUsdaFasIndexSearchList struct {
+	List   []*BaseFromUsdaFasIndexList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+type UsdaFasSingleDataResp struct {
+	BaseFromUsdaFasIndexId int
+	ClassifyId             int
+	EdbInfoId              int
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	StartTime              string
+	CreateTime             string
+	ModifyTime             string
+	EdbExist               int `description:"指标库是否已添加:0-否;1-是"`
+	Data                   []*UsdaFasSingleData
+}
+
+type UsdaFasSingleData struct {
+	Value    string `orm:"column(value)" description:"日期"`
+	DataTime string `orm:"column(data_time)" description:"值"`
+}
+
+func GetUsdaFasIndexByClassifyId(classifyId int) (items []*BaseFromUsdaFasIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT base_from_usda_fas_index_id, classify_id, index_code, index_name FROM base_from_usda_fas_index WHERE classify_id=? ORDER BY sort ASC, base_from_usda_fas_index_id ASC `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}
+
+func GetUsdaFasIndex(condition string, pars interface{}) (items []*BaseFromUsdaFasIndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_usda_fas_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_usda_fas_index_id asc`
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func GetUsdaFasIndexPage(condition string, pars interface{}, startSize, pageSize int) (items []*BaseFromUsdaFasIndexList, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_usda_fas_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_usda_fas_index_id asc LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetUsdaFasIndexPageCount(condition string, pars interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_usda_fas_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetUsdaFasIndexDataCount(indexCode string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_usda_fas_data WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&count)
+	return
+}
+
+func GetUsdaFasIndexDataByDataTime(indexCodes []string, startDate, endDate string) (items []*BaseFromUsdaFasData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_usda_fas_data WHERE  index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) and data_time >=? and data_time <? ORDER BY data_time DESC `
+	_, err = o.Raw(sql, indexCodes, startDate, endDate).QueryRows(&items)
+	return
+}
+
+func GetUsdaFasIndexDataTimePageByCodes(indexCodes []string, startSize, pageSize int) (dataTimes []string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT data_time FROM base_from_usda_fas_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) GROUP BY data_time ORDER BY data_time DESC LIMIT ?,? `
+	_, err = o.Raw(sql, indexCodes, startSize, pageSize).QueryRows(&dataTimes)
+	return
+}
+
+func GetUsdaFasIndexDataTimePageCount(indexCodes []string) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT COUNT(DISTINCT data_time) AS count  FROM base_from_usda_fas_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) `
+	err = o.Raw(sql, indexCodes).QueryRow(&count)
+	return
+}
+
+func GetUsdaFasIndexDataByCodes(indexCode []string) (items []*BaseFromUsdaFasData, err error) {
+	if len(indexCode) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT *  FROM base_from_usda_fas_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCode)) + `) ORDER BY data_time DESC  `
+	_, err = o.Raw(sql, indexCode).QueryRows(&items)
+	return
+}
+
+// GetUsdaFasByConditionAndFrequency 根据条件获取涌益咨询指标列表
+func GetUsdaFasByConditionAndFrequency(condition, frequency string, pars []interface{}) (items []*BaseFromUsdaFasIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_usda_fas_index WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` AND frequency=?`
+	sql += ` ORDER BY sort ASC, base_from_usda_fas_index_id ASC`
+	_, err = o.Raw(sql, pars, frequency).QueryRows(&items)
+	return
+}
+
+func GetUsdaFasFrequencyByCondition(condition string, pars []interface{}) (items []string, err error) {
+	sql := `SELECT DISTINCT frequency FROM base_from_usda_fas_index WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY FIELD(frequency,'日度','周度','旬度','月度','季度','半年度','年度') `
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql, pars...).QueryRows(&items)
+	return
+}
+
+// GetUsdaFasDataDataTimeByIndexId 根据指标id获取指标数据的日期列表
+func GetUsdaFasDataDataTimeByIndexId(indexIdList []int) (items []string, err error) {
+	if len(indexIdList) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT DISTINCT data_time FROM base_from_usda_fas_data WHERE base_from_usda_fas_index_id IN (` + utils.GetOrmInReplace(len(indexIdList)) + `) ORDER BY data_time DESC`
+	_, err = o.Raw(sql, indexIdList).QueryRows(&items)
+	return
+}
+
+type BaseFromUsdaFasData struct {
+	BaseFromUsdaFasDataId  int `orm:"column(base_from_usda_fas_data_id);pk"`
+	BaseFromUsdaFasIndexId int
+	IndexCode              string
+	DataTime               string
+	Value                  string
+	CreateTime             string
+	ModifyTime             string
+	DataTimestamp          int64
+}
+
+type BaseFromUsdaFasIndexSearchItem struct {
+	BaseFromUsdaFasIndexId int `orm:"column(base_from_usda_fas_index_id);pk"`
+	ClassifyId             int
+	ParentClassifyId       int
+	IndexCode              string
+	IndexName              string
+}
+
+// BatchCheckUsdaFasEdbReq 指标数据结构体
+type BatchCheckUsdaFasEdbReq struct {
+	//IsJoinEdb      int      `form:"IsJoinEdb" description:"是否加到指标库,0:未加到指标库"`
+	Frequencies   string `description:"频度;枚举值:日度、周度、月度、季度、半年度、年度"`
+	Keyword       string `description:"关键字"`
+	ClassifyIds   string `description:"所选品种id列表"`
+	ListAll       bool   `form:"ListAll" json:"ListAll" description:"列表全选"`
+	TradeCodeList string `form:"TradeCodeList" json:"TradeCodeList" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
+}
+
+// GetUsdaFasItemList 模糊查询UsdaFas数据库指标列表
+func GetUsdaFasItemList(condition string) (items []*BaseFromUsdaFasIndexSearchItem, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM base_from_usda_fas_index  WHERE 1=1"
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetUsdaFasIndexDataByCode(indexCode string) (list []*BaseFromUsdaFasData, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_usda_fas_data WHERE index_code=? `
+	_, err = o.Raw(sql, indexCode).QueryRows(&list)
+	return
+}
+
+func GetBaseFromUsdaFasIndexByIndexCode(indexCode string) (list *BaseFromUsdaFasIndex, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_usda_fas_index WHERE index_code=? `
+	err = o.Raw(sql, indexCode).QueryRow(&list)
+	return
+}
+
+type BaseFromUsdaFasIndexType struct {
+	Type2 string `orm:"column(type_2)"`
+	Type3 string `orm:"column(type_3)"`
+}
+
+// Update 更新UsdaFas指标基础信息
+func (item *BaseFromUsdaFasIndex) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(item, cols...)
+	return
+}
+
+// EditUsdaFasIndexInfoResp 新增指标的返回
+type EditUsdaFasIndexInfoResp struct {
+	BaseFromUsdaFasIndexId int    `description:"指标ID"`
+	IndexCode              string `description:"指标code"`
+}
+
+type UsdaFasIndexSource2EdbReq struct {
+	EdbCode       string
+	EdbName       string
+	Frequency     string
+	Unit          string
+	ClassifyId    int
+	AdminId       int
+	AdminRealName string
+}
+
+func GetUsdaFasFrequencyByClassifyId(classifyId int) (items []*GlFrequency, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT frequency FROM base_from_usda_fas_index WHERE classify_id = ? `
+	sql += ` GROUP BY frequency ORDER BY frequency ASC `
+	_, err = o.Raw(sql, classifyId).QueryRows(&items)
+	return
+}

+ 238 - 0
models/data_manage/base_from_usda_fas_classify.go

@@ -0,0 +1,238 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+// BaseFromUsdaFasClassify UsdaFas原始数据分类表
+type BaseFromUsdaFasClassify struct {
+	ClassifyId      int       `orm:"column(classify_id);pk"`
+	ClassifyName    string    `description:"分类名称"`
+	ParentId        int       `description:"父级id"`
+	SysUserId       int       `description:"创建人id"`
+	SysUserRealName string    `description:"创建人姓名"`
+	Level           int       `description:"层级"`
+	Sort            int       `description:"排序字段,越小越靠前,默认值:10"`
+	ModifyTime      time.Time `description:"修改时间"`
+	CreateTime      time.Time `description:"创建时间"`
+}
+
+// AddBaseFromUsdaFasClassify 添加UsdaFas原始数据分类
+func AddBaseFromUsdaFasClassify(item *BaseFromUsdaFasClassify) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	lastId, err = o.Insert(item)
+	return
+}
+
+// GetBaseFromUsdaFasClassifyCount 获取分类名称的个数
+func GetBaseFromUsdaFasClassifyCount(classifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM base_from_usda_fas_classify WHERE classify_name=? AND parent_id=? `
+	err = o.Raw(sql, classifyName, parentId).QueryRow(&count)
+	return
+}
+
+// GetBaseFromUsdaFasClassifyById 通过分类id的获取分类信息
+func GetBaseFromUsdaFasClassifyById(classifyId int) (item *BaseFromUsdaFasClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_usda_fas_classify WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+// GetBaseFromUsdaFasClassifyById 通过分类id的获取分类信息
+func GetBaseFromUsdaFasClassifyByIds(classifyIds []int) (items []*BaseFromUsdaFasClassify, err error) {
+	if len(classifyIds) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM base_from_usda_fas_classify WHERE classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) `
+	_, err = o.Raw(sql, classifyIds).QueryRows(&items)
+	return
+}
+
+// EditBaseFromUsdaFasClassify 修改UsdaFas原始数据分类
+func EditBaseFromUsdaFasClassify(classifyId int, classifyName string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE base_from_usda_fas_classify SET classify_name=?,modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, classifyName, classifyId).Exec()
+	return
+}
+
+// UpdateBaseFromUsdaFasClassifySort 修改UsdaFas原始数据分类的排序
+func UpdateBaseFromUsdaFasClassifySort(classifyId int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE base_from_usda_fas_classify SET sort=classify_id, modify_time=NOW() WHERE classify_id=? `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+type BaseFromUsdaFasClassifyItems struct {
+	ClassifyId             int    `description:"分类ID"`
+	BaseFromUsdaFasIndexId int    `description:"指标类型ID"`
+	IndexCode              string `description:"指标唯一编码"`
+	ClassifyName           string `description:"分类名称"`
+	ClassifyNameEn         string `description:"分类名称"`
+	UniqueCode             string `description:"分类唯一编码"`
+	ParentId               int    `description:"父级id"`
+	Level                  int    `description:"层级"`
+	Sort                   int    `description:"排序字段,越小越靠前,默认值:10"`
+	Children               []*BaseFromUsdaFasClassifyItems
+}
+
+type BaseFromUsdaFasClassifyNameItems struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级id"`
+}
+
+type BaseFromUsdaFasClassifyResp struct {
+	List []*BaseFromUsdaFasClassifyItems
+}
+
+type BaseFromUsdaFasClassifyNameResp struct {
+	List []*BaseFromUsdaFasClassifyNameItems
+}
+
+type BaseFromUsdaFasClassifyItemsButton struct {
+	AddButton    bool `description:"是否可添加"`
+	OpButton     bool `description:"是否可编辑"`
+	DeleteButton bool `description:"是否可删除"`
+	MoveButton   bool `description:"是否可移动"`
+}
+
+// GetBaseFromUsdaFasClassifyByParentId 根据上级id获取当下的分类列表数据
+func GetBaseFromUsdaFasClassifyByParentId(parentId int) (items []*BaseFromUsdaFasClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_usda_fas_classify WHERE parent_id=? order by sort asc,classify_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetAllBaseFromUsdaFasClassify 获取所有的分类列表数据
+func GetAllBaseFromUsdaFasClassify() (items []*BaseFromUsdaFasClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_usda_fas_classify order by parent_id asc, sort asc,classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type DeleteBaseFromUsdaFasClassifyReq struct {
+	ClassifyId int `description:"分类id"`
+	EdbInfoId  int `description:"指标id"`
+}
+
+type BaseFromUsdaFasClassifyListResp struct {
+	AllNodes      []*BaseFromUsdaFasClassifyItems
+	CanOpClassify bool `description:"是否允许操作分类"`
+}
+
+type BaseFromUsdaFasClassifySimplify struct {
+	ClassifyId   int    `description:"分类id"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int
+}
+
+// GetFirstBaseFromUsdaFasClassify 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstBaseFromUsdaFasClassify() (item *BaseFromUsdaFasClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM base_from_usda_fas_classify order by sort asc,classify_id asc limit 1`
+	err = o.Raw(sql).QueryRow(&item)
+	return
+}
+
+// UpdateBaseFromUsdaFasClassifySortByClassifyId 根据分类id更新排序
+func UpdateBaseFromUsdaFasClassifySortByClassifyId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update base_from_usda_fas_classify set sort = ` + updateSort + ` WHERE parent_id=? AND sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( classify_id > ` + fmt.Sprint(classifyId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// MoveUpUsdaFasIndexClassifyBySort 往上移动
+func MoveUpUsdaFasIndexClassifyBySort(parentId, nextSort, currentSort int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `update base_from_usda_fas_classify set sort = sort + 1 where parent_id=? and sort >= ? and sort< ?`
+	_, err = o.Raw(sql, parentId, nextSort, currentSort).Exec()
+	return
+}
+
+// MoveDownUsdaFasIndexClassifyBySort 往下移动
+func MoveDownUsdaFasIndexClassifyBySort(parentId, prevSort, currentSort int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `update base_from_usda_fas_classify set sort = sort - 1 where parent_id=? and sort <= ? and sort> ? `
+	_, err = o.Raw(sql, parentId, prevSort, currentSort).Exec()
+	return
+}
+
+// GetUsdaFasIndexClassifyMinSort 获取最小不等于0的排序
+func GetUsdaFasIndexClassifyMinSort(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `select min(sort) from base_from_usda_fas_classify where parent_id=? and sort <> 0 `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+// Update 更新分类基础信息
+func (BaseFromUsdaFasClassify *BaseFromUsdaFasClassify) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(BaseFromUsdaFasClassify, cols...)
+	return
+}
+
+type AddUsdaFasClassifyResp struct {
+	ClassifyId int
+}
+
+// DeleteUsdaFasClassifyByClassifyId 根据分类id删除对应的指标分类
+func DeleteUsdaFasClassifyByClassifyId(classifyIdList []int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	num := len(classifyIdList)
+	if num <= 0 {
+		return
+	}
+	//删除分类
+	sql := `DELETE FROM base_from_usda_fas_classify WHERE classify_id IN (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, classifyIdList).Exec()
+	return
+}
+
+// AddUsdaFasClassifyMulti 批量新增SMM类别
+func AddUsdaFasClassifyMulti(list []*BaseFromUsdaFasClassify) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(1, list)
+	return
+}
+
+// InitUsdaFasClassifySort 初始化sort值
+func InitUsdaFasClassifySort() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE base_from_usda_fas_classify 
+SET modify_time=NOW(), sort = classify_id`
+	_, err = o.Raw(sql).Exec()
+	return
+}
+
+// InitUsdaFasIndexClassifyId 历史数据的classifyId值
+func InitUsdaFasIndexClassifyId() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE base_from_UsdaFasindex s
+LEFT JOIN (
+SELECT
+	c1.classify_id,
+	CONCAT( c2.classify_name, c1.classify_name ) AS type_name 
+FROM
+	base_from_usda_fas_classify c1
+	LEFT JOIN base_from_usda_fas_classify c2 ON c1.parent_id = c2.classify_id 
+	) AS t ON CONCAT( s.type_2, s.type_3 ) = t.type_name
+	SET s.classify_id = t.classify_id, s.modify_time=NOW() where s.type_2 <>""`
+	_, err = o.Raw(sql).Exec()
+	return
+}

+ 50 - 6
models/data_manage/chart_classify.go

@@ -3,8 +3,9 @@ package data_manage
 import (
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/beego/beego/v2/client/orm"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
 )
 
 type ChartClassify struct {
@@ -183,6 +184,14 @@ func GetChartClassifyAll(source int) (items []*ChartClassifyItems, err error) {
 	return
 }
 
+// GetChartClassifyBySource 获取图表分类列表
+func GetChartClassifyBySource(source int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM chart_classify WHERE source = ? ORDER BY level ASC,chart_classify_id ASC`
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}
+
 type ChartClassifyItems struct {
 	ChartClassifyId     int `description:"分类id"`
 	ChartInfoId         int `description:"指标id"`
@@ -466,6 +475,25 @@ func GetChartClassifyAllBySource(source int) (items []*ChartClassifyItems, err e
 	return
 }
 
+// GetChartClassifyIdListByAdminId
+// @Description: 根据用户id和指标类型获取其关联的所有指标分类id列表
+// @author: Roc
+// @datetime 2024-09-11 15:52:48
+// @param adminId int
+// @param source int
+// @return chartClassifyIdList []int
+// @return err error
+func GetChartClassifyIdListByAdminId(adminId, source int) (chartClassifyIdList []int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT chart_classify_id FROM chart_info WHERE sys_user_id = ? AND source = ?`
+
+	pars := []interface{}{adminId, source}
+	sql += ` group by chart_classify_id `
+	_, err = o.Raw(sql, pars).QueryRows(&chartClassifyIdList)
+
+	return
+}
+
 // GetChartClassifyAndInfoByParentId
 func GetChartClassifyAndInfoByParentId(parentId int) (items []*ChartClassifyItems, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -481,7 +509,15 @@ func GetChartClassifyAndInfoByParentId(parentId int) (items []*ChartClassifyItem
 	sys_user_real_name,
 	sort,
 	level,
-	unique_code
+	unique_code,
+	source,
+	0 as date_type,
+	'' as start_date,
+	'' as end_date,
+	0 as chart_type,
+	'' as calendar,
+	'' as season_start_date,
+	'' as season_end_date
 FROM
 	chart_classify 
 WHERE
@@ -498,7 +534,15 @@ SELECT
 	sys_user_real_name,
 	sort,
 	0 AS level,
-	unique_code
+	unique_code,
+	source,
+	date_type,
+	start_date,
+	end_date,
+	chart_type,
+	calendar,
+	season_start_date,
+	season_end_date
 FROM
 	chart_info 
 WHERE
@@ -546,7 +590,7 @@ SELECT
 FROM
 	chart_info 
 WHERE
-	chart_classify_id = ? AND chart_type = 1 AND sys_user_id = ?
+	chart_classify_id = ? AND sys_user_id = ? AND source = 1
 ORDER BY
 	sort ASC,
 	chart_classify_id ASC`
@@ -566,6 +610,6 @@ func GetChartClassifiesById(chartClassifyId int) (items []*ChartClassifyItems, e
         FROM chart_classify
         WHERE parent_id = ?
     );`
-	_,err = o.Raw(sql, chartClassifyId, chartClassifyId, chartClassifyId).QueryRows(&items)
+	_, err = o.Raw(sql, chartClassifyId, chartClassifyId, chartClassifyId).QueryRows(&items)
 	return
-}
+}

+ 19 - 7
models/data_manage/chart_info.go

@@ -5,12 +5,13 @@ import (
 	"eta/eta_api/models/mgo"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/beego/beego/v2/client/orm"
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"go.mongodb.org/mongo-driver/bson"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"go.mongodb.org/mongo-driver/bson"
 )
 
 type ChartInfo struct {
@@ -2172,10 +2173,12 @@ type BarChartInfoReq struct {
 }
 
 type FutureGoodBarChartInfoReq struct {
-	EdbInfoIdList []BarChartInfoEdbItemReq `description:"指标信息"`
-	DateList      []BarChartInfoDateReq    `description:"日期配置"`
-	XDataList     []XData                  `description:"横轴配置"`
-	BaseEdbInfoId int                      `description:"日期基准指标id"`
+	EdbInfoIdList       []BarChartInfoEdbItemReq `description:"指标信息"`
+	DateList            []BarChartInfoDateReq    `description:"日期配置"`
+	XDataList           []XData                  `description:"横轴配置"`
+	BaseEdbInfoId       int                      `description:"日期基准指标id"`
+	FutureGoodEdbName   string                   `description:"期货名称"`
+	FutureGoodEdbNameEn string                   `description:"期货英文名称"`
 }
 
 // BarChartInfoEdbItemReq 柱方图预览请求数据(指标相关)
@@ -2788,6 +2791,15 @@ func GetChartInfoBySourceAndParentId(source, parentId, adminId int) (items []*Ch
 	return
 }
 
+func GetChartInfoBySourceAndAdminId(source, adminId int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT chart_info_id,chart_classify_id,chart_name AS chart_classify_name,chart_name_en AS chart_classify_name_en,
+             unique_code,sys_user_id,sys_user_real_name,date_type,start_date,end_date,chart_type,calendar,season_start_date,season_end_date,source
+            FROM chart_info WHERE source = ? AND sys_user_id = ? ORDER BY sort asc,chart_info_id ASC `
+	_, err = o.Raw(sql, source, adminId).QueryRows(&items)
+	return
+}
+
 // PreviewSeasonChartReq 预览季节性图的请求入参
 type PreviewSeasonChartReq struct {
 	ChartEdbInfoList  []*ChartSaveItem `description:"指标及配置信息"`

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

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

+ 2 - 0
models/data_manage/edb_data_base.go

@@ -185,6 +185,8 @@ func GetEdbDataTableName(source, subSource int) (tableName string) {
 		tableName = "edb_data_ly"
 	case utils.DATA_SOURCE_TRADE_ANALYSIS: // 持仓分析->92
 		tableName = "edb_data_trade_analysis"
+	case utils.DATA_SOURCE_CALCULATE_STL:
+		tableName = "edb_data_calculate_stl"
 	default:
 		edbSource := EdbSourceIdMap[source]
 		if edbSource != nil {

+ 6 - 6
models/data_manage/edb_data_wind.go

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

+ 59 - 34
models/data_manage/edb_info.go

@@ -38,36 +38,37 @@ type EdbInfo struct {
 	CreateTime       time.Time
 	ModifyTime       time.Time
 	BaseModifyTime   time.Time
-	MinValue         float64 `description:"指标最小值"`
-	MaxValue         float64 `description:"指标最大值"`
-	CalculateFormula string  `description:"计算公式"`
-	EdbType          int     `description:"指标类型:1:基础指标,2:计算指标"`
-	Sort             int     `description:"排序字段"`
-	LatestDate       string  `description:"数据最新日期(实际日期)"`
-	LatestValue      float64 `description:"数据最新值(实际值)"`
-	EndValue         float64 `description:"数据的最新值(预测日期的最新值)"`
-	MoveType         int     `description:"移动方式:1:领先(默认),2:滞后"`
-	MoveFrequency    string  `description:"移动频度"`
-	NoUpdate         int8    `description:"是否停止更新,0:继续更新;1:停止更新"`
-	ServerUrl        string  `description:"服务器地址"`
-	ChartImage       string  `description:"图表图片"`
-	Calendar         string  `description:"公历/农历" orm:"default(公历);"`
-	DataDateType     string  `orm:"column(data_date_type);size(255);null;default(交易日)"`
-	ManualSave       int     `description:"是否有手动保存过上下限: 0-否; 1-是"`
-	EmptyType        int     `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
-	MaxEmptyType     int     `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
-	TerminalCode     string  `description:"终端编码,用于配置在机器上"`
-	DataUpdateTime   string  `description:"最近一次数据发生变化的时间"`
-	ErDataUpdateDate string  `description:"本次更新,数据发生变化的最早日期"`
-	SourceIndexName  string  `description:"数据源中的指标名称"`
-	SubSource        int     `description:"子数据来源:0:经济数据库,1:日期序列"`
-	SubSourceName    string  `description:"子数据来源名称"`
-	IndicatorCode    string  `description:"指标代码"`
-	StockCode        string  `description:"证券代码"`
-	Extra            string  `description:"指标额外配置"`
-	IsJoinPermission int     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
-	IsStaticData     int     `description:"是否是静态指标,0否,1是"`
-	EndDateType      int     `description:"预测指标截止日期类型:0:未来日期,1未来期数"`
+	MinValue         float64   `description:"指标最小值"`
+	MaxValue         float64   `description:"指标最大值"`
+	CalculateFormula string    `description:"计算公式"`
+	EdbType          int       `description:"指标类型:1:基础指标,2:计算指标"`
+	Sort             int       `description:"排序字段"`
+	LatestDate       string    `description:"数据最新日期(实际日期)"`
+	LatestValue      float64   `description:"数据最新值(实际值)"`
+	EndValue         float64   `description:"数据的最新值(预测日期的最新值)"`
+	MoveType         int       `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string    `description:"移动频度"`
+	NoUpdate         int8      `description:"是否停止更新,0:继续更新;1:停止更新"`
+	ServerUrl        string    `description:"服务器地址"`
+	ChartImage       string    `description:"图表图片"`
+	Calendar         string    `description:"公历/农历" orm:"default(公历);"`
+	DataDateType     string    `orm:"column(data_date_type);size(255);null;default(交易日)"`
+	ManualSave       int       `description:"是否有手动保存过上下限: 0-否; 1-是"`
+	EmptyType        int       `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int       `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	TerminalCode     string    `description:"终端编码,用于配置在机器上"`
+	DataUpdateTime   string    `description:"最近一次数据发生变化的时间"`
+	ErDataUpdateDate string    `description:"本次更新,数据发生变化的最早日期"`
+	SourceIndexName  string    `description:"数据源中的指标名称"`
+	SubSource        int       `description:"子数据来源:0:经济数据库,1:日期序列"`
+	SubSourceName    string    `description:"子数据来源名称"`
+	IndicatorCode    string    `description:"指标代码"`
+	StockCode        string    `description:"证券代码"`
+	Extra            string    `description:"指标额外配置"`
+	IsJoinPermission int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	IsStaticData     int       `description:"是否是静态指标,0否,1是"`
+	SetUpdateTime    time.Time `description:"设置启用/禁用刷新状态的时间"`
+	EndDateType      int       `description:"预测指标截止日期类型:0:未来日期,1未来期数"`
 }
 
 type EdbInfoFullClassify struct {
@@ -226,6 +227,11 @@ func GetEdbInfoByIdList(edbInfoIdList []int) (items []*EdbInfo, err error) {
 
 // BatchAddCheckReq 指标批量添加校验
 type BatchAddCheckReq struct {
+	IndexCodes []string `form:"IndexCodes" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
+}
+
+// MysteelChemicalDataBatchAddCheckReq 钢联化工指标批量添加校验
+type MysteelChemicalDataBatchAddCheckReq struct {
 	// MysteelChemicalDataListReq
 	IndexCodes []string `form:"IndexCodes" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
 }
@@ -312,6 +318,12 @@ func DeleteEdbInfoAndData(edbInfoId, source, subSource int) (err error) {
 	if err != nil {
 		return
 	}
+	// 删除stl趋势分解指标的映射关系
+	sql = ` DELETE FROM calculate_stl_config_mapping WHERE edb_info_id=?`
+	_, err = to.Raw(sql, edbInfoId).Exec()
+	if err != nil {
+		return
+	}
 	return
 }
 
@@ -457,6 +469,10 @@ type EdbInfoList struct {
 	HaveOperaAuth    bool                    `description:"是否有数据权限,默认:false"`
 	IsStaticData     int                     `description:"是否是静态指标,0否,1是"`
 	IsSupplierStop   int                     `description:"是否供应商停更:1:停更,0:未停更"`
+	MoveType         int                     `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string                  `description:"移动频度"`
+	MinValue         float64                 `description:"最小值"`
+	MaxValue         float64                 `description:"最大值"`
 }
 
 type EdbDataInsertConfigItem struct {
@@ -508,6 +524,9 @@ func GetEdbDataCountByCondition(condition string, pars []interface{}, source, su
 func GetEdbDataListByCondition(condition string, pars []interface{}, source, subSource, pageSize, startSize int) (item []*EdbData, err error) {
 	o := orm.NewOrmUsingDB("data")
 	tableName := GetEdbDataTableName(source, subSource)
+	if source == utils.DATA_SOURCE_PREDICT {
+		tableName = "edb_data_predict_base"
+	}
 	sql := ` SELECT * FROM %s WHERE 1=1 `
 	sql = fmt.Sprintf(sql, tableName)
 
@@ -1806,15 +1825,15 @@ func ModifyEdbInfoUpdateStatus(edbIdList []int, isStop int, calculateEdbInfoIds
 	}()
 
 	// 更改指标的更新状态
-	sql := ` UPDATE edb_info SET no_update = ? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(idNum) + `) `
-	_, err = o.Raw(sql, isStop, edbIdList).Exec()
+	sql := ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(idNum) + `) `
+	_, err = o.Raw(sql, isStop, time.Now(), edbIdList).Exec()
 	if err != nil {
 		return
 	}
 	if len(calculateEdbInfoIds) > 0 {
 		// 批量更新相关联的指标ID
-		sql = ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
-		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), calculateEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -2109,3 +2128,9 @@ func getThsHfAllDataByMongo(edbInfoId, source, subSource int, startDataTime stri
 
 	return
 }
+
+type EdbNameCheckResult struct {
+	EdbCode string
+	EdbName string
+	Exist   bool
+}

+ 14 - 1
models/data_manage/edb_info_calculate_mapping.go

@@ -2,8 +2,9 @@ package data_manage
 
 import (
 	"eta/eta_api/utils"
-	"github.com/beego/beego/v2/client/orm"
 	"time"
+
+	"github.com/beego/beego/v2/client/orm"
 )
 
 // EdbInfoCalculateMapping 计算指标于基础指标,关系表
@@ -24,6 +25,12 @@ type EdbInfoCalculateMapping struct {
 	ModifyTime                time.Time `description:"修改时间"`
 }
 
+func (e *EdbInfoCalculateMapping) Insert() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(e)
+	return
+}
+
 func AddEdbInfoCalculateMappingMulti(items []*EdbInfoCalculateMapping) (err error) {
 	o := orm.NewOrmUsingDB("data")
 	_, err = o.InsertMulti(1, items)
@@ -93,6 +100,12 @@ type EdbInfoCalculateMappingInfo struct {
 	NoUpdate                  int8      `description:"是否停止更新,0:继续更新;1:停止更新"`
 }
 
+func (e *EdbInfoCalculateMappingInfo) Insert() (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Insert(e)
+	return
+}
+
 // GetEdbInfoCalculateMappingListByEdbInfoId 根据生成的指标id获取来源的指标id列表
 func GetEdbInfoCalculateMappingListByEdbInfoId(edbInfoId int) (items []*EdbInfoCalculateMappingInfo, err error) {
 	o := orm.NewOrmUsingDB("data")

+ 11 - 11
models/data_manage/edb_info_relation.go

@@ -104,8 +104,8 @@ func AddOrUpdateEdbInfoRelation(objectId, objectType int, relationList []*EdbInf
 
 	if len(refreshEdbInfoIds) > 0 {
 		//todo 是否需要所有指标的刷新状态
-		sql := ` UPDATE edb_info SET no_update = 0 WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND  no_update = 1`
-		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		sql := ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -172,8 +172,8 @@ func AddOrUpdateEdbInfoRelationMulti(relationList []*EdbInfoRelation, refreshEdb
 
 	if len(refreshEdbInfoIds) > 0 {
 		// todo 更新指标的刷新状态
-		sql := ` UPDATE edb_info SET no_update = 0 WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
-		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		sql := ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -386,7 +386,7 @@ func ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo *EdbInfo, edbRelationIds []
 		return
 	}
 
-	sourceWhere := ` and (refer_object_type in (1,2 ) or (refer_object_type=4 and refer_object_sub_type !=5) )` //平衡表和事件日历中的直接引用无需替换,
+	sourceWhere := ` and (refer_object_type in (1,2,5) or (refer_object_type=4 and refer_object_sub_type !=5) )` //平衡表和事件日历中的直接引用无需替换,
 	// 替换edb_info_id
 	sql = ` UPDATE edb_info_relation SET edb_info_id=?, source=?, edb_name=?, edb_code=?, modify_time=?, relation_time=?  WHERE edb_info_id=? ` + sourceWhere + ` and relation_type=0 and edb_info_relation_id in (` + utils.GetOrmInReplace(len(edbRelationIds)) + `)`
 	_, err = o.Raw(sql, newEdbInfo.EdbInfoId, newEdbInfo.Source, newEdbInfo.EdbName, newEdbInfo.EdbCode, now, now, oldEdbInfo.EdbInfoId, edbRelationIds).Exec()
@@ -416,8 +416,8 @@ func ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo *EdbInfo, edbRelationIds []
 
 	if len(refreshEdbInfoIds) > 0 {
 		// todo 更新指标的刷新状态
-		sql := ` UPDATE edb_info SET no_update = 0 WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
-		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		sql := ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -490,8 +490,8 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 
 	if len(refreshEdbInfoIds) > 0 {
 		// todo 更新指标的刷新状态
-		sql := ` UPDATE edb_info SET no_update = 0 WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) AND no_update = 1`
-		_, err = o.Raw(sql, refreshEdbInfoIds).Exec()
+		sql = ` UPDATE edb_info SET no_update = 0, set_update_time=? WHERE  edb_info_id IN (` + utils.GetOrmInReplace(len(refreshEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, time.Now(), refreshEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -500,7 +500,7 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 	//更新数据源钢联化工指标
 	if len(indexCodeList) > 0 {
 		// 更改数据源的更新状态
-		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
+		sql = ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
 		_, err = o.Raw(sql, indexCodeList).Exec()
 		if err != nil {
 			return
@@ -513,7 +513,7 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 			relationCodes = append(relationCodes, relationCode)
 		}
 		if len(relationCodes) > 0 {
-			sql := ` UPDATE edb_info_relation e1  
+			sql = ` UPDATE edb_info_relation e1  
 JOIN edb_info_relation e2 ON e1.relation_code = e2.relation_code   
 SET e1.parent_relation_id = e2.edb_info_relation_id  
 WHERE  

+ 8 - 0
models/data_manage/edb_terminal.go

@@ -148,3 +148,11 @@ func GetEdbCountGroupByTerminal(source int) (list []TerminalCodeCountGroup, err
 	_, err = o.Raw(sql, source).QueryRows(&list)
 	return
 }
+
+// EdbTerminalDirInfo 指标终端文件夹信息
+type EdbTerminalDirInfo struct {
+	Name         string `description:"终端名称"`
+	TerminalCode string `description:"终端编码,用于配置在机器上"`
+	DirPath      string `description:"终端存放的文件夹路径"`
+	FilePath     string `description:"文件夹路径"`
+}

+ 4 - 2
models/data_manage/excel/request/mixed_table.go

@@ -136,10 +136,12 @@ type MixCellShowStyle struct {
 	Pn              int         `description:"小数点位数增加或减少,正数表述增加,负数表示减少" json:"pn"`
 	Nt              string      `description:"变换类型:number 小数点位数改变,percent百分比," json:"nt"`
 	GlObj           interface{} `description:"公式对象:1:数值,2:百分比,3:文本" json:"glObj"`
-	Decimal         *int        `description:"小数点位数"`
-	Last            string      `description:"起始操作:nt|decimal"`
+	Width           float64     `description:"单元格宽度" json:"width"`
+	Decimal         *int        `description:"小数点位数" json:"decimal"`
+	Last            string      `description:"起始操作:nt|decimal" json:"last"`
 	Color           string      `description:"颜色值,#RRG" json:"color"`
 	BackgroundColor string      `description:"背景颜色值,#RRG" json:"background-color"`
+	Align           string      `description:"对齐方式:left|center|right" json:"align"`
 }
 
 type DateDataBeforeAfterReq struct {

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

@@ -31,6 +31,8 @@ type ExcelTableDetailResp struct {
 	SourcesFrom   string `description:"图表来源"`
 	ExcelSource   string `description:"表格来源str"`
 	ExcelSourceEn string `description:"表格来源(英文)"`
+	ExcelInfoId   int    `description:"表id"`
+	Source        int    `description:"表格来源"`
 }
 
 // ExcelTableDetailConfigResp

+ 11 - 10
models/data_manage/mysteel_chemical_index.go

@@ -30,6 +30,7 @@ type BaseFromMysteelChemicalIndex struct {
 	ModifyTime                        time.Time `description:"修改时间"`
 	CreateTime                        time.Time `description:"创建时间"`
 	Sort                              int       `description:"排序字段"`
+	FilePath                          string    `description:"文件"`
 	MergeFilePath                     string    `description:"合并文件"`
 	TerminalCode                      string    `description:"终端编码"`
 	IsStop                            int       `description:"是否停更:1:停更,0:未停更"`
@@ -619,8 +620,8 @@ func ModifyMysteelChemicalUpdateStatus(edbIdList []int, indexCodeList []string,
 	}
 
 	// 更改指标的更新状态
-	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND sub_source= ? AND edb_code IN (` + utils.GetOrmInReplace(codeNum) + `) `
-	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, indexCodeList).Exec()
+	sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE source = ? AND sub_source= ? AND edb_code IN (` + utils.GetOrmInReplace(codeNum) + `) `
+	_, err = o.Raw(sql, isStop, time.Now(), utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, indexCodeList).Exec()
 	if err != nil {
 		return
 	}
@@ -657,15 +658,15 @@ func ModifyMysteelChemicalUpdateStatusByEdbInfoId(edbInfoId, isStop int, edbCode
 	}
 
 	// 更改指标的更新状态
-	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND sub_source= ? AND edb_info_id=? `
-	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, edbInfoId).Exec()
+	sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE source = ? AND sub_source= ? AND edb_info_id=? `
+	_, err = o.Raw(sql, isStop, time.Now(), utils.DATA_SOURCE_MYSTEEL_CHEMICAL, 0, edbInfoId).Exec()
 	if err != nil {
 		return
 	}
 	if len(calculateEdbInfoIds) > 0 {
 		// 批量更新相关联的指标ID
-		sql = ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
-		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), calculateEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}
@@ -695,15 +696,15 @@ func ModifyMysteelChemicalUpdateStatusByEdbInfoIds(edbInfoIds []int, isStop int,
 	}
 
 	// 更改指标的更新状态
-	sql = ` UPDATE edb_info SET no_update = ? WHERE source = ? AND edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
-	_, err = o.Raw(sql, isStop, utils.DATA_SOURCE_MYSTEEL_CHEMICAL, edbInfoIds).Exec()
+	sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE source = ? AND edb_info_id IN (` + utils.GetOrmInReplace(len(edbInfoIds)) + `) `
+	_, err = o.Raw(sql, isStop, time.Now(), utils.DATA_SOURCE_MYSTEEL_CHEMICAL, edbInfoIds).Exec()
 	if err != nil {
 		return
 	}
 	if len(calculateEdbInfoIds) > 0 {
 		// 批量更新相关联的指标ID
-		sql = ` UPDATE edb_info SET no_update = ? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
-		_, err = o.Raw(sql, isStop, calculateEdbInfoIds).Exec()
+		sql = ` UPDATE edb_info SET no_update = ?, set_update_time=? WHERE edb_info_id IN (` + utils.GetOrmInReplace(len(calculateEdbInfoIds)) + `) `
+		_, err = o.Raw(sql, isStop, time.Now(), calculateEdbInfoIds).Exec()
 		if err != nil {
 			return
 		}

+ 3 - 2
models/data_manage/smm_data.go

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

+ 34 - 0
models/data_manage/stl/calculate_stl_config.go

@@ -0,0 +1,34 @@
+package stl
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type CalculateStlConfig struct {
+	CalculateStlConfigId int       `orm:"column(calculate_stl_config_id);pk"`
+	Config               string    `description:"STL计算配置"`
+	SysUserId            int       `description:"系统用户ID"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"更新时间"`
+}
+
+func (c *CalculateStlConfig) Insert() (insertId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	insertId, err = o.Insert(c)
+	return
+}
+
+func (c *CalculateStlConfig) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(c, cols...)
+	return
+}
+
+func GetCalculateStlConfigById(id int) (item *CalculateStlConfig, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM calculate_stl_config WHERE calculate_stl_config_id =?"
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}

+ 38 - 0
models/data_manage/stl/calculate_stl_config_mapping.go

@@ -0,0 +1,38 @@
+package stl
+
+import (
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type CalculateStlConfigMapping struct {
+	Id                   int       `orm:"pk" description:"主键"`
+	CalculateStlConfigId int       `description:"stl配置id"`
+	EdbInfoId            int       `description:"edb信息id"`
+	StlEdbType           int       `description:"stl指标类型: 1-Trend, 2-Seasonal, 3-Residual"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"修改时间"`
+}
+
+func (c *CalculateStlConfigMapping) Insert() (insertId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	insertId, err = o.Insert(c)
+	return
+}
+
+// GetCalculateStlConfigMappingIdByEdbInfoId 获取配置文件id
+func GetCalculateStlConfigMappingIdByEdbInfoId(edbInfoId int) (configId int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT calculate_stl_config_id FROM calculate_stl_config_mapping WHERE edb_info_id=? LIMIT 1`
+	err = o.Raw(sql, edbInfoId).QueryRow(&configId)
+	return
+}
+
+// GetCalculateStlConfigMappingByConfigId 根据配置文件id获取配置文件映射信息
+func GetCalculateStlConfigMappingByConfigId(configId int) (items []*CalculateStlConfigMapping, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM calculate_stl_config_mapping WHERE calculate_stl_config_id=?`
+	_, err = o.Raw(sql, configId).QueryRows(&items)
+	return
+}

+ 60 - 0
models/data_manage/stl/edb_data_calculate_stl.go

@@ -0,0 +1,60 @@
+package stl
+
+import (
+	"eta/eta_api/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type EdbDataCalculateStl struct {
+	EdbDataId     int       `orm:"pk"`
+	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 GetEdbDataCalculateStlByEdbCode(edbCode string) (items []*EdbDataCalculateStl, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM edb_data_calculate_stl WHERE edb_code =? ORDER BY data_time`
+	_, err = o.Raw(sql, edbCode).QueryRows(&items)
+	return
+}
+
+func DeleteAndInsertEdbDataCalculateStl(edbCode string, dataList []*EdbDataCalculateStl) (err error) {
+	tx, err := orm.NewOrmUsingDB("data").Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+	sql := `DELETE FROM edb_data_calculate_stl WHERE edb_code =?`
+	_, err = tx.Raw(sql, edbCode).Exec()
+	if err != nil {
+		return
+	}
+	_, err = tx.InsertMulti(utils.MultiAddNum, dataList)
+	return
+}
+
+func (m *EdbDataCalculateStl) GetMaxId() (maxId int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT max(edb_data_id) id FROM edb_data_calculate_stl limit 1`
+	err = o.Raw(sql).QueryRow(&maxId)
+	return
+}
+
+func (m *EdbDataCalculateStl) BatchInsert(dataList []*EdbDataCalculateStl) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.InsertMulti(utils.MultiAddNum, dataList)
+	return
+}

+ 28 - 0
models/data_manage/stl/request/stl.go

@@ -0,0 +1,28 @@
+package request
+
+type StlConfigReq struct {
+	EdbInfoId            int     `description:"指标ID"`
+	CalculateStlConfigId int     `description:"计算的STL配置ID"`
+	DataRangeType        int     `description:"数据时间类型:1-全部时间,2-最近N年,3-区间设置,4-区间设置(至今)"`
+	StartDate            string  `description:"开始日期"`
+	EndDate              string  `description:"结束日期"`
+	LastNYear            string  `description:"最近N年"`
+	Period               int     `description:"数据的周期,根据频率设置"`
+	Seasonal             int     `description:"季节性成分窗口大小,一般为period+1,可以设置为大于period的正奇数"`
+	Trend                int     `description:"趋势成分窗口大小,一般为period+1,可以设置为大于period的正奇数"`
+	Fraction             float64 `description:"趋势项的平滑系数,默认为0.2,区间为[0-1]"`
+	Robust               bool    `description:"是否使用稳健方法: true(使用) false(不使用)  "`
+	TrendDeg             int     `description:"分解中趋势多项式次数,默认为1,不超过5的正整数"`
+	SeasonalDeg          int     `description:"分解中季节性多项次数,默认为1,不超过5的正整数"`
+	LowPassDeg           int     `description:"分解中低通滤波器次数,默认为1,不超过5的正整数"`
+}
+
+type SaveStlEdbInfoReq struct {
+	CalculateStlConfigId int    `description:"计算的STL配置ID"`
+	EdbInfoId            int    `description:"指标ID"`
+	StlEdbType           int    `description:"stl指标类型: 1-Trend, 2-Seasonal, 3-Residual"`
+	Frequency            string `description:"频度"`
+	Unit                 string `description:"单位"`
+	EdbName              string `description:"指标名称"`
+	ClassifyId           int    `description:"分类ID"`
+}

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

@@ -0,0 +1,60 @@
+package response
+
+type StlPreviewResp struct {
+	OriginEdbInfo     ChartEdbInfo
+	TrendChartInfo    ChartEdbInfo
+	SeasonalChartInfo ChartEdbInfo
+	ResidualChartInfo ChartEdbInfo
+	EvaluationResult  EvaluationResult
+}
+
+type ChartEdbInfo struct {
+	EdbInfoId    int
+	Title        string
+	Unit         string
+	Frequency    string
+	MaxData      float64
+	MinData      float64
+	ClassifyId   int
+	ClassifyPath string
+	DataList     []*EdbData
+}
+
+type EvaluationResult struct {
+	Mean           string `description:"均值"`
+	Std            string `description:"标准差"`
+	AdfPValue      string `description:"ADF检验p值"`
+	LjungBoxPValue string `description:"Ljung-Box检验p值"`
+}
+
+type EdbData struct {
+	Value         float64
+	DataTime      string
+	DataTimestamp int64
+}
+
+type SaveStlConfigResp struct {
+	CalculateStlConfigId int64 `description:"配置文件id"`
+}
+
+type StlConfigResp struct {
+	EdbInfoId            int     `description:"指标ID"`
+	EdbInfoName          string  `description:"指标名称"`
+	CalculateStlConfigId int     `description:"计算的STL配置ID"`
+	DataRangeType        int     `description:"数据时间类型:1-全部时间,2-最近N年,3-区间设置,4-区间设置(至今)"`
+	StartDate            string  `description:"开始日期"`
+	EndDate              string  `description:"结束日期"`
+	LastNYear            string  `description:"最近N年"`
+	Period               int     `description:"数据的周期,根据频率设置"`
+	Seasonal             int     `description:"季节性成分窗口大小,一般为period+1,可以设置为大于period的正奇数"`
+	Trend                int     `description:"趋势成分窗口大小,一般为period+1,可以设置为大于period的正奇数"`
+	Fraction             float64 `description:"趋势项的平滑系数,默认为0.2,区间为[0-1]"`
+	Robust               bool    `description:"是否使用稳健方法: true(使用) false(不使用)  "`
+	TrendDeg             int     `description:"分解中趋势多项式次数,默认为1,不超过5的正整数"`
+	SeasonalDeg          int     `description:"分解中季节性多项次数,默认为1,不超过5的正整数"`
+	LowPassDeg           int     `description:"分解中低通滤波器次数,默认为1,不超过5的正整数"`
+}
+
+type SaveStlEdbInfoResp struct {
+	EdbInfoId int `description:"指标ID"`
+}

+ 87 - 8
models/data_manage/trade_analysis/trade_analysis.go

@@ -182,6 +182,46 @@ func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts,
 	if len(contracts) == 0 || len(companies) == 0 {
 		return
 	}
+	condBuy := fmt.Sprintf(`classify_name = ? AND classify_type IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsBuy := make([]interface{}, 0)
+	parsBuy = append(parsBuy, classifyName, contracts)
+
+	condSold := fmt.Sprintf(`classify_name = ? AND classify_type IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsSold := make([]interface{}, 0)
+	parsSold = append(parsSold, classifyName, contracts)
+
+	// 是否含有TOP20
+	var hasTop bool
+	var condCompanies []string
+	for _, v := range companies {
+		if v == TradeFuturesCompanyTop20 {
+			hasTop = true
+			continue
+		}
+		condCompanies = append(condCompanies, v)
+	}
+	if !hasTop {
+		if len(condCompanies) == 0 {
+			err = fmt.Errorf("查询条件-期货公司异常")
+			return
+		}
+		condBuy += fmt.Sprintf(` AND buy_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsBuy = append(parsBuy, condCompanies)
+		condSold += fmt.Sprintf(` AND sold_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsSold = append(parsSold, condCompanies)
+	} else {
+		// 这里rank=0或者999是因为大商所的数据并不只有999
+		if len(condCompanies) > 0 {
+			condBuy += fmt.Sprintf(` AND (rank = 999 OR rank = 0 OR buy_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			condSold += fmt.Sprintf(` AND (rank = 999 OR rank = 0 OR sold_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			parsBuy = append(parsBuy, condCompanies)
+			parsSold = append(parsSold, condCompanies)
+		} else {
+			condBuy += ` AND (rank = 999 OR rank = 0)`
+			condSold += ` AND (rank = 999 OR rank = 0)`
+		}
+	}
+
 	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
 	sql := `SELECT
 			rank,
@@ -195,7 +235,7 @@ func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts,
 		FROM
 			%s 
 		WHERE
-			classify_name = ? AND classify_type IN (%s) AND buy_short_name IN (%s)
+			%s
 		UNION ALL
 		(
 		SELECT
@@ -210,11 +250,11 @@ func GetTradeDataByClassifyAndCompany(exchange, classifyName string, contracts,
 		FROM
 			%s 
 		WHERE
-			classify_name = ? AND classify_type IN (%s) AND sold_short_name IN (%s)
+			%s
 		)`
-	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)), tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)))
+	sql = fmt.Sprintf(sql, tableName, condBuy, tableName, condSold)
 	o := orm.NewOrmUsingDB("data")
-	_, err = o.Raw(sql, classifyName, contracts, companies, classifyName, contracts, companies).QueryRows(&items)
+	_, err = o.Raw(sql, parsBuy, parsSold).QueryRows(&items)
 	return
 }
 
@@ -227,6 +267,45 @@ func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, compa
 	if len(contracts) == 0 || len(companies) == 0 {
 		return
 	}
+	condBuy := fmt.Sprintf(`classify_name IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsBuy := make([]interface{}, 0)
+	parsBuy = append(parsBuy, contracts)
+
+	condSold := fmt.Sprintf(`classify_name IN (%s)`, utils.GetOrmInReplace(len(contracts)))
+	parsSold := make([]interface{}, 0)
+	parsSold = append(parsSold, contracts)
+
+	// 是否含有TOP20
+	var hasTop bool
+	var condCompanies []string
+	for _, v := range companies {
+		if v == TradeFuturesCompanyTop20 {
+			hasTop = true
+			continue
+		}
+		condCompanies = append(condCompanies, v)
+	}
+	if !hasTop {
+		if len(condCompanies) == 0 {
+			err = fmt.Errorf("查询条件-期货公司异常")
+			return
+		}
+		condBuy += fmt.Sprintf(` AND buy_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsBuy = append(parsBuy, condCompanies)
+		condSold += fmt.Sprintf(` AND sold_short_name IN (%s)`, utils.GetOrmInReplace(len(condCompanies)))
+		parsSold = append(parsSold, condCompanies)
+	} else {
+		if len(condCompanies) > 0 {
+			condBuy += fmt.Sprintf(` AND (rank = 999 OR buy_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			condSold += fmt.Sprintf(` AND (rank = 999 OR sold_short_name IN (%s))`, utils.GetOrmInReplace(len(condCompanies)))
+			parsBuy = append(parsBuy, condCompanies)
+			parsSold = append(parsSold, condCompanies)
+		} else {
+			condBuy += ` AND rank = 999`
+			condSold += ` AND rank = 999`
+		}
+	}
+
 	tableName := fmt.Sprintf("base_from_trade_%s_index", exchange)
 	sql := `SELECT
 			rank,
@@ -239,7 +318,7 @@ func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, compa
 		FROM
 			%s 
 		WHERE
-			classify_name IN (%s) AND buy_short_name IN (%s)
+			%s
 		UNION ALL
 		(
 		SELECT
@@ -253,11 +332,11 @@ func GetTradeZhengzhouDataByClassifyAndCompany(exchange string, contracts, compa
 		FROM
 			%s 
 		WHERE
-			classify_name IN (%s) AND sold_short_name IN (%s)
+			%s
 		)`
-	sql = fmt.Sprintf(sql, tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)), tableName, utils.GetOrmInReplace(len(contracts)), utils.GetOrmInReplace(len(companies)))
+	sql = fmt.Sprintf(sql, tableName, condBuy, tableName, condSold)
 	o := orm.NewOrmUsingDB("data")
-	_, err = o.Raw(sql, contracts, companies, contracts, companies).QueryRows(&items)
+	_, err = o.Raw(sql, parsBuy, parsSold).QueryRows(&items)
 	return
 }
 

+ 70 - 0
models/db.go

@@ -1,8 +1,11 @@
 package models
 
 import (
+	aiPredictModel "eta/eta_api/models/ai_predict_model"
 	"eta/eta_api/models/ai_summary"
 	"eta/eta_api/models/aimod"
+	"eta/eta_api/models/bi_dashboard"
+	binlogDao "eta/eta_api/models/binlog"
 	"eta/eta_api/models/company"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/data_manage/chart_theme"
@@ -11,8 +14,10 @@ import (
 	"eta/eta_api/models/data_manage/edb_refresh"
 	"eta/eta_api/models/data_manage/excel"
 	future_good2 "eta/eta_api/models/data_manage/future_good"
+	"eta/eta_api/models/data_manage/stl"
 	"eta/eta_api/models/data_manage/supply_analysis"
 	"eta/eta_api/models/data_stat"
+	edbmonitor "eta/eta_api/models/edb_monitor"
 	"eta/eta_api/models/eta_trial"
 	"eta/eta_api/models/fe_calendar"
 	"eta/eta_api/models/ppt_english"
@@ -24,6 +29,7 @@ import (
 	"eta/eta_api/models/speech_recognition"
 	"eta/eta_api/models/system"
 	"eta/eta_api/models/yb"
+	binlogSvr "eta/eta_api/services/binlog"
 	"eta/eta_api/utils"
 	"time"
 
@@ -204,8 +210,25 @@ func init() {
 	// 初始化因子指标系列
 	initFactorEdbSeries()
 
+	// 初始化STL指标系列
+	initStlEdbInfo()
+
+	// 初始化指标监控
+	initEdbMonitor()
+
+	// 开启mysql binlog监听
+	if utils.MYSQL_DATA_BINLOG_URL != "" {
+		initBinlog()
+		go binlogSvr.ListenMysql()
+	}
 	// 初始化部分数据表变量(直接init会有顺序问题=_=!)
 	afterInitTable()
+
+	// 智能看板
+	initBiDashBoard()
+
+	// AI预测模型表
+	initAiPredictModel()
 }
 
 // initSystem 系统表 数据表
@@ -636,6 +659,50 @@ func initFactorEdbSeries() {
 	)
 }
 
+func initStlEdbInfo() {
+	orm.RegisterModel(
+		new(stl.CalculateStlConfig),        // STL指标配置
+		new(stl.CalculateStlConfigMapping), // STL指标映射
+		new(stl.EdbDataCalculateStl),       // STL指标计算数据
+	)
+}
+
+// 预警管理
+func initEdbMonitor() {
+	orm.RegisterModel(
+		new(edbmonitor.EdbMonitorInfo),     // 预警管理表
+		new(edbmonitor.EdbMonitorClassify), // 预警管理分类表
+		new(edbmonitor.EdbMonitorMessage),  // 预警管理消息表
+	)
+}
+
+func initBinlog() {
+	orm.RegisterModel(
+		new(binlogDao.BusinessSysInteractionLog), // binlog表
+	)
+}
+
+// initBiDashBoard 智能看板
+func initBiDashBoard() {
+	orm.RegisterModel(
+		new(bi_dashboard.BiDashboard),
+		new(bi_dashboard.BiDashboardDetail),
+		new(bi_dashboard.BiDashboardGrant),
+		new(bi_dashboard.BiDashboardHomePage),
+	)
+}
+
+// initAiPredictModel AI预测模型表
+func initAiPredictModel() {
+	orm.RegisterModel(
+		new(aiPredictModel.AiPredictModelClassify),
+		new(aiPredictModel.AiPredictModelIndex),
+		new(aiPredictModel.AiPredictModelData),
+		new(aiPredictModel.AiPredictModelDashboard),
+		new(aiPredictModel.AiPredictModelDashboardDetail),
+	)
+}
+
 // afterInitTable
 // @Description: 初始化表结构的的后置操作
 // @author: Roc
@@ -646,4 +713,7 @@ func afterInitTable() {
 
 	// 初始化是否启用mongo配置
 	InitUseMongoConf()
+
+	// 初始化商家基本配置
+	InitBusinessConf()
 }

+ 20 - 10
models/document_manage_model/outside_report.go

@@ -50,33 +50,43 @@ func init() {
 }
 
 // GetOutsideReportListByConditionCount 根据条件查询列表条数
-func GetOutsideReportListByConditionCount(condition string, pars []interface{}) (count int, err error) {
+func GetOutsideReportListByConditionCount(condition string, pars []interface{}, chartPermissionIdList []string) (count int, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `select count(distinct t1.outside_report_id) from outside_report t1 left join chart_permission_search_key_word_mapping t2 on t1.classify_id = t2.classify_id  where 1 = 1 `
+	var sql string
+	if len(chartPermissionIdList) > 0 {
+		sql = `select count(1) from (`
+	}
+	sql += `SELECT COUNT( DISTINCT t1.outside_report_id ) 
+			FROM outside_report t1 
+			    LEFT JOIN chart_permission_search_key_word_mapping t2 ON t1.classify_id = t2.classify_id 
+			WHERE 1 = 1 `
 	sql += condition
+	if len(chartPermissionIdList) > 0 {
+		sql += ` ) t`
+	}
+
 	err = o.Raw(sql, pars).QueryRow(&count)
-	if err != nil {
+	if err != nil && err != orm.ErrNoRows {
 		return 0, err
 	}
 
-	return count, err
+	return count, nil
 }
 
 // GetOutsideReportListByCondition 根据条件查询列表
-func GetOutsideReportListByCondition(condition string, pars []interface{}, currentIndex int, pageSize int) (list []OutsideReport, err error) {
+func GetOutsideReportListByCondition(condition string, pars []interface{}) (list []OutsideReport, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `select distinct t1.outside_report_id, t1.source, t1.title, t1.abstract, t1.classify_id, 
+	sql := `select DISTINCT t1.outside_report_id, t1.source, t1.title, t1.abstract, t1.classify_id, 
 t1.classify_name, t1.sys_user_id, t1.sys_user_name, t1.email_message_uid, t1.report_update_time, 
 t1.modify_time, t1.create_time, t1.report_code from outside_report t1 
 left join chart_permission_search_key_word_mapping t2 on t1.classify_id = t2.classify_id  where 1 = 1 `
 	sql += condition
-	sql += ` limit ?, ?`
-	_, err = o.Raw(sql, pars, (currentIndex-1)*pageSize, pageSize).QueryRows(&list)
-	if err != nil {
+	_, err = o.Raw(sql, pars).QueryRows(&list)
+	if err != nil && err != orm.ErrNoRows {
 		return nil, err
 	}
 
-	return list, err
+	return list, nil
 }
 
 // SaveOutsideReport 保存报告

+ 129 - 0
models/edb_monitor/edb_monitor.go

@@ -0,0 +1,129 @@
+package edbmonitor
+
+import (
+	"eta/eta_api/utils"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type EdbMonitorInfo struct {
+	EdbMonitorId         int       `orm:"column(edb_monitor_id);pk"`
+	EdbMonitorClassifyId int       `description:"预警分类id"`
+	EdbMonitorName       string    `description:"预警名称"`
+	EdbInfoId            int       `description:"指标id"`
+	EdbInfoType          int       `description:"指标类型"`
+	EdbUniqueCode        string    `description:"指标唯一标识"`
+	EdbClassifyId        int       `description:"指标id"`
+	EdbCode              string    `description:"指标编码"`
+	Source               int       `description:"指标来源"`
+	SubSource            int       `description:"指标子来源: 0-经济数据库;1-日期序列;2-高频数据"`
+	EdbLatestDate        string    `description:"最新日期"`
+	EdbLatestValue       float64   `description:"指标最新值"`
+	MonitorType          int       `description:"突破方式: 0-向上突破;1-向下突破"`
+	MonitorData          float64   `description:"预警值"`
+	MonitorLevel         string    `description:"预警等级"`
+	State                int       `description:"预警状态: 0-已关闭;1-未触发;2-已触发"`
+	EdbTriggerDate       time.Time `description:"触发日期"`
+	MonitorTriggerTime   time.Time `description:"预警触发时间"`
+	CreateUserId         int       `description:"创建人id"`
+	CreateTime           time.Time `description:"创建时间"`
+	ModifyTime           time.Time `description:"修改时间"`
+}
+
+func (m *EdbMonitorInfo) Insert() (int64, error) {
+	o := orm.NewOrmUsingDB("data")
+	return o.Insert(m)
+}
+
+func (m *EdbMonitorInfo) Update(cols []string) (err error) {
+	if len(cols) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func GetEdbMonitorLevelList() (list []string, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT DISTINCT monitor_level FROM edb_monitor_info`
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+func GetEdbMonitorEdbInfoList() (items []*EdbMonitorInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM edb_monitor_info WHERE state <> 0`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func DeleteEdbMonitorInfoById(id int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `DELETE FROM edb_monitor_info WHERE edb_monitor_id =?`
+	_, err = o.Raw(sql, id).Exec()
+	return
+}
+
+func DeleteEdbMonitorInfoByIdList(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := `DELETE FROM edb_monitor_info WHERE edb_monitor_id IN(` + utils.GetOrmInReplace(len(ids)) + `)`
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func GetEdbMonitorInfoById(id int) (item *EdbMonitorInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM edb_monitor_info WHERE edb_monitor_id =?`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func GetEdbMonitorInfoByEdbInfoId(edbInfoId int) (items []*EdbMonitorInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM edb_monitor_info WHERE edb_info_id =?`
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
+	return
+}
+
+func GetEdbMonitorInfoCountByClassifyId(classifyId []int) (count int, err error) {
+	if len(classifyId) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(*) AS count FROM edb_monitor_info WHERE edb_monitor_classify_id IN(` + utils.GetOrmInReplace(len(classifyId)) + `)`
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+func GetEdbMonitorInfoCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(*) AS count FROM edb_monitor_info WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetEdbMonitorInfoPageByCondition(condition string, pars []interface{}, startSize int, pageSize int) (items []*EdbMonitorInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM edb_monitor_info WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY create_time DESC LIMIT?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+func GetEdbMonitorCreateUserId() (userIds []int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT DISTINCT create_user_id FROM edb_monitor_info`
+	_, err = o.Raw(sql).QueryRows(&userIds)
+	return
+}

+ 191 - 0
models/edb_monitor/edb_monitor_classify.go

@@ -0,0 +1,191 @@
+package edbmonitor
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type EdbMonitorClassify struct {
+	ClassifyId   int       `orm:"column(classify_id);pk"`
+	ClassifyName string    `description:"分类名称"`
+	Level        int       `description:"分类层级"`
+	ParentId     int       `description:"父分类ID"`
+	RootId       int       `description:"根分类ID"`
+	Sort         int       `description:"排序"`
+	CreateTime   time.Time `description:"创建时间"`
+}
+
+func (e *EdbMonitorClassify) Insert() (id int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+
+	var edbMonitorIds []int
+	if e.ParentId != 0 {
+		sql := `SELECT edb_monitor_id FROM edb_monitor_info WHERE edb_monitor_classify_id = ?`
+		_, err = o.Raw(sql, e.ParentId).QueryRows(&edbMonitorIds)
+		if err != nil {
+			return
+		}
+	}
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	id, err = tx.Insert(e)
+	if len(edbMonitorIds) > 0 {
+		sql := `UPDATE edb_monitor_info SET edb_monitor_classify_id = ? WHERE edb_monitor_id IN (` + utils.GetOrmInReplace(len(edbMonitorIds)) + `)`
+		_, err = tx.Raw(sql, id, edbMonitorIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func (e *EdbMonitorClassify) Update(cols []string, classifyId []int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	var edbMonitorIds []int
+	if e.ParentId != 0 {
+		sql := `SELECT edb_monitor_id FROM edb_monitor_info WHERE edb_monitor_classify_id = ?`
+		_, err = o.Raw(sql, e.ParentId).QueryRows(&edbMonitorIds)
+		if err != nil {
+			return
+		}
+	}
+
+	tx, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+	_, err = tx.Update(e, cols...)
+	if err != nil {
+		return
+	}
+	if len(classifyId) > 0 {
+		sql := `UPDATE edb_monitor_classify SET root_id = ? WHERE classify_id IN (` + utils.GetOrmInReplace(len(classifyId)) + `)`
+		_, err = tx.Raw(sql, e.RootId, classifyId).Exec()
+		if err != nil {
+			return
+		}
+	}
+	if len(edbMonitorIds) > 0 {
+		sql := `UPDATE edb_monitor_info SET edb_monitor_classify_id = ? WHERE edb_monitor_id IN (` + utils.GetOrmInReplace(len(edbMonitorIds)) + `)`
+		_, err = tx.Raw(sql, e.ClassifyId, edbMonitorIds).Exec()
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func DeleteEdbMonitorClassifyById(id int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw("DELETE FROM edb_monitor_classify WHERE classify_id =?", id).Exec()
+	return
+}
+
+func DeleteEdbMonitorClassifyByIdList(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := "DELETE FROM edb_monitor_classify WHERE classify_id IN (" + utils.GetOrmInReplace(len(ids)) + ")"
+	_, err = o.Raw(sql, ids).Exec()
+	return
+}
+
+func GetEdbMonitorClassifyList() (items []*EdbMonitorClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM edb_monitor_classify ORDER BY sort ASC"
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetEdbMonitorClassifyById(id int) (item *EdbMonitorClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM edb_monitor_classify WHERE classify_id =?"
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func GetEdbMonitorClassifyMaxSortByParentId(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT MAX(sort) FROM edb_monitor_classify WHERE parent_id =?"
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+func GetChildEdbMonitorClassifyIdById(id int) (classifyId []int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT classify_id FROM edb_monitor_classify WHERE parent_id =?"
+	_, err = o.Raw(sql, id).QueryRows(&classifyId)
+	return
+}
+
+func GetChildEdbMonitorClassifyById(id int) (items []*EdbMonitorClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM edb_monitor_classify WHERE parent_id =?"
+	_, err = o.Raw(sql, id).QueryRows(&items)
+	return
+}
+
+func GetChildEdbMonitorClassifyByRootId(rootId int) (items []*EdbMonitorClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM edb_monitor_classify WHERE root_id =?"
+	_, err = o.Raw(sql, rootId).QueryRows(&items)
+	return
+}
+
+func GetEdbMonitorClassifyCountByIdList(ids []int) (count int, err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(*) FROM edb_monitor_classify WHERE classify_id IN (` + utils.GetOrmInReplace(len(ids)) + `)`
+	err = o.Raw(sql, ids).QueryRow(&count)
+	return
+}
+
+// GetEdbMonitorClassifyByParentIdAndName 根据父级ID和名称获取分类
+func GetEdbMonitorClassifyByParentIdAndName(parentId int, classifyName string, classifyId int) (item *EdbMonitorClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM edb_monitor_classify WHERE parent_id = ? AND classify_name = ? AND classify_id <> ? LIMIT 1`
+	err = o.Raw(sql, parentId, classifyName, classifyId).QueryRow(&item)
+	return
+}
+
+// UpdateEdbMonitorClassifySortByParentId 根据分类父类id更新排序
+func UpdateEdbMonitorClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` update edb_monitor_classify set sort = ` + updateSort + ` WHERE parent_id=? and sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( classify_id > ` + fmt.Sprint(classifyId) + ` and sort= ` + fmt.Sprint(nowSort) + `)`
+	}
+	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	return
+}
+
+// GetFirstEdbMonitorClassifyByParentId 获取当前父级图表分类下的排序第一条的数据
+func GetFirstEdbMonitorClassifyByParentId(parentId int) (item *EdbMonitorClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_monitor_classify WHERE parent_id=? order by sort asc,classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}

+ 79 - 0
models/edb_monitor/edb_monitor_message.go

@@ -0,0 +1,79 @@
+package edbmonitor
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+
+	"github.com/beego/beego/v2/client/orm"
+)
+
+type EdbMonitorMessage struct {
+	EdbMonitorMessageId int       `orm:"column(edb_monitor_message_id);pk"`
+	EdbInfoId           int       `description:"指标id"`
+	EdbInfoType         int       `description:"指标类型: 0-普通指标;1-预测指标"`
+	EdbUniqueCode       string    `description:"指标唯一标识"`
+	EdbClassifyId       int       `description:"指标id"`
+	AdminId             int       `description:"用户id"`
+	IsRead              int       `description:"是否已读: 0-未读;1-已读"`
+	Message             string    `description:"消息内容"`
+	MonitorTriggerTime  time.Time `description:"预警触发时间"`
+	CreateTime          time.Time `description:"创建时间"`
+}
+
+func (m *EdbMonitorMessage) Insert() (insertId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	insertId, err = o.Insert(m)
+	return
+}
+
+func (m *EdbMonitorMessage) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func BatchModifyEdbMonitorMessageIsRead(ids []int, adminId int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := orm.NewOrmUsingDB("data")
+	sql := `UPDATE edb_monitor_message SET is_read =1 WHERE admin_id =? AND is_read = 0 AND edb_monitor_message_id IN (` + utils.GetOrmInReplace(len(ids)) + `)`
+	_, err = o.Raw(sql, adminId, ids).Exec()
+	return
+}
+
+func (m *EdbMonitorMessage) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM edb_monitor_message WHERE 1=1 %s`, condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func GetEdbMonitorMessageById(id int) (item *EdbMonitorMessage, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM edb_monitor_message WHERE edb_monitor_message_id =?"
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func GetEdbMonitorMessageByAdminId(adminId int) (items []*EdbMonitorMessage, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM edb_monitor_message WHERE admin_id =? AND is_read = 0 ORDER BY create_time DESC"
+	_, err = o.Raw(sql, adminId).QueryRows(&items)
+	return
+}
+
+func GetEdbMonitorMessageCountByAdminId(adminId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT COUNT(*) FROM edb_monitor_message WHERE admin_id =? ORDER BY is_read ASC, create_time DESC"
+	err = o.Raw(sql, adminId).QueryRow(&count)
+	return
+}
+
+func GetEdbMonitorMessagePageByAdminId(adminId, startSize, pageSize int) (items []*EdbMonitorMessage, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := "SELECT * FROM edb_monitor_message WHERE admin_id =? ORDER BY is_read ASC, create_time DESC LIMIT?,?"
+	_, err = o.Raw(sql, adminId, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 25 - 0
models/edb_monitor/request/edb_monitor.go

@@ -0,0 +1,25 @@
+package request
+
+type EdbMonitorInfoSaveReq struct {
+	EdbMonitorId   int    `description:"预警ID"`
+	EdbInfoId      int    `description:"指标ID"`
+	EdbUniqueCode  string `description:"指标唯一标识"`
+	EdbClassifyId  int    `description:"指标id"`
+	MonitorType    int    `description:"突破方式:0-向上突破;1-向下突破"`
+	MonitorData    string `description:"预警值"`
+	EdbMonitorName string `description:"预警名称"`
+	MonitorLevel   string `description:"预警级别"`
+	ClassifyId     int    `description:"预警分类"`
+}
+
+type EdbMonitorInfoDeleteReq struct {
+	EdbMonitorId int `description:"预警ID"`
+}
+
+type EdbMonitorInfoCloseReq struct {
+	EdbMonitorId int `description:"预警ID"`
+}
+
+type EdbMonitorInfoRestartReq struct {
+	EdbMonitorId int `description:"预警ID"`
+}

+ 19 - 0
models/edb_monitor/request/edb_monitor_classify.go

@@ -0,0 +1,19 @@
+package request
+
+type EdbMonitorClassifySaveReq struct {
+	ClassifyId   int
+	ClassifyName string
+	Level        int
+	ParentId     int
+}
+
+type EdbMonitorClassifyDeleteReq struct {
+	ClassifyId int
+}
+
+type MoveEdbMonitorClassifyReq struct {
+	ClassifyId       int `description:"分类id"`
+	ParentClassifyId int `description:"父级分类id"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
+	NextClassifyId   int `description:"下一个兄弟节点分类id"`
+}

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff