ソースを参照

Merge branch 'feature/eta_2.5.4_report_free_layout' of http://8.136.199.33:3000/eta_server/eta_api into feature/eta_2.5.4_report_free_layout

kobe6258 3 日 前
コミット
1a1a82a983
100 ファイル変更6201 行追加485 行削除
  1. 40 0
      cache/llm.go
  2. 28 1
      controllers/base_auth.go
  3. 9 1
      controllers/data_manage/ccf_data.go
  4. 358 133
      controllers/data_manage/chart_classify.go
  5. 151 3
      controllers/data_manage/chart_info.go
  6. 1 1
      controllers/data_manage/correlation/correlation_chart_classify.go
  7. 43 5
      controllers/data_manage/edb_info.go
  8. 6 1
      controllers/data_manage/excel/balance_table.go
  9. 6 0
      controllers/data_manage/excel/excel_info.go
  10. 1 1
      controllers/data_manage/future_good/future_good_chart_classify.go
  11. 1 1
      controllers/data_manage/line_equation/line_chart_classify.go
  12. 1 1
      controllers/data_manage/line_feature/classify.go
  13. 10 0
      controllers/data_manage/manual_edb.go
  14. 26 14
      controllers/data_manage/my_chart.go
  15. 55 12
      controllers/data_manage/mysteel_chemical_data.go
  16. 1045 0
      controllers/data_manage/purang_data.go
  17. 1 1
      controllers/data_manage/range_analysis/chart_classify.go
  18. 49 13
      controllers/data_manage/sci_hq_data.go
  19. 1 0
      controllers/data_manage/smm_data.go
  20. 9 2
      controllers/data_source/data_source.go
  21. 6 2
      controllers/llm/llm_http/request.go
  22. 44 11
      controllers/llm/promote_controller.go
  23. 196 19
      controllers/llm/question.go
  24. 249 0
      controllers/llm/report.go
  25. 6 0
      controllers/report.go
  26. 2 2
      controllers/report_chapter.go
  27. 18 7
      controllers/report_v2.go
  28. 12 7
      controllers/smart_report/smart_report.go
  29. 3 1
      controllers/smart_report/smart_resource.go
  30. 15 3
      controllers/sys_role.go
  31. 1 1
      controllers/trade_analysis/warehouse_classify.go
  32. 1 0
      main.go
  33. 13 2
      models/business_conf.go
  34. 6 0
      models/classify.go
  35. 10 1
      models/cloud_disk_resource.go
  36. 14 12
      models/data_manage/base_from_ccf.go
  37. 292 0
      models/data_manage/base_from_purang.go
  38. 139 0
      models/data_manage/base_from_purang_classify.go
  39. 27 0
      models/data_manage/base_from_sci_hq_classify.go
  40. 10 4
      models/data_manage/base_from_sci_hq_index.go
  41. 7 0
      models/data_manage/base_from_ths_hf_classify.go
  42. 88 10
      models/data_manage/chart_classify.go
  43. 81 0
      models/data_manage/chart_description.go
  44. 6 0
      models/data_manage/chart_edb_mapping.go
  45. 7 4
      models/data_manage/chart_info.go
  46. 8 10
      models/data_manage/edb_classify.go
  47. 1 1
      models/data_manage/edb_data_base.go
  48. 2 0
      models/data_manage/excel/excel_info.go
  49. 15 0
      models/data_manage/excel/excel_info_rule_mapping.go
  50. 3 0
      models/data_manage/excel/excel_sheet.go
  51. 48 0
      models/data_manage/forum_chart_edb_mapping.go
  52. 51 13
      models/data_manage/mysteel_chemical_index.go
  53. 4 4
      models/data_manage/request/sci_hq_data.go
  54. 3 2
      models/rag/article_kb_mapping.go
  55. 8 2
      models/rag/promote_train_record.go
  56. 31 5
      models/rag/question.go
  57. 168 0
      models/rag/rag_eta_report.go
  58. 11 0
      models/rag/response/eta_report.go
  59. 12 1
      models/report.go
  60. 29 0
      models/report/report_free_layout.go
  61. 5 3
      models/report_chapter.go
  62. 118 0
      models/report_rai.go
  63. 25 1
      models/report_v2.go
  64. 2 0
      models/smart_report/smart_resource.go
  65. 4 2
      models/system/sys_admin.go
  66. 8 0
      models/system/sys_role.go
  67. 171 0
      routers/commentsRouter.go
  68. 2 0
      routers/router.go
  69. 49 0
      services/data/base_from_purang.go
  70. 88 15
      services/data/base_from_sci_hq.go
  71. 2 2
      services/data/base_from_ths_hf_classify.go
  72. 157 15
      services/data/chart_classify.go
  73. 7 0
      services/data/chart_info.go
  74. 1 1
      services/data/cross_variety/chart.go
  75. 2 2
      services/data/edb_classify.go
  76. 13 0
      services/data/excel/custom_analysis.go
  77. 17 0
      services/data/excel/excel_op.go
  78. 75 40
      services/data/future_good/profit_chart_info.go
  79. 1 0
      services/elastic.go
  80. 288 0
      services/elastic/rag_question.go
  81. 80 0
      services/eta_forum/chart_classify.go
  82. 531 27
      services/eta_forum/eta_forum_hub.go
  83. 20 1
      services/eta_forum/eta_forum_hub_lib.go
  84. 15 15
      services/excel/lucky_sheet.go
  85. 89 13
      services/llm/facade/llm_service.go
  86. 4 5
      services/llm/promote_service.go
  87. 272 0
      services/llm_report.go
  88. 20 0
      services/material/material.go
  89. 6 4
      services/report.go
  90. 4 0
      services/report_approve.go
  91. 404 0
      services/report_rai.go
  92. 110 11
      services/report_v2.go
  93. 3 2
      services/smart_report.go
  94. 34 2
      services/task.go
  95. 70 10
      services/wechat_platform.go
  96. 0 0
      static/imgs/ai/article/【专题报告】关税来袭黑色怎么看.md
  97. 0 0
      static/imgs/ai/article/【东海首席】黑色金属专题报告新一轮钢铁供给侧改革能否全面开启.md
  98. 0 0
      static/imgs/ai/article/【开源宏观】财政支出力度如何12月财政数据点评.md
  99. 0 0
      static/imgs/ai/article/巴菲特2025股东信1000字精华版来了附全文.md
  100. 16 0
      utils/common.go

+ 40 - 0
cache/wechat_platform.go → cache/llm.go

@@ -16,6 +16,11 @@ type WechatArticleOp struct {
 // @param source
 // @return bool
 func AddWechatArticleOpToCache(wechatPlatformId int, source string) bool {
+	// 如果不在发布和调试模式,那么就不加入缓存
+	if !utils.InArrayByStr([]string{utils.BusinessCodeRelease, utils.BusinessCodeDebug}, utils.BusinessCode) {
+		return true
+	}
+
 	record := new(WechatArticleOp)
 	record.Source = source
 	record.WechatPlatformId = wechatPlatformId
@@ -37,6 +42,10 @@ func AddWechatArticleOpToCache(wechatPlatformId int, source string) bool {
 // @param source
 // @return bool
 func AddWechatArticleLlmOpToCache(wechatPlatformId int, source string) bool {
+	// 如果不在发布和调试模式,那么就不加入缓存
+	if !utils.InArrayByStr([]string{utils.BusinessCodeRelease, utils.BusinessCodeDebug}, utils.BusinessCode) {
+		return true
+	}
 	record := new(WechatArticleOp)
 	record.Source = source
 	record.WechatPlatformId = wechatPlatformId
@@ -51,3 +60,34 @@ func AddWechatArticleLlmOpToCache(wechatPlatformId int, source string) bool {
 	}
 	return false
 }
+
+type RagEtaReportOpOp struct {
+	Source          string
+	ReportId        int
+	ReportChapterId int
+}
+
+// RagEtaReportOpToCache
+// @Description: 将eta报告入知识库操作加入缓存
+// @author: Roc
+// @datetime 2025-04-07 15:05:22
+// @param reportId int
+// @param reportChapterId int
+// @param source string
+// @return bool
+func RagEtaReportOpToCache(reportId, reportChapterId int, source string) bool {
+	record := new(RagEtaReportOpOp)
+	record.Source = source
+	record.ReportId = reportId
+	record.ReportChapterId = reportChapterId
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_ETA_REPORT_KNOWLEDGE, record)
+
+		utils.FileLog.Info(fmt.Sprintf("将eta报告入知识库操作加入缓存 加入缓存 RagEtaReportOpToCache LPush: 操作类型:%s,报告id:%d,章节id:%d", source, reportId, reportChapterId))
+		if err != nil {
+			fmt.Println("RagEtaReportOpToCache LPush Err:" + err.Error())
+		}
+		return true
+	}
+	return false
+}

+ 28 - 1
controllers/base_auth.go

@@ -174,13 +174,40 @@ func (c *BaseAuthController) Prepare() {
 				loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, session.Id)
 				loginInfo, _ := utils.Rc.RedisString(loginKey)
 				if loginInfo == `` {
+					// 超时退出的时候,也记录下当前请求日志,避免数据丢失
+					requestBody, err := url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+					if err != nil {
+						requestBody = string(c.Ctx.Input.RequestBody)
+					}
+					if requestBody == "" {
+						requestBody = c.Ctx.Input.URI()
+					}
+					utils.ApiLog.Info("uri:%s, authorization:%s, requestBody:%s", c.Ctx.Input.URI(), authorization, requestBody)
+
 					c.JSON(models.BaseResponse{Ret: 408, Msg: "超时未操作,系统自动退出!", ErrMsg: "超时未操作,系统自动退出!"}, false, false)
 					c.StopRun()
 					return
 				}
 
 				if loginInfo != "1" {
-					msg := `该账号于` + admin.LastLoginTime + "在其他网络登录。此客户端已退出登录。"
+					// 超时退出的时候,也记录下当前请求日志,避免数据丢失
+					requestBody, err := url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+					if err != nil {
+						requestBody = string(c.Ctx.Input.RequestBody)
+					}
+					if requestBody == "" {
+						requestBody = c.Ctx.Input.URI()
+					}
+					utils.ApiLog.Info("uri:%s, authorization:%s, requestBody:%s", c.Ctx.Input.URI(), authorization, requestBody)
+
+					var lastLoginTimeStr string
+					lastLoginTime, err := time.ParseInLocation(utils.FormatDateWallWithLoc, admin.LastLoginTime, time.Local)
+					if err == nil {
+						lastLoginTimeStr = lastLoginTime.Format(utils.FormatDateTime)
+					} else {
+						lastLoginTimeStr = admin.LastLoginTime
+					}
+					msg := `该账号于` + lastLoginTimeStr + "在其他网络登录。此客户端已退出登录。"
 					c.JSON(models.BaseResponse{Ret: 408, Msg: msg, ErrMsg: msg}, false, false)
 					c.StopRun()
 					return

+ 9 - 1
controllers/data_manage/ccf_data.go

@@ -327,9 +327,17 @@ func (this *EdbInfoController) CCFSingleData() {
 		br.ErrMsg = "获取数据失败,Err:" + err.Error()
 		return
 	}
+	edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_CCF, indexCode)
+	if err != nil && !utils.IsErrNoRow(err) {
+		br.Msg = "获取指标库信息失败"
+		br.ErrMsg = "获取数据源失败,Err:" + err.Error()
+		return
+	}
 	var ret data_manage.CCFSingleDataResp
 	var dataList []*data_manage.CCFSingleData
-
+	if edbInfo != nil {
+		ret.EdbInfoId = edbInfo.EdbInfoId
+	}
 	ret.ClassifyId = indexInfo.ClassifyId
 	ret.BaseFromCcfIndexId = indexInfo.BaseFromCcfIndexId
 	ret.IndexCode = indexInfo.IndexCode

+ 358 - 133
controllers/data_manage/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/eta_forum"
 	"eta/eta_api/utils"
 	"fmt"
 	"time"
@@ -381,7 +382,7 @@ func (this *ChartClassifyController) AddChartClassify() {
 	}
 
 	// 新增图表分类
-	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_DEFAULT, this.Lang, this.SysUser)
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_DEFAULT, req.IsSelected, this.Lang, this.SysUser)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "添加分类失败,Err:" + err.Error()
@@ -426,18 +427,35 @@ func (this *ChartClassifyController) EditChartClassify() {
 	}
 
 	// 编辑图表分类
-	_, err, errMsg, isSendEmail := data.EditChartClassifyV2(req.ChartClassifyId, req.ParentId, utils.CHART_SOURCE_DEFAULT, req.ChartClassifyName, this.Lang)
+	classifyInfo, isDeleteForumChart, tipCode, err, errMsg, isSendEmail := data.EditChartClassifyV2(req.ChartClassifyId, req.ParentId, utils.CHART_SOURCE_DEFAULT, req.ChartClassifyName, req.IsSelected, this.Lang)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "保存分类失败,Err:" + err.Error()
 		br.IsSendEmail = isSendEmail
 		return
 	}
+	var ret data_manage.EditChartClassifyResp
+	if tipCode != "" {
+		br.Ret = 200
+		br.Msg = "该分类下存在已上架的图表,请先将图表从资源库下架,再关闭该精选资源分类。"
+		ret.TipCode = tipCode
+		ret.TipMsg = "该分类下存在已上架的图表,请先将图表从资源库下架,再关闭该精选资源分类。"
+		br.IsSendEmail = false
+		br.Success = true
+		br.Data = ret
+		return
+	}
+
+	// 移除精选后,删除所有的图表
+	if isDeleteForumChart {
+		go eta_forum.ChartInfoDeleteBatch(classifyInfo, this.SysUser)
+	}
 
 	br.Ret = 200
 	br.Msg = "保存成功"
 	br.Success = true
 	br.IsAddLog = true
+	br.Data = ret
 }
 
 // @Title 删除图表检测接口
@@ -523,6 +541,24 @@ func (this *ChartClassifyController) DeleteChartClassifyCheck() {
 		tipsMsg = "可删除,进行删除操作"
 	}
 
+	if req.ChartInfoId > 0 {
+		chartInfo, err := data_manage.GetChartInfoById(req.ChartInfoId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				br.Msg = "图表已删除,请刷新页面"
+				br.ErrMsg = "指标不存在,Err:" + err.Error()
+				return
+			}
+			br.Msg = "删除失败"
+			br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+			return
+		}
+		if chartInfo.ForumChartInfoId > 0 {
+			deleteStatus = 3
+			tipsMsg = "删除后,该图表将从ETA投研资源库同步删除,影响客户的查看权限,是否确认删除?"
+		}
+	}
+
 	resp := new(data_manage.ChartClassifyDeleteCheckResp)
 	resp.DeleteStatus = deleteStatus
 	resp.TipsMsg = tipsMsg
@@ -614,6 +650,8 @@ func (this *ChartClassifyController) DeleteChartClassify() {
 			br.ErrMsg = "删除失败,Err:" + err.Error()
 			return
 		}
+
+		go eta_forum.ChartClassifySaveBatch(item.Source)
 	}
 	resp := new(data_manage.AddChartInfoResp)
 	//删除图表
@@ -699,6 +737,10 @@ func (this *ChartClassifyController) DeleteChartClassify() {
 			// 删除MY ETA 图表 es数据
 			//go data.EsDeleteMyChartInfoByChartInfoId(req.ChartInfoId)
 			go data.EsDeleteMyChartInfoByMyChartIds(myIds)
+			
+			if chartInfo.ForumChartInfoId > 0 {
+				go eta_forum.DeleteChartByForumChartInfoId(chartInfo.ForumChartInfoId)
+			}
 		}
 
 		var condition string
@@ -795,7 +837,6 @@ func (this *ChartClassifyController) ChartClassifyMove() {
 		br.Ret = 408
 		return
 	}
-
 	var req data_manage.MoveChartClassifyReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
 	if err != nil {
@@ -803,141 +844,197 @@ func (this *ChartClassifyController) ChartClassifyMove() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
-
-	if req.ClassifyId <= 0 {
+	if req.ClassifyId <= 0 && req.ChartInfoId <= 0 {
 		br.Msg = "参数错误"
-		br.ErrMsg = "分类id小于等于0"
-		return
-	}
-	//判断分类是否存在
-	chartClassifyInfo, err := data_manage.GetChartClassifyById(req.ClassifyId)
-	if err != nil {
-		if utils.IsErrNoRow(err) {
-			br.Msg = "分类不存在,请刷新页面"
-			return
-		}
-		br.Msg = "移动失败"
-		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
-		return
-	}
-
-	// 校验移动的父级目录下是否有重名分类
-	exists, e := data_manage.GetChartClassifyByParentIdAndName(req.ParentClassifyId, chartClassifyInfo.ChartClassifyName, req.ClassifyId)
-	if e != nil && !utils.IsErrNoRow(e) {
-		br.Msg = "移动失败"
-		br.ErrMsg = "获取父级目录下的同名分类失败, Err: " + e.Error()
-		return
-	}
-	if exists != nil && exists.ChartClassifyId > 0 {
-		br.Msg = "移动失败,分类名称已存在"
-		return
-	}
-	// 已授权分类id
-	permissionClassifyIdList, err := data_manage_permission.GetUserChartClassifyPermissionList(this.SysUser.AdminId, chartClassifyInfo.ChartClassifyId)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+		br.ErrMsg = "请选择拖动目标,分类目录或者指标"
 		return
 	}
-	// 权限校验
-	{
-		haveOperaAuth := data_manage_permission.CheckEdbClassifyPermissionByPermissionIdList(chartClassifyInfo.IsJoinPermission, chartClassifyInfo.ChartClassifyId, permissionClassifyIdList)
 
-		button := data.GetChartClassifyOpButton(this.SysUser, chartClassifyInfo.SysUserId, haveOperaAuth)
-		if !button.OpButton {
-			br.Msg = "无操作权限"
+	err, errMsg := data.MoveChartClassify(req, sysUser, utils.CHART_SOURCE_DEFAULT)
+	if errMsg != `` {
+		br.Msg = errMsg
+		br.ErrMsg = errMsg
+		if err != nil {
+			br.ErrMsg = err.Error()
+		} else {
 			br.IsSendEmail = false
-			return
 		}
-	}
-
-	if chartClassifyInfo.Source != utils.CHART_SOURCE_DEFAULT {
-		br.Msg = "分类异常"
-		br.ErrMsg = "分类异常,不是ETA图库的分类"
 		return
 	}
-	updateCol := make([]string, 0)
-
-	//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
-	if chartClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
-		parentChartClassifyInfo, err := data_manage.GetChartClassifyById(req.ParentClassifyId)
-		if err != nil {
-			br.Msg = "移动失败"
-			br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
-			return
-		}
-		chartClassifyInfo.ParentId = parentChartClassifyInfo.ChartClassifyId
-		chartClassifyInfo.Level = parentChartClassifyInfo.Level + 1
-		chartClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
-	}
-
-	//如果有传入 上一个兄弟节点分类id
-	if req.PrevClassifyId > 0 {
-		//上一个兄弟节点
-		prevClassify, err := data_manage.GetChartClassifyById(req.PrevClassifyId)
-		if err != nil {
-			br.Msg = "移动失败"
-			br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
-			return
-		}
-
-		//如果是移动在两个兄弟节点之间
-		if req.NextClassifyId > 0 {
-			//下一个兄弟节点
-			nextClassify, err := data_manage.GetChartClassifyById(req.NextClassifyId)
-			if err != nil {
-				br.Msg = "移动失败"
-				br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
-				return
-			}
-			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
-			if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == chartClassifyInfo.Sort {
-				//变更兄弟节点的排序
-				updateSortStr := `sort + 2`
-				_ = data_manage.UpdateChartClassifySortByParentId(prevClassify.ParentId, prevClassify.ChartClassifyId, prevClassify.Sort, updateSortStr)
-			} else {
-				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
-				if nextClassify.Sort-prevClassify.Sort == 1 {
-					//变更兄弟节点的排序
-					updateSortStr := `sort + 1`
-					_ = data_manage.UpdateChartClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
-				}
-			}
-		}
-
-		chartClassifyInfo.Sort = prevClassify.Sort + 1
-		chartClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "Sort", "ModifyTime")
-
-	} else {
-		firstClassify, err := data_manage.GetFirstChartClassifyByParentId(chartClassifyInfo.ParentId)
-		if err != nil && !utils.IsErrNoRow(err) {
-			br.Msg = "移动失败"
-			br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
-			return
-		}
-
-		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
-		if firstClassify != nil && firstClassify.Sort == 0 {
-			updateSortStr := ` sort + 1 `
-			_ = data_manage.UpdateChartClassifySortByParentId(firstClassify.ParentId, firstClassify.ChartClassifyId-1, 0, updateSortStr)
-		}
-
-		chartClassifyInfo.Sort = 0 //那就是排在第一位
-		chartClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "Sort", "ModifyTime")
-	}
-
-	//更新
-	if len(updateCol) > 0 {
-		err = chartClassifyInfo.Update(updateCol)
-		if err != nil {
-			br.Msg = "移动失败"
-			br.ErrMsg = "修改失败,Err:" + err.Error()
-			return
-		}
-	}
+	// var req data_manage.MoveChartClassifyReq
+	// err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	// if err != nil {
+	// 	br.Msg = "参数解析异常!"
+	// 	br.ErrMsg = "参数解析失败,Err:" + err.Error()
+	// 	return
+	// }
+
+	// if req.ClassifyId <= 0 {
+	// 	br.Msg = "参数错误"
+	// 	br.ErrMsg = "分类id小于等于0"
+	// 	return
+	// }
+	// //判断分类是否存在
+	// chartClassifyInfo, err := data_manage.GetChartClassifyById(req.ClassifyId)
+	// if err != nil {
+	// 	if utils.IsErrNoRow(err) {
+	// 		br.Msg = "分类不存在,请刷新页面"
+	// 		return
+	// 	}
+	// 	br.Msg = "移动失败"
+	// 	br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+	// 	return
+	// }
+	// oldParentId := chartClassifyInfo.ParentId
+	// oldLevelPath := chartClassifyInfo.LevelPath
+	// oldSelected := chartClassifyInfo.IsSelected
+
+	// // 校验移动的父级目录下是否有重名分类
+	// exists, e := data_manage.GetChartClassifyByParentIdAndName(req.ParentClassifyId, chartClassifyInfo.ChartClassifyName, req.ClassifyId)
+	// if e != nil && !utils.IsErrNoRow(e) {
+	// 	br.Msg = "移动失败"
+	// 	br.ErrMsg = "获取父级目录下的同名分类失败, Err: " + e.Error()
+	// 	return
+	// }
+	// if exists != nil && exists.ChartClassifyId > 0 {
+	// 	br.Msg = "移动失败,分类名称已存在"
+	// 	return
+	// }
+	// // 已授权分类id
+	// permissionClassifyIdList, err := data_manage_permission.GetUserChartClassifyPermissionList(this.SysUser.AdminId, chartClassifyInfo.ChartClassifyId)
+	// if err != nil {
+	// 	br.Msg = "获取失败"
+	// 	br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+	// 	return
+	// }
+	// // 权限校验
+	// {
+	// 	haveOperaAuth := data_manage_permission.CheckEdbClassifyPermissionByPermissionIdList(chartClassifyInfo.IsJoinPermission, chartClassifyInfo.ChartClassifyId, permissionClassifyIdList)
+
+	// 	button := data.GetChartClassifyOpButton(this.SysUser, chartClassifyInfo.SysUserId, haveOperaAuth)
+	// 	if !button.OpButton {
+	// 		br.Msg = "无操作权限"
+	// 		br.IsSendEmail = false
+	// 		return
+	// 	}
+	// }
+
+	// if chartClassifyInfo.Source != utils.CHART_SOURCE_DEFAULT {
+	// 	br.Msg = "分类异常"
+	// 	br.ErrMsg = "分类异常,不是ETA图库的分类"
+	// 	return
+	// }
+	// updateCol := make([]string, 0)
+
+	// var parentChartClassifyInfo *data_manage.ChartClassify
+	// if req.ParentClassifyId > 0 {
+	// 	parentChartClassifyInfo, err = data_manage.GetChartClassifyById(req.ParentClassifyId)
+	// 	if err != nil {
+	// 		br.Msg = "移动失败"
+	// 		br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+	// 		return
+	// 	}
+	// }
+	// //判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+	// if chartClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
+	// 	if chartClassifyInfo.Level != parentChartClassifyInfo.Level+1 { //禁止层级调整
+	// 		br.Msg = "移动失败"
+	// 		br.ErrMsg = "不支持目录层级变更"
+	// 		return
+	// 	}
+	// 	chartClassifyInfo.ParentId = parentChartClassifyInfo.ChartClassifyId
+	// 	chartClassifyInfo.Level = parentChartClassifyInfo.Level + 1
+	// 	chartClassifyInfo.ModifyTime = time.Now()
+	// 	updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+	// }else if chartClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId == 0 {
+	// 	br.Msg = "移动失败"
+	// 	br.ErrMsg = "不支持目录层级变更"
+	// 	return
+	// }
+
+	// //如果有传入 上一个兄弟节点分类id
+	// if req.PrevClassifyId > 0 {
+	// 	//上一个兄弟节点
+	// 	prevClassify, err := data_manage.GetChartClassifyById(req.PrevClassifyId)
+	// 	if err != nil {
+	// 		br.Msg = "移动失败"
+	// 		br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+	// 		return
+	// 	}
+
+	// 	//如果是移动在两个兄弟节点之间
+	// 	if req.NextClassifyId > 0 {
+	// 		//下一个兄弟节点
+	// 		nextClassify, err := data_manage.GetChartClassifyById(req.NextClassifyId)
+	// 		if err != nil {
+	// 			br.Msg = "移动失败"
+	// 			br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+	// 			return
+	// 		}
+	// 		//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+	// 		if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == chartClassifyInfo.Sort {
+	// 			//变更兄弟节点的排序
+	// 			updateSortStr := `sort + 2`
+	// 			_ = data_manage.UpdateChartClassifySortByParentId(prevClassify.ParentId, prevClassify.ChartClassifyId, prevClassify.Sort, updateSortStr)
+	// 		} else {
+	// 			//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+	// 			if nextClassify.Sort-prevClassify.Sort == 1 {
+	// 				//变更兄弟节点的排序
+	// 				updateSortStr := `sort + 1`
+	// 				_ = data_manage.UpdateChartClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+	// 			}
+	// 		}
+	// 	}
+
+	// 	chartClassifyInfo.Sort = prevClassify.Sort + 1
+	// 	chartClassifyInfo.ModifyTime = time.Now()
+	// 	updateCol = append(updateCol, "Sort", "ModifyTime")
+
+	// } else {
+	// 	firstClassify, err := data_manage.GetFirstChartClassifyByParentId(chartClassifyInfo.ParentId)
+	// 	if err != nil && !utils.IsErrNoRow(err) {
+	// 		br.Msg = "移动失败"
+	// 		br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+	// 		return
+	// 	}
+
+	// 	//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+	// 	if firstClassify != nil && firstClassify.Sort == 0 {
+	// 		updateSortStr := ` sort + 1 `
+	// 		_ = data_manage.UpdateChartClassifySortByParentId(firstClassify.ParentId, firstClassify.ChartClassifyId-1, 0, updateSortStr)
+	// 	}
+
+	// 	chartClassifyInfo.Sort = 0 //那就是排在第一位
+	// 	chartClassifyInfo.ModifyTime = time.Now()
+	// 	updateCol = append(updateCol, "Sort", "ModifyTime")
+	// }
+
+	// //更新
+	// if len(updateCol) > 0 {
+	// 	err = chartClassifyInfo.Update(updateCol)
+	// 	if err != nil {
+	// 		br.Msg = "移动失败"
+	// 		br.ErrMsg = "修改失败,Err:" + err.Error()
+	// 		return
+	// 	}
+	// }
+	// if oldParentId != req.ParentClassifyId {
+	// 	if err = data.UpdateChartClassifyLevelPathWithChildren(chartClassifyInfo, parentChartClassifyInfo, oldParentId, oldLevelPath); err != nil {
+	// 		br.Msg = "修改分类失败"
+	// 		br.ErrMsg = "更新分类level_path失败,Err:" + err.Error()
+	// 		return
+	// 	}
+	// }
+	// // 如果是精选目录,则需要同步到ETA资源库
+	// if chartClassifyInfo.Source == utils.CHART_SOURCE_DEFAULT {
+	// 	// 如果当前目录的精选标识发生变化,需要同步更新子目录的精选标识
+	// 	if err = data.UpdateChildClassifySelection(chartClassifyInfo, parentChartClassifyInfo, oldSelected); err != nil {
+	// 		br.Msg = "修改分类失败"
+	// 		br.ErrMsg = "更新子目录精选标识失败,Err:" + err.Error()
+	// 		return
+	// 	}
+	// 	go eta_forum.ChartClassifySaveBatch(chartClassifyInfo.Source)
+	// }
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "移动成功"
@@ -1095,7 +1192,7 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 		br.Msg = "参数错误"
 		return
 	}
-
+	
 	// 获取当前账号的不可见指标
 	noPermissionChartIdMap := make(map[int]bool)
 	{
@@ -1111,9 +1208,20 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 		}
 	}
 	var allNodes []*data_manage.ChartClassifyItems
+	// 获取是否精选资源标识
+	isSelected, _ := this.GetInt("IsSelected")
+	if chartClassifyId > 0 {
+		chartClassifyInfo, err := data_manage.GetChartClassifyById(chartClassifyId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+			return
+		}
+		isSelected = chartClassifyInfo.IsSelected
+	}
 	isShowMe, _ := this.GetBool("IsShowMe")
 	if isShowMe {
-		allChartInfo, err := data_manage.GetChartClassifyAndInfoByParentIdForMe(chartClassifyId, sysUser.AdminId)
+		allChartInfo, err := data_manage.GetChartClassifyAndInfoByParentIdForMe(chartClassifyId, sysUser.AdminId, isSelected)
 		if err != nil && !utils.IsErrNoRow(err) {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取数据失败,Err:" + err.Error()
@@ -1136,7 +1244,7 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 		//fmt.Println("source my classify")
 		//return
 	} else {
-		allChartInfo, err := data_manage.GetChartClassifyAndInfoByParentId(chartClassifyId)
+		allChartInfo, err := data_manage.GetChartClassifyAndInfoByParentId(chartClassifyId, isSelected)
 		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取数据失败,Err:" + err.Error()
@@ -1194,3 +1302,120 @@ func (this *ChartClassifyController) ChartClassifyChartListV3() {
 	br.Msg = "获取成功"
 	br.Data = resp
 }
+
+// SetChartClassifyResourceStatus
+// @Title 一键从资源库上架/下架
+// @Description 一键从资源库上架/下架
+// @Param   ChartClassifyId   query   bool  true       "图片分类id"
+// @Param   ResourceStatus   query   bool  true       "资源状态"
+// @Success 200 {object} models.BaseResponse
+// @router /chart_classify/resource_status [post]
+func (this *ChartClassifyController) SetChartClassifyResourceStatus() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req data_manage.SetChartClassifyResourceStatusReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	chartClassifyId := req.ChartClassifyId	
+	if chartClassifyId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	resourceStatus := req.ResourceStatus
+	if resourceStatus <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	chartClassifyInfo, err := data_manage.GetChartClassifyById(chartClassifyId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	if resourceStatus == utils.ChartClassifyResourceStatusDown {
+		errMsg, err := eta_forum.SetChartClassifyResourceStatusDown(chartClassifyInfo, this.SysUser)
+		if err != nil {
+			if errMsg != "" {
+				br.Msg = errMsg
+				br.ErrMsg = err.Error()
+				return
+			}
+			br.Msg = "操作失败"
+			br.ErrMsg = err.Error()
+			return
+		}
+	} else if resourceStatus == utils.ChartClassifyResourceStatusUp {
+		errMsg, err := eta_forum.SetChartClassifyResourceStatusUp(chartClassifyInfo, this.SysUser)
+		if err != nil {
+			if errMsg != "" {
+				br.Msg = errMsg
+				br.ErrMsg = err.Error()
+				return
+			}
+			br.Msg = "操作失败"
+			br.ErrMsg = err.Error()
+			return
+		}
+	}
+	
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+
+// 一键更新至资源库
+// @Title 一键更新至资源库
+// @Description 一键更新至资源库
+// @Param   ChartClassifyId   query   bool  true       "图片分类id"
+// @Success 200 {object} models.BaseResponse
+// @router /chart_classify/forum_chart/update [post]
+func (this *ChartClassifyController) UpdateChartClassifyResource() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req data_manage.ChartClassifyResourceUpdate
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	
+	chartClassifyInfo, err := data_manage.GetChartClassifyById(req.ChartClassifyId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+	if chartClassifyInfo.Source != utils.CHART_SOURCE_DEFAULT {
+		br.Msg = "分类来源错误"
+		return
+	}
+	if chartClassifyInfo.IsSelected != utils.ChartClassifyIsSelected {
+		br.Msg = "该分类不是精选分类"
+		return
+	}
+
+	err = eta_forum.ChartBatchUpdateAndUpload(chartClassifyInfo, this.SysUser)
+	if err != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = err.Error()
+		return
+	}
+	
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 151 - 3
controllers/data_manage/chart_info.go

@@ -783,7 +783,7 @@ func (this *ChartInfoController) ChartInfoMove() {
 			return
 		}
 	}
-
+	oldClassifyId := chartInfo.ChartClassifyId
 	//如果改变了分类,那么移动该图表数据
 	if chartInfo.ChartClassifyId != req.ChartClassifyId {
 		//查询需要修改的分类下是否存在同一个图表名称
@@ -874,6 +874,20 @@ func (this *ChartInfoController) ChartInfoMove() {
 		}
 		//添加es数据
 		go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId)
+
+		// 判断是否为精选目录
+			// 如果该目录不是精选目录,且该图表已经上架,则需撤回该图表
+			if oldClassifyId != req.ChartClassifyId {
+				parentChartClassifyInfo, err := data_manage.GetChartClassifyById(req.ChartClassifyId)
+				if err != nil {
+					br.Msg = "移动失败"
+					br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+					return
+				}
+				if parentChartClassifyInfo.IsSelected == 0 && chartInfo.ForumChartInfoId > 0 {
+					go eta_forum.DeleteChart(chartInfo.ChartInfoId)
+				}
+			}
 	}
 
 	if err != nil {
@@ -1601,6 +1615,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 	}
 
 	resp := new(data_manage.ChartInfoDetailResp)
+	isSelected := 0
 	// 图表数据权限
 	{
 		// 图表分类
@@ -1610,7 +1625,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 			br.ErrMsg = "获取图表分类信息失败,Err:" + err.Error()
 			return
 		}
-
+		isSelected = chartClassify.IsSelected
 		// 已授权分类id
 		permissionChartIdList, permissionClassifyIdList, err := data_manage_permission.GetUserChartAndClassifyPermissionList(this.SysUser.AdminId, chartInfo.ChartInfoId, chartInfo.ChartClassifyId)
 		if err != nil {
@@ -1818,6 +1833,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 	}
 
 	// 图表当前分类的分类树
+	
 	classifyLevels := make([]string, 0)
 	{
 		list, e := data_manage.GetChartClassifyAllBySource(utils.CHART_SOURCE_DEFAULT)
@@ -1836,6 +1852,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 	}
 
 	resp.ClassifyLevels = classifyLevels
+	resp.IsSelected = isSelected
 
 	//图表操作权限
 	chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId, chartInfo.HaveOperaAuth)
@@ -2358,7 +2375,7 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 		}
 		// 当前列表中的分类map
 		chartClassifyMap := make(map[int]*data_manage.ChartClassify)
-
+		isSelectClassifyMap := make(map[int]bool)
 		// 图表分类
 		{
 			chartClassifyList, err := data_manage.GetChartClassifyByIdList(classifyIdList)
@@ -2369,6 +2386,9 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 			}
 			for _, v := range chartClassifyList {
 				chartClassifyMap[v.ChartClassifyId] = v
+				if v.IsSelected == 1 {
+					isSelectClassifyMap[v.ChartClassifyId] = true
+				}
 			}
 		}
 		// 图表
@@ -2418,6 +2438,9 @@ func (this *ChartInfoController) ChartInfoSearchByEs() {
 			if tmp.SearchText == "" {
 				tmp.SearchText = v.ChartName
 			}
+			if _, ok := isSelectClassifyMap[v.ChartClassifyId]; ok {
+				tmp.IsSelected = 1
+			}
 			finalList = append(finalList, tmp)
 		}
 	}
@@ -4449,6 +4472,9 @@ func (this *ChartInfoController) UpdateToForum() {
 		return
 	}
 
+	// 更新指标数据
+	utils.Rc.LPush(utils.CACHE_KEY_EDB_DATA_UPDATE_LOG, []byte("1"))
+	
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -5115,8 +5141,130 @@ func (this *ChartInfoController) ModifyChartList() {
 		br.ErrMsg = "更新图表分类失败,Err:" + err.Error()
 		return
 	}
+	
+	go eta_forum.ChartInfoDeleteBatchByChartInfoIds(chartIds, req.ChartClassifyId)
 
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
 }
+
+// 查询图表简介列表
+// @Title 查询图表简介列表
+// @Description 查询图表简介列表
+// @Param   ChartInfoId   query   int  true       "图表id"
+// @Success 200 {object} models.ChartDescriptionListResponse
+// @router /chart_info/description/list [get]
+func (this *ChartInfoController) GetChartDescriptionList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	chartInfoId, _ := this.GetInt("ChartInfoId")
+	if chartInfoId <= 0 {
+		br.Msg = "请选择图表"
+		return
+	}
+
+	chartDescriptionList, err := data_manage.GetChartDescriptionByChartInfoId(chartInfoId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表简介失败,Err:" + err.Error()
+		return
+	}
+	list := make([]*data_manage.ChartDescriptionList, 0)
+	response := new(data_manage.ChartDescriptionListResponse)
+	for _, v := range chartDescriptionList {
+		list = append(list, &data_manage.ChartDescriptionList{
+			Id:               v.Id,
+			Description:      v.Description,
+			ChartInfoId:      v.ChartInfoId,
+			SysUserId:        v.SysUserId,
+			SysUserRealName:  v.SysUserRealName,
+			CreateTime:       v.CreateTime.Format(utils.FormatDateTime),
+		})
+	}
+	response.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = response
+}
+
+// 添加图表简介
+// @Title 添加图表简介
+// @Description 添加图表简介
+// @Param   ChartInfoId   query   int  true       "图表id"
+// @Success 200 {object} models.ChartDescriptionListResponse
+// @router /chart_info/description/add [post]
+func (this *ChartInfoController) AddChartDescription() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}	
+
+	var req data_manage.ChartDescriptionAddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ChartInfoId <= 0 {
+		br.Msg = "请选择图表"
+		return
+	}
+	if req.Description == "" {
+		br.Msg = "请输入简介"
+		return
+	}
+	// 判断图表是否存在
+	_, err = data_manage.GetChartInfoById(req.ChartInfoId)
+	if err != nil {
+		if !utils.IsErrNoRow(err) {
+			br.Msg = "图表不存在"
+			return
+		}
+		br.Msg = "获取图表信息失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+	item := &data_manage.ChartDescription{
+		ChartInfoId: req.ChartInfoId,
+		Description: req.Description,
+		SysUserId: sysUser.AdminId,
+		SysUserRealName: sysUser.RealName,
+		ModifyTime: time.Now(),
+		CreateTime: time.Now(),
+	}
+	err = data_manage.AddChartDescription(item)
+	if err != nil {
+		br.Msg = "添加失败"
+		br.ErrMsg = "添加图表简介失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "添加成功"
+}
+

+ 1 - 1
controllers/data_manage/correlation/correlation_chart_classify.go

@@ -263,7 +263,7 @@ func (this *CorrelationChartClassifyController) AddChartClassify() {
 	}
 
 	// 新增图表分类
-	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_CORRELATION, this.Lang, this.SysUser)
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_CORRELATION, 0,this.Lang, this.SysUser)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "添加分类失败,Err:" + err.Error()

+ 43 - 5
controllers/data_manage/edb_info.go

@@ -2123,7 +2123,7 @@ func (this *EdbInfoController) EdbInfoSearch() {
 
 				//edb指标信息
 				edbInfoItem, err := data_manage.GetEdbInfoByEdbCode(source, edbCode)
-				if edbInfoItem != nil {
+				if edbInfoItem != nil && edbInfoItem.EdbInfoId > 0 {
 					searchItem.EdbName = edbInfoItem.EdbName
 				}
 			}
@@ -6820,18 +6820,56 @@ func (this *EdbInfoController) ModifyEdbList() {
 			br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
 			return
 		}
-		classifyIdsArr := make([]int, 0)
+
+		// 提交上来的分类id
+		tmpClassifyIdsArr := make([]int, 0)
 		for _, v := range classifyIds {
 			if v != `` {
 				id, _ := strconv.Atoi(v)
-				classifyIdsArr = append(classifyIdsArr, id)
+				tmpClassifyIdsArr = append(tmpClassifyIdsArr, id)
 			}
 		}
 
-		if len(permissionClassifyIdList) > 0 {
-			classifyIdsArr = utils.IntersectInt(permissionClassifyIdList, classifyIdsArr)
+		// 我有权限的分类id
+		classifyIdsArr := make([]int, 0)
+		if len(tmpClassifyIdsArr) > 0 {
+			classifyInfoList, err := data_manage.GetEdbClassifyByIdList(tmpClassifyIdsArr)
+			if err != nil && !utils.IsErrNoRow(err) {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取数据失败,Err:" + err.Error()
+				return
+			}
+
+			permissionClassifyIdMap := make(map[int]bool)
+			for _, classifyId := range permissionClassifyIdList {
+				permissionClassifyIdMap[classifyId] = true
+			}
+			for _, v := range classifyInfoList {
+				// 没有加入指标目录管控,那么就直接加入并进入下个循环
+				if v.IsJoinPermission == 0 {
+					classifyIdsArr = append(classifyIdsArr, v.ClassifyId)
+					continue
+				}
+
+				// 如果加入指标目录管控了,同时自己有权限,那么就加入
+				if _, ok := permissionClassifyIdMap[v.ClassifyId]; ok {
+					classifyIdsArr = append(classifyIdsArr, v.ClassifyId)
+					continue
+				}
+
+			}
 		}
 
+		if len(classifyIds) > 0 && len(classifyIdsArr) <= 0 {
+			br.Msg = `所选中的指标目录没有权限`
+			br.IsSendEmail = false
+			return
+		}
+
+		//if len(permissionClassifyIdList) > 0 {
+		//	classifyIdsArr = utils.IntersectInt(permissionClassifyIdList, classifyIdsArr)
+		//}
+
 		condition += " AND edb_info_type = 0 "
 		if len(classifyIdsArr) > 0 {
 			if !req.SubClassify {

+ 6 - 1
controllers/data_manage/excel/balance_table.go

@@ -1425,7 +1425,12 @@ func downloadBalanceTable(excelInfo *excel.ExcelInfo, lang string) (savePath, zi
 				err = fmt.Errorf("转换成table失败,Err:" + err.Error())
 				return
 			}
-
+			//tableData, err = excel2.HandleRuleToTableCell(childExcelInfo.ExcelInfoId, tableData)
+			//if err != nil {
+			//	errMsg = "获取失败"
+			//	err = fmt.Errorf("处理条件格式管理规则失败,Err:%w", err)
+			//	return
+			//}
 			// 将单个sheet的数据写入到excel
 			err = tableData.WriteExcelSheetData(xlsxFile, childExcelInfo.ExcelName)
 			if err != nil {

+ 6 - 0
controllers/data_manage/excel/excel_info.go

@@ -2725,6 +2725,12 @@ func (c *ExcelInfoController) Download() {
 			br.ErrMsg = "转换成table失败,Err:" + err.Error()
 			return
 		}
+		//tableData, err = excel.HandleRuleToTableCell(excelInfo.ExcelInfoId, tableData)
+		//if err != nil {
+		//	br.Msg = "获取失败"
+		//	br.ErrMsg = "处理条件格式管理规则失败,Err:" + err.Error()
+		//	return
+		//}
 	case utils.BALANCE_TABLE: // 混合表格
 		savePath, fileName, uploadDir, err, errMsg := downloadBalanceTable(excelInfo, c.Lang)
 		if err != nil {

+ 1 - 1
controllers/data_manage/future_good/future_good_chart_classify.go

@@ -203,7 +203,7 @@ func (this *FutureGoodChartClassifyController) AddChartClassify() {
 	}
 
 	// 新增图表分类
-	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, 0, req.Level, utils.CHART_SOURCE_FUTURE_GOOD, this.Lang, this.SysUser)
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, 0, req.Level, utils.CHART_SOURCE_FUTURE_GOOD, 0, this.Lang, this.SysUser)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "添加分类失败,Err:" + err.Error()

+ 1 - 1
controllers/data_manage/line_equation/line_chart_classify.go

@@ -208,7 +208,7 @@ func (this *LineEquationChartClassifyController) AddChartClassify() {
 	}
 
 	// 新增图表分类
-	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_LINE_EQUATION, this.Lang, this.SysUser)
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_LINE_EQUATION, 0, this.Lang, this.SysUser)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "添加分类失败,Err:" + err.Error()

+ 1 - 1
controllers/data_manage/line_feature/classify.go

@@ -208,7 +208,7 @@ func (this *LineFeaturesChartClassifyController) AddChartClassify() {
 	}
 
 	// 新增图表分类
-	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, this.Lang, this.SysUser)
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_LINE_FEATURE_STANDARD_DEVIATION, 0, this.Lang, this.SysUser)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "添加分类失败,Err:" + err.Error()

+ 10 - 0
controllers/data_manage/manual_edb.go

@@ -435,6 +435,16 @@ func (c *ManualEdbController) EdbList() {
 		if num > 0 {
 			condition += ` AND a.classify_id in (` + utils.GetOrmInReplace(num) + `) `
 			pars = append(pars, classifyIdList)
+		} else {
+			resp := models.EdbListResp{
+				List:   []*models.EdbInfoListItem{},
+				Paging: paging.GetPaging(currentIndex, pageSize, 0),
+			}
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			br.Data = resp
+			return
 		}
 
 	} else {

+ 26 - 14
controllers/data_manage/my_chart.go

@@ -70,30 +70,42 @@ func (this *MyChartController) ChartList() {
 	condition += ` AND source = ? `
 	pars = append(pars, utils.CHART_SOURCE_DEFAULT)
 
-	chartClassifyIds := make([]int, 0)
-	if chartClassifyId > 0 {
+	// 是否显示精选资源
+	isSelected, _ := this.GetInt("IsSelected", -1)
 
-		list, e := data_manage.GetChartClassifyAllBySource(utils.CHART_SOURCE_DEFAULT)
-		if e != nil {
+	chartClassifyIds := make([]int, 0)
+	var classifyList []*data_manage.ChartClassifyItems
+	var err error
+	if isSelected >= 0 {
+		classifyList, err = data_manage.GetChartClassifyAllBySourceIsSelected(utils.CHART_SOURCE_DEFAULT, isSelected)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取图表分类失败, Err: %v", err)
+			return
+		}
+	} else {
+		classifyList, err = data_manage.GetChartClassifyAllBySource(utils.CHART_SOURCE_DEFAULT)
+		if err != nil {
 			br.Msg = "获取失败"
-			br.ErrMsg = fmt.Sprintf("获取图表分类失败, Err: %v", e)
+			br.ErrMsg = fmt.Sprintf("获取图表分类失败, Err: %v", err)
 			return
 		}
-		parents := data.GetChartClassifyChildrenRecursive(list, chartClassifyId)
+	}
+	if chartClassifyId > 0 {
+		parents := data.GetChartClassifyChildrenRecursive(classifyList, chartClassifyId)
 		sort.Slice(parents, func(i, j int) bool {
 			return parents[i].Level < parents[i].Level
 		})
 		for _, v := range parents {
 			chartClassifyIds = append(chartClassifyIds, v.ChartClassifyId)
 		}
-
-		//chartClassifyId, err := data_manage.GetChartClassify(chartClassifyId)
-		//if err != nil && err.Error() != utils.ErrNoRow() {
-		//	br.Msg = "获取图表信息失败"
-		//	br.ErrMsg = "获取信息失败,GetChartClassify,Err:" + err.Error()
-		//	return
-		//}
-		condition += " AND chart_classify_id IN(" + utils.GetOrmInReplace(len(chartClassifyIds)) + ") "
+		condition += " AND chart_classify_id IN (" + utils.GetOrmInReplace(len(chartClassifyIds)) + ") "
+		pars = append(pars, chartClassifyIds)
+	} else if isSelected >= 0 {
+		for _, v := range classifyList {
+			chartClassifyIds = append(chartClassifyIds, v.ChartClassifyId)
+		}
+		condition += " AND chart_classify_id IN (" + utils.GetOrmInReplace(len(chartClassifyIds)) + ") "
 		pars = append(pars, chartClassifyIds)
 	}
 

+ 55 - 12
controllers/data_manage/mysteel_chemical_data.go

@@ -1745,7 +1745,7 @@ func (c *EdbInfoController) AddCheck() {
 	br.Success = true
 }
 
-// MysteelChemicalSearch
+// MysteelChemicalBatchSearch
 // @Title 钢联化工指标查询
 // @Description 钢联化工指标查询
 // @Param   BaseFromMysteelChemicalClassifyIds   query   string  true       "分类id"
@@ -1779,17 +1779,58 @@ func (this *EdbInfoController) MysteelChemicalBatchSearch() {
 		condition += " AND (index_name like ? OR index_code like ?) "
 		pars = utils.GetLikeKeywordPars(pars, keyword, 2)
 	}
+	// 频度(多选)
+	frequencies := this.GetString("Frequencies")
+	frequencies = strings.TrimSpace(frequencies)
+	if frequencies != "" {
+		freArr := strings.Split(frequencies, ",")
+		if len(freArr) > 0 {
+			condition += " AND frequency IN ?"
+			pars = append(pars, freArr)
+		}
+	}
+	resp := new(data_manage.MysteelChemicalPageListResp)
+	resp.List = make([]*data_manage.MysteelChemicalList, 0)
+
+	//if classifyIdStr == `` && keyword == `` {
+	//	var list = make([]*data_manage.MysteelChemicalList, 0)
+	//	br.Ret = 200
+	//	br.Success = true
+	//	br.Msg = "获取成功"
+	//	br.Data = list
+	//	return
+	//}
+
+	// 分页查询
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize10
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
 
-	if classifyIdStr == `` && keyword == `` {
-		var list = make([]*data_manage.MysteelChemicalList, 0)
+	// 获取未加入指标库的原始指标
+	total, e := data_manage.GetNoEdbMysteelChemicalIndexCount(condition, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取未加入指标库的钢联指标数失败, %v", e)
+		return
+	}
+	if total <= 0 {
+		page := paging.GetPaging(currentIndex, pageSize, 0)
+		resp.Paging = page
+		br.Data = resp
 		br.Ret = 200
 		br.Success = true
 		br.Msg = "获取成功"
-		br.Data = list
 		return
 	}
 
-	list, err := data_manage.GetMysteelChemicalIndex(condition, pars)
+	list, err := data_manage.GetNoEdbMysteelChemicalIndexPageList(condition, pars, startSize, pageSize)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -1802,6 +1843,7 @@ func (this *EdbInfoController) MysteelChemicalBatchSearch() {
 			classifyIdInts = append(classifyIdInts, v.BaseFromMysteelChemicalClassifyId)
 		}
 	}
+	classifyListMap := make(map[int]int, 0)
 	if len(classifyIdInts) > 0 {
 		// 查询父级分类信息
 		classifyList, e := data_manage.GetBaseFromMysteelChemicalClassifyByIds(classifyIdInts)
@@ -1810,20 +1852,21 @@ func (this *EdbInfoController) MysteelChemicalBatchSearch() {
 			br.ErrMsg = "获取失获取目录信息失败,Err:" + e.Error()
 			return
 		}
-		classifyListMap := make(map[int]int, 0)
 		for _, v := range classifyList {
 			classifyListMap[v.BaseFromMysteelChemicalClassifyId] = v.ParentId
 		}
-		for _, v := range list {
-			v.UniqueCode = fmt.Sprint(v.BaseFromMysteelChemicalClassifyId, "_", v.Id)
-			if p, ok := classifyListMap[v.BaseFromMysteelChemicalClassifyId]; ok {
-				v.ParentClassifyId = p
-			}
+	}
+	for _, v := range list {
+		if p, ok := classifyListMap[v.BaseFromMysteelChemicalClassifyId]; ok {
+			v.ParentClassifyId = p
 		}
+		resp.List = append(resp.List, v)
 	}
 
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp.Paging = page
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
-	br.Data = list
+	br.Data = resp
 }

+ 1045 - 0
controllers/data_manage/purang_data.go

@@ -0,0 +1,1045 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+)
+
+type BaseFromPurangController struct {
+	controllers.BaseAuthController
+}
+
+// PurangClassify
+// @Title Purang数据分类
+// @Description Purang数据分类接口
+// @Success 200 {object} data_manage.BaseFromPurangClassify
+// @router /purang/classify [get]
+func (this *BaseFromPurangController) PurangClassify() {
+	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.GetAllBaseFromPurangClassify()
+	if err != nil && !utils.IsErrNoRow(err) {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	//组装一级分类
+	rootMap := make(map[int][]*data_manage.BaseFromPurangClassifyItems)
+	list := make([]*data_manage.BaseFromPurangClassifyItems, 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.BaseFromPurangClassifyItems, 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.BaseFromPurangClassifyResp
+	ret.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// PurangIndexData
+// @Title 获取Purang数据
+// @Description 获取Purang数据接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   ClassifyId   query   string  true       "分类id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /purang/index/data [get]
+func (this *BaseFromPurangController) PurangIndexData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+	var startSize int
+
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	classifyId, _ := this.GetInt("ClassifyId")
+	if classifyId < 0 {
+		br.Msg = "请选择分类"
+		br.ErrMsg = "请选择分类"
+		return
+	}
+	// 增加频度请求入参
+	frequency := this.GetString("Frequency")
+
+	//获取指标
+	var condition string
+	var pars []interface{}
+
+	if classifyId > 0 {
+		condition += ` AND classify_id=? `
+		pars = append(pars, classifyId)
+	}
+	if frequency != "" {
+		condition += ` AND frequency=? `
+		pars = append(pars, frequency)
+	}
+
+	purangList, err := data_manage.GetPurangIndex(condition, pars)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	edbCodeList := make([]string, 0)
+	for _, v := range purangList {
+		edbCodeList = append(edbCodeList, v.IndexCode)
+	}
+	edbInfoMap := make(map[string]*data_manage.EdbInfo)
+	dataMap := make(map[string][]*data_manage.BaseFromPurangData)
+	total := 0
+	if len(edbCodeList) > 0 {
+		edbInfoList, err := data_manage.GetEdbInfoByEdbCodeList(utils.DATA_SOURCE_PURANG, edbCodeList)
+		if err != nil {
+			br.Msg = "获取数据源失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range edbInfoList {
+			edbInfoMap[v.EdbCode] = v
+		}
+		// 首先对分类下的指标按照日期进行分页,再针对日期,进行排序
+		dataTimes, err := data_manage.GetPurangIndexDataTimePageByCodes(edbCodeList, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据日期信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataTimes) > 0 {
+			startDate := utils.GormDateStrToDateStr(dataTimes[len(dataTimes)-1])
+			endDate := utils.GormDateStrToDateStr(dataTimes[0])
+			// 把截止日往后加1天
+			endDateT, _ := time.ParseInLocation(utils.FormatDate, endDate, time.Local)
+			endDate = endDateT.AddDate(0, 0, 1).Format(utils.FormatDate)
+			dataList, e := data_manage.GetPurangIndexDataByDataTime(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.GetPurangIndexDataTimePageCount(edbCodeList)
+		if err != nil {
+			br.Msg = "获取数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+
+	resultList := make([]*data_manage.BaseFromPurangIndexList, 0)
+
+	for _, v := range purangList {
+		product := new(data_manage.BaseFromPurangIndexList)
+		product.BaseFromPurangIndexId = v.BaseFromPurangIndexId
+		product.Unit = v.Unit
+		product.IndexCode = v.IndexCode
+		product.IndexName = v.IndexName
+		product.Frequency = v.Frequency
+		product.ModifyTime = v.ModifyTime
+		product.ClassifyId = v.ClassifyId
+		if edb, ok := edbInfoMap[v.IndexCode]; ok {
+			product.EdbInfoId = edb.EdbInfoId
+			product.EdbExist = 1
+		}
+
+		dataListTmp, ok := dataMap[v.IndexCode]
+		if !ok {
+			dataListTmp = make([]*data_manage.BaseFromPurangData, 0)
+		}
+		product.DataList = dataListTmp
+		product.Paging = page
+		resultList = append(resultList, product)
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resultList
+}
+
+// PurangSearchList
+// @Title Purang模糊搜索
+// @Description Purang模糊搜索
+// @Param   Keyword   query   string  ture       "关键字搜索"
+// @Success 200 {object} models.BaseResponse
+// @router /purang/search_list [get]
+func (this *BaseFromPurangController) PurangSearchList() {
+	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.BaseFromPurangIndexSearchItem, 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.GetPurangItemList(condition)
+			if err != nil {
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				br.Msg = "获取失败"
+				return
+			}
+		}
+
+	} else {
+		list, err = data_manage.GetPurangItemList("")
+		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.GetBaseFromPurangClassifyByIds(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
+}
+
+// PurangSingleData
+// @Title 获取Purang数据
+// @Description 获取Purang单条数据接口
+// @Param   IndexCode   query   string  true       "指标唯一编码"
+// @Success 200 {object} models.BaseResponse
+// @router /purang/single_data [get]
+func (this *BaseFromPurangController) PurangSingleData() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	indexCode := this.GetString("IndexCode")
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	var total int64
+	page := paging.GetPaging(currentIndex, pageSize, int(total))
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	indexInfo, err := data_manage.GetBaseFromPurangIndexByIndexCode(indexCode)
+	if err != nil {
+		br.Msg = "获取指标信息失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+	total, err = data_manage.GetPurangIndexDataTotalByCode(indexCode)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	page = paging.GetPaging(currentIndex, pageSize, int(total))
+	dataTmpList, err := data_manage.GetPurangIndexDataByCode(indexCode, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取数据失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_PURANG, indexCode)
+	if err != nil && !utils.IsErrNoRow(err) {
+		br.Msg = "获取数据源失败"
+		br.ErrMsg = "获取数据源失败,Err:" + err.Error()
+		return
+	}
+
+	var ret data_manage.PurangSingleDataResp
+	var dataList []*data_manage.PurangSingleData
+
+	if edbInfo != nil {
+		ret.EdbInfoId = edbInfo.EdbInfoId
+		ret.EdbExist = 1
+	}
+	ret.ClassifyId = indexInfo.ClassifyId
+	ret.BaseFromPurangIndexId = indexInfo.BaseFromPurangIndexId
+	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.PurangSingleData{
+			Value:    v.Value,
+			DataTime: v.DataTime,
+		}
+		dataList = append(dataList, tmp)
+	}
+	ret.Data = dataList
+	ret.Paging = page
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// PurangIndexList
+// @Title Purang指标列表
+// @Description Purang指标列表
+// @Param   ClassifyId   query   int  true       "分类id"
+// @Success 200 {object} data_manage.BaseFromPurangClassifyResp
+// @router /purang/classify/index/list [get]
+func (this *BaseFromPurangController) PurangIndexList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	classifyId, _ := this.GetInt("ClassifyId", 0)
+	indexList, err := data_manage.GetPurangIndexByClassifyId(classifyId)
+	if err != nil && !utils.IsErrNoRow(err) {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
+		return
+	}
+
+	var ret data_manage.BaseFromPurangClassifyResp
+	list := make([]*data_manage.BaseFromPurangClassifyItems, 0)
+	for _, v := range indexList {
+		classify := new(data_manage.BaseFromPurangClassifyItems)
+		classify.ClassifyId = classifyId
+		classify.BaseFromPurangIndexId = v.BaseFromPurangIndexId
+		classify.IndexCode = v.IndexCode
+		classify.ClassifyName = v.IndexName
+		classify.UniqueCode = fmt.Sprintf("%d_%d", classifyId, v.BaseFromPurangIndexId)
+		list = append(list, classify)
+	}
+	ret.List = list
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = ret
+}
+
+// PurangNameCheck
+// @Title 加入指标库的重名检测
+// @Description 加入指标库的重名检测
+// @Param   ClassifyIds   query   string  true       "分类id, 多个分类用英文"
+// @Param   Keyword   query   string  true       "关键词, 指标ID/指标名称"
+// @Success 200 {object} NameCheckResult
+// @router /purang/edb_info/name_check [post]
+func (this *BaseFromPurangController) PurangNameCheck() {
+	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_PURANG, 0, utils.EDB_DATA_LIMIT)
+		if err != nil && !utils.IsErrNoRow(err) {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取钢联已存在信息失败,Err:" + err.Error()
+			return
+		}
+		if len(dataItems) <= 0 {
+			respItem, err := data.AddEdbData(utils.DATA_SOURCE_PURANG, 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
+}
+
+// PurangAddCheck
+// @Title 加入指标库指标Id检测
+// @Description 加入指标库指标Id检测
+// @Param	request	body request.BatchAddCheckReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /purang/edb_info/add_check [post]
+func (c *BaseFromPurangController) PurangAddCheck() {
+	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_PURANG)
+	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.GetPurangIndex(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.BaseFromPurangIndexList, 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
+}
+
+// PurangEdbInfoAdd
+// @Title 新增指标接口
+// @Description 新增指标接口
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /purang/edb_info/add [post]
+func (this *BaseFromPurangController) PurangEdbInfoAdd() {
+	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.GetPurangIndexDataCount(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_PURANG, 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
+}
+
+// ExportPurangList
+// @Title 导出Purang数据
+// @Description 导出Purang数据
+// @Param   ClassifyId   query   int  true       "关键字搜索"
+// @Param   IndexCode   query   string  true       "指标编码"
+// @Success 200  导出成功
+// @router /purang/export [get]
+func (this *BaseFromPurangController) ExportPurangList() {
+	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.GetBaseFromPurangClassifyById(classifyId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				br.Msg = "分类不存在"
+				return
+			}
+			br.Msg = "下载失败"
+			br.ErrMsg = "获取分类失败,Err:" + err.Error()
+			return
+		}
+		classifyName = classifyInfo.ClassifyName
+		childClassify, err := data_manage.GetBaseFromPurangClassifyByParentId(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.GetPurangIndex(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.BaseFromPurangIndexList)
+	for _, v := range indexList {
+		codeList = append(codeList, v.IndexCode)
+		frequenciesMap[v.Frequency] = append(frequenciesMap[v.Frequency], v)
+	}
+	dataListMap := make(map[string][]*data_manage.BaseFromPurangData)
+	if len(indexList) > 0 {
+		allDataList, e := data_manage.GetPurangIndexDataByCodes(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.BaseFromPurangIndexId)
+		}
+		dataTimeList, err := data_manage.GetPurangDataDataTimeByIndexId(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.BaseFromPurangData)
+			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 Purang数据频度
+// @Description Purang数据频度接口
+// @Param   ClassifyId   query   string  true       "分类Id"
+// @Success 200 {object} data_manage.LzFrequency
+// @router /purang/frequency [get]
+func (this *BaseFromPurangController) 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.GetPurangFrequencyByClassifyId(classifyId)
+	if err != nil {
+		br.Msg = "获取频度失败"
+		br.ErrMsg = "获取频度失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = frequencyList
+} 

+ 1 - 1
controllers/data_manage/range_analysis/chart_classify.go

@@ -262,7 +262,7 @@ func (this *RangeChartClassifyController) AddChartClassify() {
 	}
 
 	// 新增图表分类
-	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_RANGE_ANALYSIS, this.Lang, this.SysUser)
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_RANGE_ANALYSIS, 0, this.Lang, this.SysUser)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "添加分类失败,Err:" + err.Error()

+ 49 - 13
controllers/data_manage/sci_hq_data.go

@@ -264,7 +264,7 @@ func (this *SciHqDataController) MoveClassify() {
 		return
 	}
 
-	err, errMsg := data.MoveSciHqClassify(req.ClassifyId, req.ParentId, req.PrevClassifyId, req.NextClassifyId)
+	err, errMsg := data.MoveSciHqClassify(req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -333,12 +333,12 @@ func (this *SciHqDataController) BatchIndexList() {
 	}
 	if req.IsSelectAll {
 		if len(req.SelectedId) > 0 {
-			condition += ` AND base_from_sci_hq_index_id NOT IN (` + utils.GetOrmInReplace(len(req.SelectedId)) + `)`
+			condition += ` AND base_from_sci_hq_index_id NOT IN (?)`
 			pars = append(pars, req.SelectedId)
 		}
 	} else {
 		if len(req.SelectedId) > 0 {
-			condition += ` AND base_from_sci_hq_index_id IN (` + utils.GetOrmInReplace(len(req.SelectedId)) + `)`
+			condition += ` AND base_from_sci_hq_index_id IN (?)`
 			pars = append(pars, req.SelectedId)
 		}
 	}
@@ -353,11 +353,14 @@ func (this *SciHqDataController) BatchIndexList() {
 			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)
-			}
+		var childIds []int
+		for _, v := range childClassify {
+			childIds = append(childIds, v.ClassifyId)
+		}
+		if len(childIds) > 0 {
+			childIds = append(childIds, req.ClassifyId)
+			condition += `AND classify_id IN (?)`
+			pars = append(pars, childIds)
 		} else {
 			condition += ` AND classify_id=?`
 			pars = append(pars, req.ClassifyId)
@@ -465,6 +468,11 @@ func (this *SciHqDataController) IndexPageList() {
 		indexList = tmpIndexList
 	}
 
+	// 唯一编码前端定位用
+	for _, v := range indexList {
+		v.UniqueCode = fmt.Sprintf("%d_%d", v.ClassifyId, v.BaseFromSciHqIndexId)
+	}
+
 	page := paging.GetPaging(currrentIndex, pageSize, total)
 	resp := new(response.SciHqIndexPageListResp)
 	resp.List = indexList
@@ -757,6 +765,19 @@ func (this *SciHqDataController) AddEdbInfo() {
 		return
 	}
 
+	// 刷新指标数据
+	refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+		return
+	}
+	if refreshRes != nil && refreshRes.Ret != 200 {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+		return
+	}
+
 	// 试用平台更新用户累计新增指标数
 	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
 	if e != nil {
@@ -1091,6 +1112,19 @@ func (this *SciHqDataController) BatchAdd() {
 			continue
 		}
 
+		// 刷新指标数据
+		refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+			return
+		}
+		if refreshRes != nil && refreshRes.Ret != 200 {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+			return
+		}
+
 		// 试用平台更新用户累计新增指标数
 		if utils.BusinessCode == utils.BusinessCodeSandbox {
 			go func() {
@@ -1417,11 +1451,13 @@ func (this *SciHqDataController) ExportSciHqList() {
 			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)
-			}
+		var childIds []int
+		for _, v := range childClassify {
+			childIds = append(childIds, v.ClassifyId)
+		}
+		if len(childIds) > 0 {
+			condition += `AND classify_id IN (?)`
+			pars = append(pars, childIds)
 		} else {
 			condition += ` AND classify_id=?`
 			pars = append(pars, classifyId)

+ 1 - 0
controllers/data_manage/smm_data.go

@@ -165,6 +165,7 @@ func (this *EdbInfoController) SmmIndexList() {
 			Level:                0,
 			Sort:                 0,
 			Children:             nil,
+			UniqueCode:           fmt.Sprintf("%d_%d", v.ClassifyId, v.BaseFromSmmIndexId),
 		}
 		list = append(list, tmp)
 	}

+ 9 - 2
controllers/data_source/data_source.go

@@ -217,7 +217,10 @@ func (c *DataSourceController) SearchByEs() {
 		}
 	}
 
-	// 分类的唯一编码(前端定位用)
+	// 个别数据源需要统一唯一编码前端定位用
+	if primaryIdKey == "" {
+		primaryIdKey = "Id"
+	}
 	if classifyIdKey == "" {
 		classifyIdKey = "ClassifyId"
 	}
@@ -228,10 +231,14 @@ func (c *DataSourceController) SearchByEs() {
 		for _, v := range listMap {
 			classifyId, ok := v[classifyIdKey].(int)
 			if !ok {
-				v["ClassifyUniqueCode"] = ""
+				continue
+			}
+			id, ok := v[primaryIdKey].(int)
+			if !ok {
 				continue
 			}
 			v["ClassifyUniqueCode"] = strconv.Itoa(classifyId)
+			v["UniqueCode"] = fmt.Sprintf("%d_%d", classifyId, id)
 		}
 	}
 	//if source == utils.DATA_SOURCE_MANUAL {

+ 6 - 2
controllers/llm/llm_http/request.go

@@ -22,12 +22,16 @@ type UserChatRecordReq struct {
 }
 
 type GenerateContentReq struct {
-	WechatArticleId int    `json:"WechatArticleId" description:"公众号Id"`
+	Source          int    `json:"Source" description:"来源,0:公众号文章,1:eta报告"`
+	WechatArticleId int    `json:"WechatArticleId" description:"公众号文章Id"`
 	Promote         string `json:"Promote" description:"提示词"`
+	LLMModel        string `json:"LLMModel"`
 }
 type SaveContentReq struct {
-	WechatArticleId int             `json:"WechatArticleId" description:"公众号Id"`
+	Source          int             `json:"Source" description:"来源,0:公众号文章,1:eta报告"`
+	WechatArticleId int             `json:"WechatArticleId" description:"公众号文章Id"`
 	Title           string          `json:"Title" description:"标题"`
+	Llm             string          `json:"LLM"`
 	Promote         json.RawMessage `json:"Promote" description:"提示词"`
 	AigcContent     json.RawMessage `json:"AigcContent" description:"生成内容"`
 }

+ 44 - 11
controllers/llm/promote_controller.go

@@ -9,6 +9,7 @@ import (
 	"eta/eta_api/services/llm/facade"
 	"eta/eta_api/utils"
 	"fmt"
+	"strings"
 	"time"
 )
 
@@ -50,26 +51,51 @@ func (pCtrl *PromoteController) GenerateContent() {
 		br.ErrMsg = "公众号文章编号非法"
 		return
 	}
+	if gcReq.LLMModel == "" {
+		br.Msg = "LLM模型不能为空"
+		br.ErrMsg = "LLM模型不能为空"
+		return
+	}
 	userSendTime := time.Now()
 	userContent := llm_http.Content{
 		Content:  gcReq.Promote,
 		Role:     "user",
 		SendTime: userSendTime.Format(utils.FormatDateTime),
 	}
-	article, err := rag.GetArticleById(gcReq.WechatArticleId)
-	if err != nil {
-		br.Msg = "获取公众号内容失败"
-		br.ErrMsg = "获取公众号内容失败,Err:" + err.Error()
-		return
-	}
-	if article.TextContent == "" {
-		br.Msg = "暂不支持纯文本以外的内容生成"
-		br.ErrMsg = "暂不支持纯文本以外的内容生成"
-		return
+
+	switch gcReq.Source {
+	case 0:
+		article, err := rag.GetArticleById(gcReq.WechatArticleId)
+		if err != nil {
+			br.Msg = "获取公众号内容失败"
+			br.ErrMsg = "获取公众号内容失败,Err:" + err.Error()
+			return
+		}
+		if article.TextContent == "" {
+			br.Msg = "暂不支持纯文本以外的内容生成"
+			br.ErrMsg = "暂不支持纯文本以外的内容生成"
+			return
+		}
+	case 1:
+		obj := rag.RagEtaReport{}
+		article, err := obj.GetById(gcReq.WechatArticleId)
+		if err != nil {
+			br.Msg = "获取报告内容失败"
+			br.ErrMsg = "获取报告内容失败,Err:" + err.Error()
+			return
+		}
+		if article.TextContent == "" {
+			br.Msg = "暂不支持纯文本以外的内容生成"
+			br.ErrMsg = "暂不支持纯文本以外的内容生成"
+			return
+		}
+
 	}
 	res, err := facade.AIGCBaseOnPromote(facade.AIGC{
 		Promote:   gcReq.Promote,
+		Source:    gcReq.Source,
 		ArticleId: gcReq.WechatArticleId,
+		LLMModel:  gcReq.LLMModel,
 	})
 	if err != nil {
 		br.Msg = err.Error()
@@ -86,9 +112,12 @@ func (pCtrl *PromoteController) GenerateContent() {
 		Promote: userContent,
 		Answer:  aiContent,
 	}
+	llm := strings.ReplaceAll(gcReq.LLMModel, ":", "")
 	saveContentReq := rag.PromoteTrainRecord{
+		Source:          gcReq.Source,
 		WechatArticleId: gcReq.WechatArticleId,
 		Title:           userContent.Content,
+		Llm:             llm,
 		AigcContent:     res.Answer,
 		AigcSendTime:    aiSendTime,
 		TemplatePromote: userContent.Content,
@@ -189,9 +218,12 @@ func (pCtrl *PromoteController) SavePromoteContent() {
 		br.ErrMsg = "AI生成时间不能为空"
 		return
 	}
+	llm := strings.ReplaceAll(gcReq.Llm, ":", "")
 	saveContentReq := rag.PromoteTrainRecord{
+		Source:          gcReq.Source,
 		WechatArticleId: gcReq.WechatArticleId,
 		Title:           titile,
+		Llm:             llm,
 		AigcContent:     assistantContent.Content,
 		AigcSendTime:    assistantSendTime,
 		TemplatePromote: userContent.Content,
@@ -262,6 +294,7 @@ func (pCtrl *PromoteController) PromoteContentList() {
 		pCtrl.ServeJSON()
 	}()
 	wechatArticleId, _ := pCtrl.GetInt("WechatArticleId")
+	source, _ := pCtrl.GetInt("Source")
 	sysUser := pCtrl.SysUser
 	if sysUser == nil {
 		br.Msg = "请登录"
@@ -276,7 +309,7 @@ func (pCtrl *PromoteController) PromoteContentList() {
 		return
 	}
 
-	list, err := rag.GetRecordList(wechatArticleId)
+	list, err := rag.GetRecordList(wechatArticleId, source)
 	if err != nil {
 		br.Msg = "查询列表失败"
 		br.ErrMsg = "查询列表失败,err:" + err.Error()

+ 196 - 19
controllers/llm/question.go

@@ -7,6 +7,8 @@ import (
 	"eta/eta_api/models/rag"
 	"eta/eta_api/models/rag/request"
 	"eta/eta_api/models/rag/response"
+	"eta/eta_api/services"
+	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -54,23 +56,131 @@ func (c *QuestionController) List() {
 	}
 	startSize = utils.StartIndex(currentIndex, pageSize)
 
-	var condition string
-	var pars []interface{}
+	var total int
+	viewList := make([]rag.QuestionView, 0)
 
-	if keyWord != "" {
-		condition += fmt.Sprintf(` AND %s like ?`, rag.QuestionColumns.QuestionContent)
-		pars = append(pars, `%`+keyWord+`%`)
+	if keyWord == `` {
+		var condition string
+		var pars []interface{}
+
+		if keyWord != "" {
+			condition += fmt.Sprintf(` AND %s like ?`, rag.QuestionColumns.QuestionContent)
+			pars = append(pars, `%`+keyWord+`%`)
+		}
+		obj := new(rag.Question)
+		tmpTotal, list, err := obj.GetPageListByCondition(condition, pars, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = tmpTotal
+
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ListToViewList(list)
+		}
+	} else {
+		sortMap := map[string]string{
+			//"ArticleCreateTime": "desc",
+			//"WechatArticleId":   "desc",
+		}
+		tmpTotal, list, err := elastic.RagQuestionEsSearch(keyWord, startSize, pageSize, sortMap)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = int(tmpTotal)
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ToViewList(list)
+		}
 	}
 
-	obj := new(rag.Question)
-	total, list, err := obj.GetPageListByCondition(condition, pars, startSize, pageSize)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取失败,Err:" + err.Error()
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.QuestionListListResp{
+		List:   viewList,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// TitleList
+// @Title 标题列表
+// @Description 标题列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /question/title/list [get]
+func (c *QuestionController) TitleList() {
+	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"
 		return
 	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyWord := c.GetString("KeyWord")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
 
-	viewList := obj.ListToViewList(list)
+	var total int
+	viewList := make([]rag.QuestionView, 0)
+
+	if keyWord == `` {
+		var condition string
+		var pars []interface{}
+
+		if keyWord != "" {
+			condition += fmt.Sprintf(` AND %s like ?`, rag.QuestionColumns.QuestionContent)
+			pars = append(pars, `%`+keyWord+`%`)
+		}
+		obj := new(rag.Question)
+		tmpTotal, list, err := obj.GetTitlePageListByCondition(condition, pars, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = tmpTotal
+
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ListToViewList(list)
+		}
+	} else {
+		sortMap := map[string]string{
+			//"ArticleCreateTime": "desc",
+			//"WechatArticleId":   "desc",
+		}
+		tmpTotal, list, err := elastic.RagQuestionEsSearch(keyWord, startSize, pageSize, sortMap)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = int(tmpTotal)
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ToViewList(list)
+		}
+	}
 
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := response.QuestionListListResp{
@@ -84,6 +194,47 @@ func (c *QuestionController) List() {
 	br.Data = resp
 }
 
+// Detail
+// @Title 列表
+// @Description 列表
+// @Param   QuestionId   query   int  true       "问题id"
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /question/detail [get]
+func (c *QuestionController) Detail() {
+	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"
+		return
+	}
+	questionId, _ := c.GetInt("QuestionId")
+	if questionId <= 0 {
+		br.Msg = "问题id不能为空"
+		br.ErrMsg = "问题id不能为空"
+		return
+	}
+
+	obj := new(rag.Question)
+	questionItem, err := obj.GetByID(questionId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Data = questionItem.ToView()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
 // Add
 // @Title 新增问题
 // @Description 新增问题
@@ -109,16 +260,18 @@ func (c *QuestionController) Add() {
 		br.IsSendEmail = false
 		return
 	}
-	obj := rag.Question{}
-	_, err = obj.GetByCondition(` AND question_content = ? `, []interface{}{req.Content})
-	if err == nil {
-		br.Msg = "问题已入库,请不要重复添加"
-		br.IsSendEmail = false
-		return
-	}
+	//obj := rag.Question{}
+	//_, err = obj.GetByCondition(` AND question_content = ? `, []interface{}{req.Content})
+	//if err == nil {
+	//	br.Msg = "问题已入库,请不要重复添加"
+	//	br.IsSendEmail = false
+	//	return
+	//}
 
+	title := utils.GetFirstNChars(req.Content, 20)
 	item := &rag.Question{
 		QuestionId:      0,
+		QuestionTitle:   title,
 		QuestionContent: req.Content,
 		Sort:            0,
 		ModifyTime:      time.Now(),
@@ -131,6 +284,9 @@ func (c *QuestionController) Add() {
 		return
 	}
 
+	// 新增/编辑ES数据
+	go services.AddOrEditEsRagQuestion(item.QuestionId)
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = `添加成功`
@@ -178,14 +334,17 @@ func (c *QuestionController) Edit() {
 		}
 		return
 	}
+	item.QuestionTitle = utils.GetFirstNChars(req.Content, 20)
 	item.QuestionContent = req.Content
 	item.ModifyTime = time.Now()
-	err = item.Update([]string{"question_content", "modify_time"})
+	err = item.Update([]string{"question_title", "question_content", "modify_time"})
 	if err != nil {
 		br.Msg = "修改失败"
 		br.ErrMsg = "修改失败,Err:" + err.Error()
 		return
 	}
+	// 新增/编辑ES数据
+	go services.AddOrEditEsRagQuestion(item.QuestionId)
 
 	br.Ret = 200
 	br.Success = true
@@ -235,7 +394,25 @@ func (c *QuestionController) Del() {
 		return
 	}
 
+	// 删除ES数据
+	go services.DelEsRagQuestion(item.QuestionId)
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = `删除成功`
 }
+
+//func init() {
+//	// 问题加到es
+//	{
+//		obj := rag.Question{}
+//		list, _ := obj.GetListByCondition(``, ` `, []interface{}{}, 0, 10000)
+//		total := len(list)
+//		for k, item := range list {
+//			fmt.Println(k, "/", total)
+//			services.AddOrEditEsRagQuestion(item.QuestionId)
+//		}
+//
+//		fmt.Println("结束了")
+//	}
+//}

+ 249 - 0
controllers/llm/report.go

@@ -0,0 +1,249 @@
+package llm
+
+import (
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/rag"
+	"eta/eta_api/models/rag/response"
+	"eta/eta_api/services"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"html"
+	"strings"
+)
+
+// RagEtaReportController
+// @Description: eta报告的接口
+type RagEtaReportController struct {
+	controllers.BaseAuthController
+}
+
+// ArticleList
+// @Title 我关注的接口
+// @Description 我关注的接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} *rag.RagEtaReportListListResp
+// @router /eta_report/article/list [get]
+func (c *RagEtaReportController) ArticleList() {
+	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"
+		return
+	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyWord := c.GetString("KeyWord")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var total int
+	viewList := make([]rag.RagEtaReportView, 0)
+
+	var condition string
+	var pars []interface{}
+
+	condition += fmt.Sprintf(` AND %s = ? AND %s = ? `, rag.RagEtaReportColumns.IsDeleted, rag.RagEtaReportColumns.IsPublished)
+	pars = append(pars, 0, 1)
+
+	if keyWord != "" {
+		condition += fmt.Sprintf(` AND %s like ? `, rag.RagEtaReportColumns.Title)
+		pars = append(pars, `%`+keyWord+`%`)
+	}
+
+	obj := new(rag.RagEtaReport)
+	tmpTotal, list, err := obj.GetPageListByCondition(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	total = tmpTotal
+
+	if list != nil && len(list) > 0 {
+		viewList = obj.ListToViewList(list)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.RagEtaReportListListResp{
+		List:   viewList,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ArticleDetail
+// @Title 文章详情
+// @Description 我关注的接口
+// @Param   RagEtaReportId   query   int  true       "知识库与eta报告关联的id"
+// @Success 200 {object} []*rag.WechatArticle
+// @router /eta_report/article/detail [get]
+func (c *RagEtaReportController) ArticleDetail() {
+	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"
+		return
+	}
+	ragEtaReportId, _ := c.GetInt("RagEtaReportId")
+	if ragEtaReportId <= 0 {
+		br.Msg = "请选择文章"
+		br.IsSendEmail = false
+		return
+	}
+	obj := new(rag.RagEtaReport)
+	item, err := obj.GetById(ragEtaReportId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if item.IsDeleted == 1 {
+		br.Msg = "文章已删除"
+		br.IsSendEmail = false
+		return
+	}
+	resp := item.ToView()
+
+	content := ``
+
+	// 获取源报告信息
+	{
+		if item.ReportChapterId <= 0 {
+			// 普通报告
+			reportInfo, err := models.GetReportByReportId(item.ReportId)
+			if err != nil && !utils.IsErrNoRow(err) {
+				br.Msg = "获取报告详情失败"
+				br.ErrMsg = "获取源报告详情失败,Err:" + err.Error()
+				return
+			}
+			content = reportInfo.Content
+			// 如果是rai报告,则使用纯文本的报告内容
+			if reportInfo.RaiReportId > 0 {
+				content = strings.ReplaceAll(item.TextContent, "\n", "<br />")
+			}
+		} else {
+			// 章节报告
+			reportChapterInfo, err := models.GetReportChapterInfoById(item.ReportChapterId)
+			if err != nil && !utils.IsErrNoRow(err) {
+				br.Msg = "获取报告详情失败"
+				br.ErrMsg = "获取源报告章节详情失败,Err:" + err.Error()
+				return
+			}
+			content = reportChapterInfo.Content
+		}
+
+		if content != `` {
+			content = html.UnescapeString(content)
+			businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
+			if err != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取配置失败,Err:" + err.Error()
+				return
+			}
+
+			if businessConf.ConfVal == `true` {
+				tokenMap := make(map[string]string)
+				content = services.HandleReportContent(content, "add", tokenMap)
+			}
+		}
+	}
+
+	resp.Content = content
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ArticleDel
+// @Title 删除文章
+// @Description 我关注的接口
+// @Param   RagEtaReportId   query   int  true       "知识库与eta报告关联的id"
+// @Success 200 {object} []*rag.WechatPlatform
+// @router /eta_report/article/del [get]
+func (c *RagEtaReportController) ArticleDel() {
+	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"
+		return
+	}
+	ragEtaReportId, _ := c.GetInt("RagEtaReportId")
+	if ragEtaReportId <= 0 {
+		br.Msg = "请选择文章"
+		br.IsSendEmail = false
+		return
+	}
+	obj := new(rag.RagEtaReport)
+	item, err := obj.GetById(ragEtaReportId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if item.IsDeleted == 1 {
+		br.Msg = "文章已删除"
+		br.IsSendEmail = false
+		return
+	}
+	item.IsDeleted = 1
+	err = item.Update([]string{"is_deleted"})
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+//// 修复历史ETA报告到知识库
+//func init() {
+//	idList, err := models.GetAllPublishReportId()
+//	if err != nil {
+//		fmt.Println("查询失败:", err.Error())
+//		return
+//	}
+//	for _, v := range idList {
+//		cache.RagEtaReportOpToCache(v, 0, "publish")
+//	}
+//	fmt.Println("写入完成")
+//}

+ 6 - 0
controllers/report.go

@@ -1919,6 +1919,12 @@ func (this *ReportController) SendMsg() {
 		return
 	}
 
+	//
+	if reportInfo.IsPublicPublish != 1 {
+		br.Msg = "报告未公开"
+		return
+	}
+
 	// 更新报告发布时间
 	videoNameDate := `(` + time.Now().Format("0102") + `)`
 	if err = models.UpdateReportPublishTime(req.ReportId, videoNameDate); err != nil {

+ 2 - 2
controllers/report_chapter.go

@@ -1455,7 +1455,7 @@ func (this *ReportController) PublishDayWeekReportChapter() {
 
 	// 更新章节ES
 	{
-		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId, reportInfo.IsPublicPublish)
 	}
 
 	// 同时发布报告
@@ -1739,7 +1739,7 @@ func (this *ReportController) CancelPublishReportChapter() {
 
 	// 更新章节ES
 	{
-		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId, reportInfo.IsPublicPublish)
 	}
 
 	br.Ret = 200

+ 18 - 7
controllers/report_v2.go

@@ -2,6 +2,7 @@ package controllers
 
 import (
 	"encoding/json"
+	"eta/eta_api/cache"
 	"eta/eta_api/models"
 	"eta/eta_api/models/report"
 	"eta/eta_api/models/report_approve"
@@ -720,24 +721,28 @@ func (this *ReportController) Detail() {
 
 	if item.HeadResourceId > 0 {
 		headResource, err := smart_report.GetResourceItemById(item.HeadResourceId)
-		if err != nil {
+		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "操作失败"
 			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
 			return
 		}
-		item.HeadImg = headResource.ImgUrl
-		item.HeadStyle = headResource.Style
+		if headResource != nil && headResource.ResourceId > 0 {
+			item.HeadImg = headResource.ImgUrl
+			item.HeadStyle = headResource.Style
+		}
 	}
 
 	if item.EndResourceId > 0 {
 		endResource, err := smart_report.GetResourceItemById(item.EndResourceId)
-		if err != nil {
+		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "操作失败"
-			br.ErrMsg = "获取资源库版失败, Err: " + err.Error()
+			br.ErrMsg = "获取资源库版失败, Err: " + err.Error()
 			return
 		}
-		item.EndImg = endResource.ImgUrl
-		item.EndStyle = endResource.Style
+		if endResource != nil && endResource.ResourceId > 0 {
+			item.EndImg = endResource.ImgUrl
+			item.EndStyle = endResource.Style
+		}
 	}
 
 	item.Content = services.HandleReportContentTable(item.Id, item.Content)
@@ -1488,6 +1493,9 @@ func (this *ReportController) PublishCancelReport() {
 		_ = services.ResetMiniProgramReportDetailCover(reportInfo.Id)
 	}()
 
+	// 报告取消发布成功后,需要将相关信息入知识库
+	go cache.RagEtaReportOpToCache(reportInfo.Id, 0, `un_publish`)
+
 	br.Ret = 200
 	br.Success = true
 }
@@ -1855,6 +1863,9 @@ func (this *ReportController) CancelApprove() {
 		_ = services.ResetMiniProgramReportDetailCover(reportItem.Id)
 	}()
 
+	// 报告发布成功后,需要将相关信息入知识库
+	go cache.RagEtaReportOpToCache(reportItem.Id, 0, `un_publish`)
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 12 - 7
controllers/smart_report/smart_report.go

@@ -391,24 +391,29 @@ func (this *SmartReportController) Detail() {
 	resp := smart_report.FormatSmartReport2Item(item)
 	if resp.HeadResourceId > 0 {
 		headResource, err := smart_report.GetResourceItemById(resp.HeadResourceId)
-		if err != nil {
+		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "操作失败"
 			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
 			return
 		}
-		resp.HeadImg = headResource.ImgUrl
-		resp.HeadStyle = headResource.Style
+		if headResource != nil && headResource.ResourceId > 0 {
+			resp.HeadImg = headResource.ImgUrl
+			resp.HeadStyle = headResource.Style
+		}
 	}
 
 	if resp.EndResourceId > 0 {
 		endResource, err := smart_report.GetResourceItemById(resp.EndResourceId)
-		if err != nil {
+		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "操作失败"
-			br.ErrMsg = "获取资源库版失败, Err: " + err.Error()
+			br.ErrMsg = "获取资源库版失败, Err: " + err.Error()
 			return
 		}
-		resp.EndImg = endResource.ImgUrl
-		resp.EndStyle = endResource.Style
+		if endResource != nil && endResource.ResourceId > 0 {
+			resp.EndImg = endResource.ImgUrl
+			resp.EndStyle = endResource.Style
+		}
+
 	}
 	br.Ret = 200
 	br.Success = true

+ 3 - 1
controllers/smart_report/smart_resource.go

@@ -167,7 +167,9 @@ func (this *SmartReportResourceController) Edit() {
 		return
 	}
 
-	cols := []string{"ImgName", "Style"}
+	cols := []string{"Type", "ImgUrl", "ImgName", "Style"}
+	item.Type = req.Type
+	item.ImgUrl = req.ImgUrl
 	item.ImgName = req.ImgName
 	item.Style = req.Style
 

+ 15 - 3
controllers/sys_role.go

@@ -65,7 +65,7 @@ func (this *SysRoleController) Add() {
 	}
 
 	// 同步角色缓存
-	if utils.BusinessCode == utils.BusinessCodeRelease {
+	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeDebug {
 		var syncData system.SyncRoleData
 		syncData.Source = utils.SOURCE_ETA_FLAG
 		syncData.RoleId = int(roleId)
@@ -117,6 +117,8 @@ func (this *SysRoleController) Edit() {
 		br.Msg = "角色不存在, 请刷新页面"
 		return
 	}
+	oldRoleCodeType := item.RoleTypeCode
+	oldRoleName := item.RoleName
 	exists, e := system.GetSysRoleByName(req.RoleName)
 	if e != nil && !utils.IsErrNoRow(e) {
 		br.Msg = "操作失败"
@@ -135,8 +137,18 @@ func (this *SysRoleController) Edit() {
 		return
 	}
 
+	if oldRoleName != req.RoleName || oldRoleCodeType != roleTypeCode {
+		// 查询所有和角色相关的管理员的信息,并更新成最新的角色
+		err = system.UpdateAdminRoleInfoByRoleId(req.RoleId, req.RoleName, roleTypeCode)
+		if err != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "更新管理员角色信息失败, Err: " + err.Error()
+			return
+		}
+	}
+
 	// 同步角色缓存
-	if utils.BusinessCode == utils.BusinessCodeRelease {
+	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeDebug {
 		var syncData system.SyncRoleData
 		syncData.Source = utils.SOURCE_ETA_FLAG
 		syncData.RoleId = item.RoleId
@@ -192,7 +204,7 @@ func (this *SysRoleController) Delete() {
 	}
 
 	// 同步角色缓存
-	if utils.BusinessCode == utils.BusinessCodeRelease {
+	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeDebug {
 		var syncData system.SyncRoleData
 		syncData.Source = utils.SOURCE_ETA_FLAG
 		syncData.RoleId = role.RoleId

+ 1 - 1
controllers/trade_analysis/warehouse_classify.go

@@ -219,7 +219,7 @@ func (this *WarehouseClassifyController) AddChartClassify() {
 	}
 
 	// 新增图表分类
-	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_TRADE_ANALYSIS_PROCESS, this.Lang, this.SysUser)
+	_, err, errMsg, isSendEmail := data.AddChartClassify(req.ChartClassifyName, req.ParentId, req.Level, utils.CHART_SOURCE_TRADE_ANALYSIS_PROCESS, 0, this.Lang, this.SysUser)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = "添加分类失败,Err:" + err.Error()

+ 1 - 0
main.go

@@ -23,6 +23,7 @@ func main() {
 		web.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
 	}
 	go services.Task()
+	//services.RaiReportInit()
 
 	// 异常处理
 	web.ErrorController(&controllers.ErrorController{})

+ 13 - 2
models/business_conf.go

@@ -64,8 +64,10 @@ const (
 	KnowledgeArticleName                     = "KnowledgeArticleName"             // 原文库
 	BusinessConfEsWechatArticle              = "EsIndexNameWechatArticle"         // ES索引名称-微信文章
 	BusinessConfEsWechatArticleAbstract      = "EsIndexNameWechatArticleAbstract" // ES索引名称-微信文章摘要
-	BusinessConfIsOpenChartExpired     = "IsOpenChartExpired"     // 是否开启图表有效期鉴权/报告禁止复制
-	BusinessConfReportChartExpiredTime = "ReportChartExpiredTime" // 图表有效期鉴权时间,单位:分钟
+	BusinessConfEsRagQuestion                = "EsIndexNameRagQuestion"           // ES索引名称-知识库问题
+	BusinessConfIsOpenChartExpired           = "IsOpenChartExpired"               // 是否开启图表有效期鉴权/报告禁止复制
+	BusinessConfReportChartExpiredTime       = "ReportChartExpiredTime"           // 图表有效期鉴权时间,单位:分钟
+	BusinessConfOssUrlReplace                = "OssUrlReplace"                    // OSS地址替换-兼容内网客户用
 )
 
 const (
@@ -277,6 +279,9 @@ func InitBusinessConf() {
 	if BusinessConfMap[BusinessConfEsWechatArticleAbstract] != "" {
 		utils.EsWechatArticleAbstractName = BusinessConfMap[BusinessConfEsWechatArticleAbstract]
 	}
+	if BusinessConfMap[BusinessConfEsRagQuestion] != "" {
+		utils.EsRagQuestionName = BusinessConfMap[BusinessConfEsRagQuestion]
+	}
 	confStr := BusinessConfMap[LLMInitConfig]
 	if confStr != "" {
 		var config LLMConfig
@@ -301,3 +306,9 @@ func InitBusinessConf() {
 	}
 
 }
+
+type OssUrlReplace struct {
+	IsReplace    bool   `description:"是否替换"`
+	OssUrlOrigin string `description:"被替换的资源地址"`
+	OssUrlNew    string `description:"新的资源地址"`
+}

+ 6 - 0
models/classify.go

@@ -754,3 +754,9 @@ func GetClassifyListByParentId(parentId int) (items []*Classify, err error) {
 	err = global.DbMap[utils.DbNameReport].Raw(sql, parentId).Find(&items).Error
 	return items, err
 }
+
+func GetReportClassifyByClassifyName(classifyNameList []string) (items []*Classify, err error) {
+	sql := `SELECT * FROM classify WHERE classify_name IN (?) and enabled = 1`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, classifyNameList).Find(&items).Error
+	return items, err
+}

+ 10 - 1
models/cloud_disk_resource.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	sql2 "database/sql"
 	"eta/eta_api/global"
 	"eta/eta_api/utils"
 	"fmt"
@@ -169,8 +170,16 @@ func GetSizeTotalByMenuIds(menuIds []int) (sizeTotal int64, err error) {
 	if len(menuIds) == 0 {
 		return
 	}
+	var sizeTotalNull sql2.NullInt64
 	sql := `SELECT SUM(size) FROM cloud_disk_resource WHERE menu_id IN (` + utils.GetOrmInReplace(len(menuIds)) + `)`
-	err = global.DEFAULT_DB.Raw(sql, menuIds).Scan(&sizeTotal).Error
+	err = global.DEFAULT_DB.Raw(sql, menuIds).Scan(&sizeTotalNull).Error
+	if err != nil {
+		return
+	}
+	if sizeTotalNull.Valid {
+		sizeTotal = sizeTotalNull.Int64
+	}
+
 	return
 }
 

+ 14 - 12
models/data_manage/base_from_ccf.go

@@ -39,18 +39,20 @@ type BaseFromCCFIndexList struct {
 	DataList           []*BaseFromCCFData `gorm:"-"`
 	Paging             *paging.PagingItem `description:"分页数据" gorm:"-"`
 }
+
 func (b *BaseFromCCFIndexList) TableName() string {
 	return "base_from_ccf_index"
 }
 
 func (b *BaseFromCCFIndexList) AfterFind(tx *gorm.DB) (err error) {
-		b.ModifyTime = utils.GormDateStrToDateTimeStr(b.ModifyTime)
-		b.CreateTime = utils.GormDateStrToDateTimeStr(b.CreateTime)
-		b.EndDate = utils.GormDateStrToDateStr(b.EndDate)
+	b.ModifyTime = utils.GormDateStrToDateTimeStr(b.ModifyTime)
+	b.CreateTime = utils.GormDateStrToDateTimeStr(b.CreateTime)
+	b.EndDate = utils.GormDateStrToDateStr(b.EndDate)
 	return
 }
 
 type CCFSingleDataResp struct {
+	EdbInfoId          int
 	BaseFromCcfIndexId int
 	ClassifyId         int
 	IndexCode          string
@@ -130,9 +132,9 @@ type BaseFromCCFData struct {
 }
 
 func (baseFromCCFData *BaseFromCCFData) AfterFind(tx *gorm.DB) (err error) {
-			baseFromCCFData.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromCCFData.ModifyTime)
-			baseFromCCFData.CreateTime = utils.GormDateStrToDateTimeStr(baseFromCCFData.CreateTime)
-			baseFromCCFData.DataTime = utils.GormDateStrToDateStr(baseFromCCFData.DataTime)
+	baseFromCCFData.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromCCFData.ModifyTime)
+	baseFromCCFData.CreateTime = utils.GormDateStrToDateTimeStr(baseFromCCFData.CreateTime)
+	baseFromCCFData.DataTime = utils.GormDateStrToDateStr(baseFromCCFData.DataTime)
 	return
 }
 
@@ -226,9 +228,9 @@ func GetCCFIndexDataByDataTime(indexCodes []string, startDate, endDate string) (
 func GetCCFIndexDataTimePageByCodes(indexCodes []string, startSize, pageSize int) (dataTimes []string, err error) {
 	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 = global.DbMap[utils.DbNameIndex].Raw(sql, indexCodes, startSize, pageSize).Find(&dataTimes).Error
-		for i := range dataTimes {
-			dataTimes[i] = utils.GormDateStrToDateStr(dataTimes[i])
-		}
+	for i := range dataTimes {
+		dataTimes[i] = utils.GormDateStrToDateStr(dataTimes[i])
+	}
 	return
 }
 
@@ -255,9 +257,9 @@ func GetCCFDataDataTimeByIndexId(indexIdList []int) (items []string, err error)
 	if err != nil {
 		return
 	}
-		for i := range items {
-			items[i] = utils.GormDateStrToDateStr(items[i])
-		}
+	for i := range items {
+		items[i] = utils.GormDateStrToDateStr(items[i])
+	}
 	return
 }
 

+ 292 - 0
models/data_manage/base_from_purang.go

@@ -0,0 +1,292 @@
+package data_manage
+
+import (
+	"eta/eta_api/utils"
+	"time"
+
+	"gorm.io/gorm"
+
+	"database/sql"
+	"eta/eta_api/global"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type BaseFromPurangIndex struct {
+	BaseFromPurangIndexId int `orm:"column(base_from_purang_index_id);pk"`
+	ClassifyId             int
+	IndexCode              string
+	IndexName              string
+	Frequency              string
+	Unit                   string
+	Sort                   int
+	StartDate              time.Time `description:"开始日期"`
+	EndDate                time.Time `description:"结束日期"`
+	EndValue               float64
+	CreateTime             time.Time
+	ModifyTime             time.Time
+}
+
+type BaseFromPurangIndexList struct {
+	BaseFromPurangIndexId int `orm:"column(base_from_purang_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               []*BaseFromPurangData `gorm:"-"`
+	Paging                 *paging.PagingItem     `description:"分页数据" gorm:"-"`
+}
+
+func (baseFromPurangIndexList *BaseFromPurangIndexList) AfterFind(tx *gorm.DB) (err error) {
+	baseFromPurangIndexList.CreateTime = utils.GormDateStrToDateTimeStr(baseFromPurangIndexList.CreateTime)
+	baseFromPurangIndexList.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromPurangIndexList.ModifyTime)
+	baseFromPurangIndexList.StartDate = utils.GormDateStrToDateStr(baseFromPurangIndexList.StartDate)
+	baseFromPurangIndexList.EndDate = utils.GormDateStrToDateStr(baseFromPurangIndexList.EndDate)
+	return
+}
+
+type BaseFromPurangIndexSearchList struct {
+	List   []*BaseFromPurangIndexList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type PurangSingleDataResp struct {
+	BaseFromPurangIndexId 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                   []*PurangSingleData `gorm:"-"`
+	Paging                 *paging.PagingItem   `description:"分页数据" gorm:"-"`
+}
+
+type PurangSingleData struct {
+	Value    string `orm:"column(value)" description:"日期"`
+	DataTime string `orm:"column(data_time)" description:"值"`
+}
+
+func GetPurangIndexByClassifyId(classifyId int) (items []*BaseFromPurangIndex, err error) {
+	sql := ` SELECT base_from_purang_index_id, classify_id, index_code, index_name FROM base_from_purang_index WHERE classify_id=? ORDER BY sort ASC, base_from_purang_index_id ASC `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, classifyId).Find(&items).Error
+	return
+}
+
+func GetPurangIndex(condition string, pars []interface{}) (items []*BaseFromPurangIndexList, err error) {
+	sql := ` SELECT * FROM base_from_purang_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_purang_index_id asc`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func GetPurangIndexPage(condition string, pars []interface{}, startSize, pageSize int) (items []*BaseFromPurangIndexList, err error) {
+	sql := ` SELECT * FROM base_from_purang_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY sort ASC, base_from_purang_index_id asc LIMIT ?,?`
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func GetPurangIndexPageCount(condition string, pars []interface{}) (count int, err error) {
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_purang_index WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func GetPurangIndexDataCount(indexCode string) (count int, err error) {
+	sql := ` SELECT COUNT(1) AS count  FROM base_from_purang_data WHERE index_code=? `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexCode).Scan(&count).Error
+	return
+}
+
+func GetPurangIndexDataByDataTime(indexCodes []string, startDate, endDate string) (items []*BaseFromPurangData, err error) {
+	sql := ` SELECT *  FROM base_from_purang_data WHERE  index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) and data_time >=? and data_time <? ORDER BY data_time DESC `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexCodes, startDate, endDate).Find(&items).Error
+	return
+}
+
+func GetPurangIndexDataTimePageByCodes(indexCodes []string, startSize, pageSize int) (dataTimes []string, err error) {
+	sql := ` SELECT data_time FROM base_from_purang_data WHERE index_code in ? GROUP BY data_time ORDER BY data_time DESC LIMIT ?,? `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexCodes, startSize, pageSize).Find(&dataTimes).Error
+	return
+}
+
+func GetPurangIndexDataTimePageCount(indexCodes []string) (count int, err error) {
+	sql := ` SELECT COUNT(DISTINCT data_time) AS count  FROM base_from_purang_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCodes)) + `) `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexCodes).Scan(&count).Error
+	return
+}
+
+func GetPurangIndexDataByCodes(indexCode []string) (items []*BaseFromPurangData, err error) {
+	if len(indexCode) == 0 {
+		return
+	}
+	sql := ` SELECT *  FROM base_from_purang_data WHERE index_code in (` + utils.GetOrmInReplace(len(indexCode)) + `) ORDER BY data_time DESC  `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexCode).Find(&items).Error
+	return
+}
+
+// GetPurangByConditionAndFrequency 根据条件获取普瑞指标列表
+func GetPurangByConditionAndFrequency(condition, frequency string, pars []interface{}) (items []*BaseFromPurangIndex, err error) {
+	sql := ` SELECT * FROM base_from_purang_index WHERE 1=1 `
+
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` AND frequency=?`
+	sql += ` ORDER BY sort ASC, base_from_purang_index_id ASC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars, frequency).Find(&items).Error
+	return
+}
+
+func GetPurangFrequencyByCondition(condition string, pars []interface{}) (items []string, err error) {
+	sql := `SELECT DISTINCT frequency FROM base_from_purang_index WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY FIELD(frequency,'日度','周度','旬度','月度','季度','半年度','年度') `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// GetPurangDataDataTimeByIndexId 根据指标id获取指标数据的日期列表
+func GetPurangDataDataTimeByIndexId(indexIdList []int) (items []string, err error) {
+	if len(indexIdList) == 0 {
+		return
+	}
+	sql := ` SELECT DISTINCT data_time FROM base_from_purang_data WHERE base_from_purang_index_id IN (` + utils.GetOrmInReplace(len(indexIdList)) + `) ORDER BY data_time DESC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexIdList).Find(&items).Error
+	for i, item := range items {
+		items[i] = utils.GormDateStrToDateStr(item)
+	}
+	return
+}
+
+type BaseFromPurangData struct {
+	BaseFromPurangDataId  int `orm:"column(base_from_purang_data_id);pk"`
+	BaseFromPurangIndexId int
+	IndexCode              string
+	DataTime               string
+	Value                  string
+	CreateTime             string
+	ModifyTime             string
+	DataTimestamp          int64
+}
+
+func (baseFromPurangData *BaseFromPurangData) AfterFind(tx *gorm.DB) (err error) {
+	baseFromPurangData.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromPurangData.ModifyTime)
+	baseFromPurangData.CreateTime = utils.GormDateStrToDateTimeStr(baseFromPurangData.CreateTime)
+	baseFromPurangData.DataTime = utils.GormDateStrToDateStr(baseFromPurangData.DataTime)
+	return
+}
+
+type BaseFromPurangIndexSearchItem struct {
+	BaseFromPurangIndexId int `orm:"column(base_from_purang_index_id);pk"`
+	ClassifyId             int
+	ParentClassifyId       int
+	IndexCode              string
+	IndexName              string
+}
+
+// BatchCheckPurangEdbReq 指标数据结构体
+type BatchCheckPurangEdbReq struct {
+	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时, 该数组为不选的指标"`
+}
+
+// GetPurangItemList 模糊查询Purang数据库指标列表
+func GetPurangItemList(condition string) (items []*BaseFromPurangIndexSearchItem, err error) {
+	sql := "SELECT * FROM base_from_purang_index  WHERE 1=1"
+	if condition != "" {
+		sql += condition
+	}
+	err = global.DbMap[utils.DbNameIndex].Raw(sql).Find(&items).Error
+	return
+}
+
+func GetPurangIndexDataByCode(indexCode string, pageIndex, pageSize int) (list []*BaseFromPurangData, err error) {
+	sql := `SELECT * FROM base_from_purang_data WHERE index_code=? order by data_time desc limit ?,?`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexCode, pageIndex, pageSize).Find(&list).Error
+	return
+}
+
+func GetPurangIndexDataTotalByCode(indexCode string) (total int64, err error) {
+	sqlStr := `SELECT count(*) FROM base_from_purang_data WHERE index_code=?`
+	var totalNull sql.NullInt64
+	err = global.DbMap[utils.DbNameIndex].Raw(sqlStr, indexCode).Scan(&totalNull).Error
+	if !totalNull.Valid {
+		total = 0
+	} else {
+		total = totalNull.Int64
+	}
+	return
+}
+
+func GetBaseFromPurangIndexByIndexCode(indexCode string) (list *BaseFromPurangIndex, err error) {
+	sql := ` SELECT * FROM base_from_purang_index WHERE index_code=? `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexCode).First(&list).Error
+	return
+}
+
+type BaseFromPurangIndexType struct {
+	Type2 string `gorm:"column:type_2"`
+	Type3 string `gorm:"column:type_3"`
+}
+
+// Update 更新Purang指标基础信息
+func (item *BaseFromPurangIndex) Update(cols []string) (err error) {
+	err = global.DbMap[utils.DbNameIndex].Select(cols).Updates(item).Error
+	return
+}
+
+// EditPurangIndexInfoResp 新增指标的返回
+type EditPurangIndexInfoResp struct {
+	BaseFromPurangIndexId int    `description:"指标ID"`
+	IndexCode              string `description:"指标code"`
+}
+
+type PurangIndexSource2EdbReq struct {
+	EdbCode       string
+	EdbName       string
+	Frequency     string
+	Unit          string
+	ClassifyId    int
+	AdminId       int
+	AdminRealName string
+}
+
+func GetPurangFrequencyByClassifyId(classifyId int) (items []*GlFrequency, err error) {
+	sql := ` SELECT frequency FROM base_from_purang_index WHERE classify_id = ? `
+	sql += ` GROUP BY frequency ORDER BY frequency ASC `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, classifyId).Find(&items).Error
+	return
+} 

+ 139 - 0
models/data_manage/base_from_purang_classify.go

@@ -0,0 +1,139 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"time"
+)
+
+// BaseFromPurangClassify Purang原始数据分类表
+type BaseFromPurangClassify struct {
+	ClassifyId      int       `orm:"column(classify_id);pk" gorm:"column:classify_id;primary_key"`
+	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:"创建时间"`
+}
+
+// GetBaseFromPurangClassifyCount 获取分类名称的个数
+func GetBaseFromPurangClassifyCount(classifyName string, parentId int) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM base_from_purang_classify WHERE classify_name=? AND parent_id=? `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, classifyName, parentId).Scan(&count).Error
+	return
+}
+
+// GetBaseFromPurangClassifyById 通过分类id的获取分类信息
+func GetBaseFromPurangClassifyById(classifyId int) (item *BaseFromPurangClassify, err error) {
+	sql := `SELECT * FROM base_from_purang_classify WHERE classify_id=? `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, classifyId).Find(&item).Error
+	return
+}
+
+// GetBaseFromPurangClassifyByIds 通过分类id的获取分类信息
+func GetBaseFromPurangClassifyByIds(classifyIds []int) (items []*BaseFromPurangClassify, err error) {
+	if len(classifyIds) == 0 {
+		return
+	}
+	sql := `SELECT * FROM base_from_purang_classify WHERE classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `) `
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, classifyIds).Find(&items).Error
+	return
+}
+
+// EditBaseFromPurangClassify 修改Purang原始数据分类
+func EditBaseFromPurangClassify(classifyId int, classifyName string) (err error) {
+	sql := `UPDATE base_from_purang_classify SET classify_name=?,modify_time=NOW() WHERE classify_id=? `
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, classifyName, classifyId).Error
+	return
+}
+
+// UpdateBaseFromPurangClassifySort 修改Purang原始数据分类的排序
+func UpdateBaseFromPurangClassifySort(classifyId int) (err error) {
+	sql := `UPDATE base_from_purang_classify SET sort=classify_id, modify_time=NOW() WHERE classify_id=? `
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, classifyId).Error
+	return
+}
+
+type BaseFromPurangClassifyItems struct {
+	ClassifyId          int                          `description:"分类ID"`
+	BaseFromPurangIndexId 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            []*BaseFromPurangClassifyItems `gorm:"-"`
+}
+
+type BaseFromPurangClassifyNameItems struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级id"`
+}
+
+type BaseFromPurangClassifyResp struct {
+	List []*BaseFromPurangClassifyItems
+}
+
+type BaseFromPurangClassifyNameResp struct {
+	List []*BaseFromPurangClassifyNameItems
+}
+
+type BaseFromPurangClassifyItemsButton struct {
+	AddButton    bool `description:"是否可添加"`
+	OpButton     bool `description:"是否可编辑"`
+	DeleteButton bool `description:"是否可删除"`
+	MoveButton   bool `description:"是否可移动"`
+}
+
+// GetBaseFromPurangClassifyByParentId 根据上级id获取当下的分类列表数据
+func GetBaseFromPurangClassifyByParentId(parentId int) (items []*BaseFromPurangClassifyItems, err error) {
+	sql := ` SELECT * FROM base_from_purang_classify WHERE parent_id=? order by sort asc,classify_id asc`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId).Find(&items).Error
+	return
+}
+
+// GetAllBaseFromPurangClassify 获取所有的分类列表数据
+func GetAllBaseFromPurangClassify() (items []*BaseFromPurangClassifyItems, err error) {
+	sql := ` SELECT * FROM base_from_purang_classify order by parent_id asc, sort asc,classify_id asc`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql).Find(&items).Error
+	return
+}
+
+type DeleteBaseFromPurangClassifyReq struct {
+	ClassifyId int `description:"分类id"`
+	EdbInfoId  int `description:"指标id"`
+}
+
+type BaseFromPurangClassifyListResp struct {
+	AllNodes      []*BaseFromPurangClassifyItems
+	CanOpClassify bool `description:"是否允许操作分类"`
+}
+
+type BaseFromPurangClassifySimplify struct {
+	ClassifyId   int    `description:"分类id"`
+	ClassifyName string `description:"分类名称"`
+	ParentId     int
+}
+
+// GetFirstBaseFromPurangClassify 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstBaseFromPurangClassify() (item *BaseFromPurangClassify, err error) {
+	sql := ` SELECT * FROM base_from_purang_classify order by sort asc,classify_id asc limit 1`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql).First(&item).Error
+	return
+}
+
+// Update 更新分类基础信息
+func (BaseFromPurangClassify *BaseFromPurangClassify) Update(cols []string) (err error) {
+	err = global.DbMap[utils.DbNameIndex].Select(cols).Updates(BaseFromPurangClassify).Error
+	return
+}
+
+type AddPurangClassifyResp struct {
+	ClassifyId int
+} 

+ 27 - 0
models/data_manage/base_from_sci_hq_classify.go

@@ -3,6 +3,7 @@ package data_manage
 import (
 	"eta/eta_api/global"
 	"eta/eta_api/utils"
+	"fmt"
 	"time"
 )
 
@@ -218,3 +219,29 @@ func MoveDownSciHqIndexClassifyBySort(parentId, prevSort, currentSort int) (err
 	err = o.Exec(sql, parentId, prevSort, currentSort).Error
 	return
 }
+
+// UpdateSciHqClassifySortByParentId 根据父类id更新排序
+func UpdateSciHqClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` update base_from_sci_hq_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.Exec(sql, parentId, nowSort).Error
+	return
+}
+
+// GetFirstSciHqClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func GetFirstSciHqClassifyByParentId(parentId int) (item *BaseFromSciHqClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_sci_hq_classify WHERE parent_id = ? ORDER BY sort ASC,classify_id ASC LIMIT 1`
+	err = o.Raw(sql, parentId).First(&item).Error
+	return
+}
+
+func GetSciHqClassifySortMaxByParentId(parentId int) (sort int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT COALESCE(Max(sort), 0) AS sort FROM base_from_sci_hq_classify WHERE parent_id = ?`
+	err = o.Raw(sql, parentId).Scan(&sort).Error
+	return
+}

+ 10 - 4
models/data_manage/base_from_sci_hq_index.go

@@ -25,6 +25,12 @@ type BaseFromSciHqIndex struct {
 	ModifyTime           time.Time
 }
 
+func (m *BaseFromSciHqIndex) AfterFind(db *gorm.DB) (err error) {
+	m.StartDate = utils.GormDateStrToDateStr(m.StartDate)
+	m.EndDate = utils.GormDateStrToDateStr(m.EndDate)
+	return
+}
+
 type BaseFromSciHqIndexView struct {
 	BaseFromSciHqIndexId int     `orm:"pk" gorm:"primaryKey"`
 	EdbInfoId            int     `description:"指标库id"`
@@ -44,10 +50,10 @@ type BaseFromSciHqIndexView struct {
 }
 
 func (b *BaseFromSciHqIndexView) AfterFind(db *gorm.DB) (err error) {
-			b.LatestDate = utils.GormDateStrToDateStr(b.LatestDate)
-			b.StartDate = utils.GormDateStrToDateStr(b.StartDate)
-			b.EndDate = utils.GormDateStrToDateStr(b.EndDate)
-			b.ModifyTime = utils.GormDateStrToDateTimeStr(b.ModifyTime)
+	b.LatestDate = utils.GormDateStrToDateStr(b.LatestDate)
+	b.StartDate = utils.GormDateStrToDateStr(b.StartDate)
+	b.EndDate = utils.GormDateStrToDateStr(b.EndDate)
+	b.ModifyTime = utils.GormDateStrToDateTimeStr(b.ModifyTime)
 	return
 }
 func (b *BaseFromSciHqIndex) Update(cols []string) (err error) {

+ 7 - 0
models/data_manage/base_from_ths_hf_classify.go

@@ -289,3 +289,10 @@ func UpdateThsHfClassifyChildByParentClassifyId(classifyIds []int, rootId int, l
 	err = o.Exec(sql, pars...).Error
 	return
 }
+
+// GetThsHfClassifyByParentIdAndName 根据父级ID和名称获取分类
+func GetThsHfClassifyByParentIdAndName(parentId int, classifyName string, classifyId int) (item *BaseFromThsHfClassify, err error) {
+	sql := `SELECT * FROM base_from_ths_hf_classify WHERE parent_id = ? AND classify_name = ? AND base_from_ths_hf_classify_id <> ? LIMIT 1`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, classifyName, classifyId).First(&item).Error
+	return
+}

+ 88 - 10
models/data_manage/chart_classify.go

@@ -25,6 +25,9 @@ type ChartClassify struct {
 	IsJoinPermission    int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	ChartClassifyNameEn string    `description:"英文分类名称"`
 	RootId              int       `description:"顶级ID"`
+	IsSelected          int       `description:"是否精选资源,0:否;1:是"`
+	ResourceStatus      int       `description:"在ETA投研资源库中的状态,0:初始状态,1上架,2下架"`
+	LevelPath           string    `description:"所有的父级分类id"`
 }
 
 func AddChartClassify(item *ChartClassify) (lastId int64, err error) {
@@ -37,6 +40,7 @@ type AddChartClassifyReq struct {
 	ChartClassifyName string `description:"分类名称"`
 	ParentId          int    `description:"父级id,第一级传0"`
 	Level             int    `description:"层级,第一级传0,其余传上一级的层级"`
+	IsSelected        int    `description:"是否精选资源,0:否;1:是"`
 }
 
 // GetChartClassifyCount
@@ -77,6 +81,7 @@ type EditChartClassifyReq struct {
 	ChartClassifyName string `description:"分类名称"`
 	ChartClassifyId   int    `description:"分类id"`
 	ParentId          int    `description:"父级分类id"`
+	IsSelected        int    `description:"是否精选资源,0:否;1:是"`
 }
 
 func GetChartClassifyById(classifyId int) (item *ChartClassify, err error) {
@@ -269,6 +274,8 @@ type ChartClassifyItems struct {
 	IsJoinPermission    int                      `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
 	HaveOperaAuth       bool                     `description:"是否有数据权限,默认:false"`
 	Disable             bool                     `description:"勾选是否禁用"`
+	IsSelected          int                      `description:"是否精选资源,0:否;1:是"`
+	ResourceStatus      int                      `description:"在ETA投研资源库中的状态,0:初始状态,1上架,2下架"`
 }
 
 // ChartClassifyItemsButton 操作按钮
@@ -347,6 +354,16 @@ func GetChartClassifyByCondition(condition string, pars []interface{}) (item *Ch
 	return
 }
 
+func GetChartClassifyListByCondition(condition string, pars []interface{}) (items []*ChartClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM chart_classify WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
 // MoveChartClassifyReq 移动图表分类请求参数
 type MoveChartClassifyReq struct {
 	ClassifyId       int `description:"分类id"`
@@ -560,6 +577,20 @@ func GetChartClassifyAllBySource(source int) (items []*ChartClassifyItems, err e
 	return
 }
 
+// GetChartClassifyAllBySourceIsSelected 根据来源获取所有精选分类
+func GetChartClassifyAllBySourceIsSelected(source int, isSelected int) (items []*ChartClassifyItems, err error) {
+	sql := ` SELECT * FROM chart_classify WHERE source = ? AND is_selected = ? ORDER BY parent_id ASC, sort ASC, chart_classify_id ASC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, source, isSelected).Find(&items).Error
+	return
+}
+
+// GetChartClassifyInfoSelectedBySource 获取所有的精选目录
+func GetChartClassifyInfoSelectedBySource(source int) (items []*ChartClassify, err error) {
+	sql := ` SELECT * FROM chart_classify WHERE source = ? AND is_selected = ? ORDER BY parent_id ASC, sort ASC, chart_classify_id ASC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, source, utils.ChartClassifyIsSelected).Find(&items).Error
+	return
+}
+
 // GetChartClassifyIdListByAdminId
 // @Description: 根据用户id和指标类型获取其关联的所有指标分类id列表
 // @author: Roc
@@ -579,7 +610,7 @@ func GetChartClassifyIdListByAdminId(adminId, source int) (chartClassifyIdList [
 }
 
 // GetChartClassifyAndInfoByParentId
-func GetChartClassifyAndInfoByParentId(parentId int) (items []*ChartClassifyItems, err error) {
+func GetChartClassifyAndInfoByParentId(parentId int, isSelected int) (items []*ChartClassifyItems, err error) {
 	sql := ` SELECT
 	0 AS chart_info_id,
 	chart_classify_id,
@@ -600,11 +631,13 @@ func GetChartClassifyAndInfoByParentId(parentId int) (items []*ChartClassifyItem
 	0 as chart_type,
 	'' as calendar,
 	'' as season_start_date,
-	'' as season_end_date
+	'' as season_end_date,
+	is_selected,
+	resource_status
 FROM
 	chart_classify 
 WHERE
-	parent_id = ? and source = 1 UNION ALL
+	parent_id = ? and source = 1 AND is_selected = ?  UNION ALL
 SELECT
 	chart_info_id,
 	chart_classify_id,
@@ -625,7 +658,9 @@ SELECT
 	chart_type,
 	calendar,
 	season_start_date,
-	season_end_date
+	season_end_date,
+	0 as is_selected,
+	resource_status
 FROM
 	chart_info 
 WHERE
@@ -633,12 +668,12 @@ WHERE
 ORDER BY
 	sort ASC,
 	chart_classify_id ASC`
-	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, parentId).Find(&items).Error
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, isSelected, parentId).Find(&items).Error
 	return
 }
 
 // GetChartClassifyAndInfoByParentId
-func GetChartClassifyAndInfoByParentIdForMe(parentId, adminId int) (items []*ChartClassifyItems, err error) {
+func GetChartClassifyAndInfoByParentIdForMe(parentId, adminId, isSelected int) (items []*ChartClassifyItems, err error) {
 	sql := ` SELECT
 	0 AS chart_info_id,
 	chart_classify_id,
@@ -651,11 +686,13 @@ func GetChartClassifyAndInfoByParentIdForMe(parentId, adminId int) (items []*Cha
 	sys_user_real_name,
 	sort,
 	level,
-	unique_code
+	unique_code,
+	is_selected,
+	resource_status
 FROM
 	chart_classify 
 WHERE
-	parent_id = ? and source = 1 UNION ALL
+	parent_id = ? and source = 1 AND is_selected = ? UNION ALL
 SELECT
 	chart_info_id,
 	chart_classify_id,
@@ -668,7 +705,9 @@ SELECT
 	sys_user_real_name,
 	sort,
 	0 AS level,
-	unique_code
+	unique_code,
+	0 as is_selected,
+	resource_status
 FROM
 	chart_info 
 WHERE
@@ -676,7 +715,7 @@ WHERE
 ORDER BY
 	sort ASC,
 	chart_classify_id ASC`
-	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, parentId, adminId).Find(&items).Error
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, isSelected, parentId, adminId).Find(&items).Error
 	return
 }
 
@@ -694,3 +733,42 @@ func GetChartClassifiesById(chartClassifyId int) (items []*ChartClassifyItems, e
 	err = global.DbMap[utils.DbNameIndex].Raw(sql, chartClassifyId, chartClassifyId, chartClassifyId).Find(&items).Error
 	return
 }
+
+func GetChartClassifyByLevelPath(levelPath string, source int) (items []*ChartClassify, err error) {
+	sql := `SELECT * FROM chart_classify where level_path like '` + levelPath + `%' and source = ?`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, source).Find(&items).Error
+	return
+}
+
+func UpdateChartClassifyIsSelected(source int, isSelected int, levelPath string) (err error) {
+	sql := `UPDATE chart_classify SET is_selected = ? WHERE source = ? AND level_path LIKE '` + levelPath + `%'`
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, isSelected, source).Error
+	return
+}
+
+func UpdateChartClassifyResourceStatus(source int, resourceStatus int, levelPath string) (err error) {
+	sql := `UPDATE chart_classify SET resource_status = ? WHERE source = ? AND level_path LIKE '` + levelPath + `%'`
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, resourceStatus, source).Error
+	return
+}
+
+// 查询存在已经上架的图表的分类
+func GetChartClassifyHasUpChartBySource(source int, levelPath string) (count int, err error) {
+	sql := `SELECT count(1) FROM chart_info WHERE source = ? AND resource_status = ? and chart_classify_id in (SELECT chart_classify_id FROM chart_classify WHERE level_path like '` + levelPath + `%')`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, source, utils.ChartClassifyResourceStatusUp).Scan(&count).Error
+	return
+}
+
+type SetChartClassifyResourceStatusReq struct {
+	ChartClassifyId int `description:"分类id"`
+	ResourceStatus  int `description:"资源状态"`
+}
+
+type ChartClassifyResourceUpdate struct {
+	ChartClassifyId int `description:"分类id"`
+}
+
+type EditChartClassifyResp struct {
+	TipCode          string `description:"提示码"`
+	TipMsg           string `description:"提示信息"`
+}

+ 81 - 0
models/data_manage/chart_description.go

@@ -0,0 +1,81 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"time"
+)
+
+type ChartDescription struct {
+	Id               int       `gorm:"column:id;primaryKey"`
+	Description      string    `gorm:"column:description"`
+	ChartInfoId      int       `gorm:"column:chart_info_id"` // 图表id
+	SysUserId        int       `gorm:"column:sys_user_id"`     // 创建人id
+	SysUserRealName  string    `gorm:"column:sys_user_real_name"` // 创建人姓名
+	ModifyTime       time.Time `gorm:"column:modify_time"`     // 变更时间
+	CreateTime       time.Time `gorm:"column:create_time"`     // 关系建立时间
+}
+
+// AddChartDescription 添加图表简介
+func AddChartDescription(item *ChartDescription) (err error) {
+	err = global.DbMap[utils.DbNameIndex].Create(item).Error
+	return
+}
+
+// GetChartDescriptionByChartInfoId 根据图表ID获取图表简介
+func GetChartDescriptionByChartInfoId(chartInfoId int) (item []*ChartDescription, err error) {
+	sql := `SELECT * FROM chart_description WHERE chart_info_id = ? order by create_time desc, id desc`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, chartInfoId).Find(&item).Error
+	return
+}
+
+// GetChartDescriptionById 根据ID获取图表简介
+func GetChartDescriptionById(id int) (item *ChartDescription, err error) {
+	sql := `SELECT * FROM chart_description WHERE id = ?`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, id).First(&item).Error
+	return
+}
+
+// DeleteChartDescription 删除图表简介
+func DeleteChartDescription(id int) (err error) {
+	sql := `DELETE FROM chart_description WHERE id = ?`
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, id).Error
+	return
+}
+
+// DeleteChartDescriptionByChartInfoId 根据图表ID删除图表简介
+func DeleteChartDescriptionByChartInfoId(chartInfoId int) (err error) {
+	sql := `DELETE FROM chart_description WHERE chart_info_id = ?`
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, chartInfoId).Error
+	return
+} 
+
+type ChartDescriptionList struct {
+	Id               int       `gorm:"column:id;primaryKey"`
+	Description      string    `gorm:"column:description"`
+	ChartInfoId      int       `gorm:"column:chart_info_id"` // 图表id
+	SysUserId        int       `gorm:"column:sys_user_id"`     // 创建人id
+	SysUserRealName  string    `gorm:"column:sys_user_real_name"` // 创建人姓名
+	CreateTime       string	   `gorm:"column:create_time"`     // 创建时间
+}
+
+type ChartDescriptionListResponse struct {
+	List []*ChartDescriptionList
+}
+
+type ChartDescriptionAddReq struct {
+	ChartInfoId int `description:"图表id"`
+	Description string `description:"简介"`
+}
+
+
+type ChartDescriptionReq struct {
+	Id               int       
+	Description      string      // 图表id
+	ChartInfoId      int       
+	SysUserId        int         // 创建人id
+	SysUserRealName  string    	 // 创建人姓名
+	AdminName        string   	 // 创建人姓名
+	ModifyTime       time.Time 	 // 变更时间
+	CreateTime       time.Time 	 // 关系建立时间  
+}

+ 6 - 0
models/data_manage/chart_edb_mapping.go

@@ -70,6 +70,12 @@ func GetChartMappingList(chartInfoId int) (list []*ChartEdbMapping, err error) {
 	err = o.Raw(sql, chartInfoId).Find(&list).Error
 	return
 }
+func GetChartMappingListOrderByEdbInfoId(chartInfoId int) (list []*ChartEdbMapping, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT * FROM chart_edb_mapping WHERE chart_info_id = ? ORDER BY edb_info_id ASC`
+	err = o.Raw(sql, chartInfoId).Find(&list).Error
+	return
+}
 
 func GetChartEdbMappingListByChartInfoIds(chartInfoIds string) (list []*ChartEdbInfoMapping, err error) {
 	o := global.DbMap[utils.DbNameIndex]

+ 7 - 4
models/data_manage/chart_info.go

@@ -62,6 +62,7 @@ type ChartInfo struct {
 	ForumChartInfoId  int    `description:"社区的图表ID"`
 	ChartAlias        string `description:"图表别名"`
 	DateTypeNum       int    `description:"date_type=25(N月前)时的N值,其他N值可复用此字段"`
+	ResourceStatus    int    `description:"资源状态,0默认, 1上架,2下架"`
 }
 
 // AreaExtraConf 面积图配置
@@ -77,6 +78,7 @@ type ChartInfoMore struct {
 	IsEnChart     bool   `description:"是否展示英文标识"`
 	HaveOperaAuth bool   `description:"是否有数据权限,默认:false"`
 	SearchText    string `description:"搜索结果(含高亮)"`
+	IsSelected    int    `description:"是否是精选目录下的图表"`
 }
 
 func AddChartInfo(item *ChartInfo) (lastId int64, err error) {
@@ -378,14 +380,15 @@ func ModifyChartInfo(item *EditChartInfoReq) (err error) {
 	err = o.Exec(sql, item.ChartName, item.ChartClassifyId, item.ChartInfoId).Error
 	return
 }
-func SetForumChartInfoId(chartInfoId, forumChartInfoId int) (err error) {
+func SetForumChartInfoId(chartInfoId, forumChartInfoId int, resourceStatus int) (err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := ` UPDATE  chart_info
 			SET
               forum_chart_info_id=?,
-			  modify_time = NOW()
+			  modify_time = NOW(),
+			  resource_status = ?
 			WHERE chart_info_id = ?`
-	err = o.Exec(sql, forumChartInfoId, chartInfoId).Error
+	err = o.Exec(sql, forumChartInfoId, resourceStatus, chartInfoId).Error
 	return
 }
 
@@ -815,6 +818,7 @@ type ChartInfoDetailResp struct {
 	CorrelationChartInfo *CorrelationInfo `description:"相关性图表信息"`
 	DataResp             interface{}      `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
 	ClassifyLevels       []string         `description:"图表分类的UniqueCode-从最顶级到当前分类(给前端回显定位用的=_=!)"`
+	IsSelected        int             `description:"是否为精选目录下的图表,0:否;1:是;默认:0"`
 }
 
 type FutureGoodChartInfoDetailResp struct {
@@ -1780,7 +1784,6 @@ func ChartInfoSearchByEmptyKeyWord(showSysId int, sourceList []int, noPermission
 		baseSql += ` AND chart_info_id not in (` + utils.GetOrmInReplace(lenNoPermissionChartIdList) + `) `
 		basePars = append(basePars, noPermissionChartIdList)
 	}
-
 	// 查找数量
 	totalSql := " SELECT count(1) as total " + baseSql
 	err = o.Raw(totalSql, basePars...).Scan(&total).Error

+ 8 - 10
models/data_manage/edb_classify.go

@@ -166,7 +166,7 @@ func GetEdbInfoCountByClassifyId(classifyId int) (count int, err error) {
 				WHERE classify_id = ?
 			)`
 		pars = append(pars, classifyId, classifyId)
-	}else{
+	} else {
 		sql = ` SELECT COUNT(1) AS count FROM edb_info AS a
 		WHERE a.classify_id IN(
 		SELECT t.classify_id FROM 
@@ -181,7 +181,7 @@ func GetEdbInfoCountByClassifyId(classifyId int) (count int, err error) {
 		) `
 		pars = append(pars, classifyId)
 	}
-	
+
 	//err = o.Raw(sql, classifyId).QueryRow(&count)
 	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).Scan(&count).Error
 	return
@@ -417,7 +417,7 @@ func GetClassifyCountByClassifyId(classifyId int) (count int, err error) {
 		) AS t
 		WHERE t.classify_id <> ?`
 		pars = append(pars, classifyId, classifyId, classifyId)
-	}else{
+	} else {
 		sql = ` SELECT COUNT(1) AS count FROM (
 			SELECT rd.*
 			FROM (SELECT * FROM edb_classify WHERE parent_id IS NOT NULL) rd,
@@ -429,7 +429,7 @@ func GetClassifyCountByClassifyId(classifyId int) (count int, err error) {
 			WHERE t.classify_id<>? `
 		pars = append(pars, classifyId, classifyId)
 	}
-		
+
 	//err = o.Raw(sql, classifyId, classifyId).QueryRow(&count)
 	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).Scan(&count).Error
 	return
@@ -655,7 +655,7 @@ func FixPredictEdbClassify() {
 
 func GetEdbClassify(classifyId int) (classifyIds string, err error) {
 	//o := orm.NewOrmUsingDB("data")
-	sql :=fmt.Sprintf( `SELECT %s AS classify_ids FROM (
+	sql := fmt.Sprintf(`SELECT %s AS classify_ids FROM (
 			SELECT a.classify_id FROM edb_classify AS a 
 			WHERE a.classify_id=?
 			UNION ALL
@@ -731,11 +731,9 @@ where classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
 }
 
 // GetEdbClassifyByParentIdAndName 根据父级ID和名称获取分类
-func GetEdbClassifyByParentIdAndName(parentId int, classifyName string, classifyId int) (item *EdbClassify, err error) {
-	//o := orm.NewOrmUsingDB("data")
-	sql := `SELECT * FROM edb_classify WHERE parent_id = ? AND classify_name = ? AND classify_id <> ? LIMIT 1`
-	//err = o.Raw(sql, parentId, classifyName, classifyId).QueryRow(&item)
-	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, classifyName, classifyId).First(&item).Error
+func GetEdbClassifyByParentIdAndName(parentId int, classifyName string, classifyId, classifyType int) (item *EdbClassify, err error) {
+	sql := `SELECT * FROM edb_classify WHERE parent_id = ? AND classify_name = ? AND classify_id <> ? AND classify_type = ? LIMIT 1`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, classifyName, classifyId, classifyType).First(&item).Error
 	return
 }
 

+ 1 - 1
models/data_manage/edb_data_base.go

@@ -353,7 +353,7 @@ func GetBaseIndexInfoByEdbCode(edbCode string, source int) (item *BaseIndexInfo,
 
 	sql = fmt.Sprintf(sql, tableName)
 	//err = o.Raw(sql, pars).QueryRow(&item)
-	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).Find(&item).Error
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars...).First(&item).Error
 	return
 }
 

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

@@ -496,6 +496,7 @@ func AddExcelInfoAndSheet(excelInfo *ExcelInfo, sheetParamsList []AddExcelSheetP
 			CalcChain:    sheetInfo.CalcChain,
 			ModifyTime:   time.Now(),
 			CreateTime:   time.Now(),
+			Frozen:       sheetInfo.Frozen,
 		}
 
 		tmpErr := to.Create(excelSheetInfo).Error
@@ -571,6 +572,7 @@ func SaveExcelInfoAndSheet(excelInfo *ExcelInfo, updateExcelInfoParam []string,
 			CalcChain:    sheetInfo.CalcChain,
 			ModifyTime:   time.Now(),
 			CreateTime:   time.Now(),
+			Frozen:       sheetInfo.Frozen,
 		}
 
 		tmpErr := to.Create(excelSheetInfo).Error

+ 15 - 0
models/data_manage/excel/excel_info_rule_mapping.go

@@ -76,3 +76,18 @@ func DeleteExcelRuleMappingById(id int) (err error) {
 	err = global.DbMap[utils.DbNameIndex].Exec(sql, id).Error
 	return
 }
+
+func (e *ExcelInfoRuleMapping) CreateMulti(items []*ExcelInfoRuleMapping) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	err = global.DbMap[utils.DbNameIndex].CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+// GetExcelRuleMappingsByExcelInfoId 根据excelInfoId获取规则映射信息
+func GetExcelRuleMappingsByExcelInfoId(id int) (items []*ExcelInfoRuleMapping, err error) {
+	sql := `SELECT * FROM excel_info_rule_mapping WHERE excel_info_id = ? ORDER BY create_time ASC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, id).Find(&items).Error
+	return
+}

+ 3 - 0
models/data_manage/excel/excel_sheet.go

@@ -18,6 +18,7 @@ type ExcelSheet struct {
 	CalcChain    string    `description:"计算公式"`
 	ModifyTime   time.Time `description:"最近修改日期"`
 	CreateTime   time.Time `description:"创建日期"`
+	Frozen       string    `description:"冻结行"`
 }
 
 // Update 更新 excel表格的sheet基础信息
@@ -56,6 +57,7 @@ type SheetItem struct {
 	ModifyTime   time.Time       `description:"最近修改日期" json:"-"`
 	CreateTime   time.Time       `description:"创建日期"`
 	Data         *ExcelSheetData `gorm:"-" description:"excel的数据"`
+	Frozen       string          `description:"冻结行"`
 }
 
 // GetAllSheetItemList 根据excel_id获取所有的sheet详情
@@ -86,4 +88,5 @@ type AddExcelSheetParams struct {
 	Config       string            `description:"配置信息"`
 	CalcChain    string            `description:"计算公式"`
 	DataList     []*ExcelSheetData `description:"excel的数据"`
+	Frozen       string            `description:"冻结行"`
 }

+ 48 - 0
models/data_manage/forum_chart_edb_mapping.go

@@ -0,0 +1,48 @@
+package data_manage
+
+import (
+	"time"
+
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+)
+
+type ForumChartEdbMapping struct {
+	ID int `gorm:"column:id;primaryKey" description:"主键"`
+	ChartInfoId int 
+	EdbInfoIds string 
+	CreateTime time.Time 
+	ModifyTime time.Time 
+}
+
+// 根据chartInfoId获取edbInfoIds
+func GetForumEdbInfoIdsByChartInfoId(chartInfoId int) (edbInfoIds string, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT edb_info_ids FROM forum_chart_edb_mapping WHERE chart_info_id = ?`
+	err = o.Raw(sql, chartInfoId).Scan(&edbInfoIds).Error
+	return
+}
+
+// 新增
+func AddForumChartEdbMapping(chartInfoId int, edbInfoIds string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `INSERT INTO forum_chart_edb_mapping (chart_info_id, edb_info_ids, create_time, modify_time) VALUES (?, ?, ?, ?)`
+	err = o.Exec(sql, chartInfoId, edbInfoIds, time.Now(), time.Now()).Error
+	return
+}
+
+// 更新
+func UpdateForumChartEdbMapping(chartInfoId int, edbInfoIds string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `UPDATE forum_chart_edb_mapping SET edb_info_ids = ?, modify_time = ? WHERE chart_info_id = ?`
+	err = o.Exec(sql, edbInfoIds, time.Now(), chartInfoId).Error
+	return
+}
+
+// 删除
+func DeleteForumChartEdbMapping(chartInfoId int) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `DELETE FROM forum_chart_edb_mapping WHERE chart_info_id = ?`
+	err = o.Exec(sql, chartInfoId).Error
+	return
+}

+ 51 - 13
models/data_manage/mysteel_chemical_index.go

@@ -45,7 +45,7 @@ type BaseFromMysteelChemicalIndexItem struct {
 	IndexName                      string `description:"指标名称"`
 	Unit                           string `description:"单位"`
 	Frequency                      string `description:"频度"`
-	Source                         string    `description:"数据来源"`
+	Source                         string `description:"数据来源"`
 	StartDate                      string `description:"开始日期"`
 	EndDate                        string `description:"结束日期"`
 	CreateTime                     string `description:"创建时间"`
@@ -56,13 +56,15 @@ type BaseFromMysteelChemicalIndexItem struct {
 	IsStop                         int    `description:"是否停更:1:停更,0:未停更"`
 	EdbExist                       int    `description:"指标库是否已添加:0-否;1-是"`
 }
-func (m *BaseFromMysteelChemicalIndexItem) AfterFind(tx * gorm.DB) (err error) {
-	m.StartDate=utils.GormDateStrToDateStr(m.StartDate)
-	m.EndDate =utils.GormDateStrToDateStr(m.EndDate)
-	m.CreateTime=utils.GormDateStrToDateTimeStr(m.CreateTime)
-	m.ModifyTime=utils.GormDateStrToDateTimeStr(m.ModifyTime)
+
+func (m *BaseFromMysteelChemicalIndexItem) AfterFind(tx *gorm.DB) (err error) {
+	m.StartDate = utils.GormDateStrToDateStr(m.StartDate)
+	m.EndDate = utils.GormDateStrToDateStr(m.EndDate)
+	m.CreateTime = utils.GormDateStrToDateTimeStr(m.CreateTime)
+	m.ModifyTime = utils.GormDateStrToDateTimeStr(m.ModifyTime)
 	return
 }
+
 var BaseFromMysteelChemicalIndexCols = struct {
 	BaseFromMysteelChemicalIndexId string
 	IndexCode                      string
@@ -98,7 +100,7 @@ func (m *BaseFromMysteelChemicalIndex) GeItemsByCondition(condition string, pars
 		pars = make([]interface{}, 0)
 	}
 	//pars = append(pars, utils.DATA_SOURCE_MYSTEEL_CHEMICAL)
-	pars=utils.ForwardPars(pars,utils.DATA_SOURCE_MYSTEEL_CHEMICAL)
+	pars = utils.ForwardPars(pars, utils.DATA_SOURCE_MYSTEEL_CHEMICAL)
 	sql := `select a.*,b.edb_info_id,b.unique_code as edb_unique_code,b.classify_id as edb_classify_id FROM base_from_mysteel_chemical_index AS a LEFT JOIN edb_info b on a.index_code=b.edb_code AND b.source=? WHERE 1=1 `
 	if condition != "" {
 		sql += condition
@@ -201,11 +203,14 @@ type MysteelChemicalList struct {
 	IsStop                            int                    `description:"是否停更:1:停更,0:未停更"`
 	IsSupplierStop                    int                    `description:"是否供应商停更:1:停更,0:未停更"`
 	Paging                            *paging.PagingItem     `description:"分页数据" gorm:"-"`
+	EndDate                           string                 `description:"结束日期"`
+	EndValue                          float64                `description:"最新值"`
 	DataList                          []*MysteelChemicalData `gorm:"-"`
 }
 
 func (m *MysteelChemicalList) AfterFind(tx *gorm.DB) (err error) {
-			m.UpdateTime = utils.GormDateStrToDateTimeStr(m.UpdateTime)
+	m.UpdateTime = utils.GormDateStrToDateTimeStr(m.UpdateTime)
+	m.EndDate = utils.GormDateStrToDateStr(m.EndDate)
 	return
 }
 
@@ -216,7 +221,7 @@ type MysteelChemicalData struct {
 }
 
 func (m *MysteelChemicalData) AfterFind(tx *gorm.DB) (err error) {
-			m.DataTime = utils.GormDateStrToDateStr(m.DataTime)
+	m.DataTime = utils.GormDateStrToDateStr(m.DataTime)
 	return
 }
 
@@ -346,9 +351,9 @@ func GetBaseFromMysteelChemicalDataTimeByIndexId(indexIdList []int) (items []str
 	if err != nil {
 		return
 	}
-		for i := range items {
-			items[i] = utils.GormDateStrToDateStr(items[i])
-		}
+	for i := range items {
+		items[i] = utils.GormDateStrToDateStr(items[i])
+	}
 	return
 }
 
@@ -548,7 +553,7 @@ type BaseRefreshEdbInfo struct {
 }
 
 func (obj *BaseRefreshEdbInfo) AfterFind(tx *gorm.DB) (err error) {
-			obj.EndDate = utils.GormDateStrToDateStr(obj.EndDate)
+	obj.EndDate = utils.GormDateStrToDateStr(obj.EndDate)
 	return
 }
 
@@ -755,3 +760,36 @@ func GetNotIsSupplierStopIndexByCodeList(codeList []string, isStop int) (items [
 
 	return
 }
+
+type MysteelChemicalPageListResp struct {
+	List   []*MysteelChemicalList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+// GetNoEdbMysteelChemicalIndexCount 获取未加入指标库的钢联原始指标-分页
+func GetNoEdbMysteelChemicalIndexCount(condition string, pars []interface{}) (count int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM base_from_mysteel_chemical_index WHERE 1=1 AND index_code NOT IN (
+		  SELECT DISTINCT edb_code FROM edb_info WHERE source = %d
+		) `, utils.DATA_SOURCE_MYSTEEL_CHEMICAL)
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+// GetNoEdbMysteelChemicalIndexPageList 获取未加入指标库的钢联原始指标-分页
+func GetNoEdbMysteelChemicalIndexPageList(condition string, pars []interface{}, startSize, pageSize int) (items []*MysteelChemicalList, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT * FROM base_from_mysteel_chemical_index WHERE 1=1 AND index_code NOT IN (
+		  SELECT DISTINCT edb_code FROM edb_info WHERE source = %d
+		) `, utils.DATA_SOURCE_MYSTEEL_CHEMICAL)
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY base_from_mysteel_chemical_index_id ASC LIMIT ?,?`
+	pars = append(pars, startSize, pageSize)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}

+ 4 - 4
models/data_manage/request/sci_hq_data.go

@@ -48,10 +48,10 @@ type SciHqDataBatchListReq struct {
 
 // MoveBaseFromSciHqClassifyReq 移动分类请求参数
 type MoveBaseFromSciHqClassifyReq struct {
-	ClassifyId     int `description:"分类id"`
-	ParentId       int `description:"父级分类id"`
-	PrevClassifyId int `description:"上一个兄弟节点分类id"`
-	NextClassifyId int `description:"下一个兄弟节点分类id"`
+	ClassifyId       int `description:"分类id"`
+	ParentClassifyId int `description:"父级分类id"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
+	NextClassifyId   int `description:"下一个兄弟节点分类id"`
 }
 
 // MoveBaseFromSciHqReq 移动指标请求参数

+ 3 - 2
models/rag/article_kb_mapping.go

@@ -10,6 +10,7 @@ import (
 type ArticleKbMapping struct {
 	Id              int `gorm:"id;primaryKey"`
 	WechatArticleId int
+	Source          int
 	KbId            string
 	CreatedTime     time.Time
 	UpdateTime      time.Time
@@ -19,8 +20,8 @@ func (a *ArticleKbMapping) TableName() string {
 	return "article_kb_mapping"
 }
 
-func GetArticleKbMapping(articleId int) (articleKbMapping *ArticleKbMapping, err error) {
-	err = global.DbMap[utils.DbNameAI].Where("wechat_article_id = ?", articleId).First(&articleKbMapping).Error
+func GetArticleKbMapping(articleId, source int) (articleKbMapping *ArticleKbMapping, err error) {
+	err = global.DbMap[utils.DbNameAI].Where("wechat_article_id = ? AND source = ? ", articleId, source).First(&articleKbMapping).Error
 	return
 }
 

+ 8 - 2
models/rag/promote_train_record.go

@@ -9,6 +9,8 @@ import (
 type PromoteTrainRecord struct {
 	Id              int       `gorm:"id;primaryKey"`
 	Title           string    `gorm:"title"`
+	Llm             string    `gorm:"llm"`
+	Source          int       `gorm:"source" description:"来源,0:公众号文章,1:eta报告"`
 	WechatArticleId int       `gorm:"wechat_article_id"`
 	TemplatePromote string    `gorm:"template_promote"`
 	PromoteSendTime time.Time `gorm:"promote_send_time"`
@@ -23,6 +25,8 @@ func (p *PromoteTrainRecord) ToView() *PromoteTrainRecordView {
 	return &PromoteTrainRecordView{
 		Id:              p.Id,
 		Title:           p.Title,
+		Llm:             p.Llm,
+		Source:          p.Source,
 		WechatArticleId: p.WechatArticleId,
 		TemplatePromote: p.TemplatePromote,
 		PromoteSendTime: p.PromoteSendTime.Format(utils.FormatDateTime),
@@ -34,6 +38,8 @@ func (p *PromoteTrainRecord) ToView() *PromoteTrainRecordView {
 type PromoteTrainRecordView struct {
 	Id              int
 	Title           string
+	Llm             string
+	Source          int
 	WechatArticleId int
 	TemplatePromote string
 	PromoteSendTime string
@@ -52,9 +58,9 @@ func DeleteContent(id int) error {
 	return global.DbMap[utils.DbNameAI].Model(&PromoteTrainRecord{}).Where("id = ?", id).Update("is_deleted", true).Error
 }
 
-func GetRecordList(wechatArticleId int) (list []*PromoteTrainRecordView, err error) {
+func GetRecordList(wechatArticleId, source int) (list []*PromoteTrainRecordView, err error) {
 	var ormList []PromoteTrainRecord
-	err = global.DbMap[utils.DbNameAI].Model(&PromoteTrainRecord{}).Where("wechat_article_id = ? and is_deleted=?", wechatArticleId, false).Order(`created_time DESC`).Find(&ormList).Error
+	err = global.DbMap[utils.DbNameAI].Model(&PromoteTrainRecord{}).Where("wechat_article_id = ? AND source = ?  and is_deleted=?", wechatArticleId, source, false).Order(`created_time DESC`).Find(&ormList).Error
 	if err != nil {
 		return
 	}

+ 31 - 5
models/rag/question.go

@@ -10,8 +10,11 @@ import (
 
 type Question struct {
 	QuestionId      int       `gorm:"column:question_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"question_id"`
+	QuestionTitle   string    `gorm:"column:question_title;type:varchar(255);comment:问题标题;" description:"问题标题"`
 	QuestionContent string    `gorm:"column:question_content;type:varchar(255);comment:问题内容;" description:"问题内容"`
 	Sort            int       `gorm:"column:sort;type:int(11);comment:排序;default:0;" description:"排序"`
+	SysUserId       int       `gorm:"column:sys_user_id;type:int(11);comment:添加人id;default:0;" description:"添加人id"`
+	SysUserRealName string    `gorm:"column:sys_user_real_name;type:varchar(255);comment:添加人真实名称;" description:"添加人真实名称"`
 	ModifyTime      time.Time `gorm:"column:modify_time;type:datetime;default:NULL;" description:"modify_time"`
 	CreateTime      time.Time `gorm:"column:create_time;type:datetime;default:NULL;" description:"create_time"`
 }
@@ -24,12 +27,14 @@ func (m *Question) TableName() string {
 // QuestionColumns get sql column name.获取数据库列名
 var QuestionColumns = struct {
 	QuestionID      string
+	QuestionTitle   string
 	QuestionContent string
 	Sort            string
 	ModifyTime      string
 	CreateTime      string
 }{
 	QuestionID:      "question_id",
+	QuestionTitle:   "question_title",
 	QuestionContent: "question_content",
 	Sort:            "sort",
 	ModifyTime:      "modify_time",
@@ -56,8 +61,11 @@ func (m *Question) Del() (err error) {
 
 type QuestionView struct {
 	QuestionId      int    `gorm:"column:question_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"question_id"`
-	QuestionContent string `gorm:"column:question_content;type:varchar(255);comment:问题内容;" description:"问题内容"` //
-	Sort            int    `gorm:"column:sort;type:int(11);comment:排序;default:0;" description:"sort"`          // 排序
+	QuestionTitle   string `gorm:"column:question_title;type:varchar(255);comment:问题标题;" description:"问题标题"`
+	QuestionContent string `gorm:"column:question_content;type:varchar(255);comment:问题内容;" description:"问题内容"`
+	Sort            int    `gorm:"column:sort;type:int(11);comment:排序;default:0;" description:"排序"`
+	SysUserId       int    `gorm:"column:sys_user_id;type:int(11);comment:添加人id;default:0;" description:"添加人id"`
+	SysUserRealName string `gorm:"column:sys_user_real_name;type:varchar(255);comment:添加人真实名称;" description:"添加人真实名称"`
 	ModifyTime      string `gorm:"column:modify_time;type:datetime;default:NULL;" description:"modify_time"`
 	CreateTime      string `gorm:"column:create_time;type:datetime;default:NULL;" description:"create_time"`
 }
@@ -73,8 +81,11 @@ func (m *Question) ToView() QuestionView {
 	}
 	return QuestionView{
 		QuestionId:      m.QuestionId,
+		QuestionTitle:   m.QuestionTitle,
 		QuestionContent: m.QuestionContent,
 		Sort:            m.Sort,
+		SysUserId:       m.SysUserId,
+		SysUserRealName: m.SysUserRealName,
 		ModifyTime:      modifyTime,
 		CreateTime:      createTime,
 	}
@@ -102,8 +113,11 @@ func (m *Question) GetByCondition(condition string, pars []interface{}) (item *Q
 	return
 }
 
-func (m *Question) GetListByCondition(condition string, pars []interface{}, startSize, pageSize int) (items []*Question, err error) {
-	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s order by question_id desc LIMIT ?,?`, m.TableName(), condition)
+func (m *Question) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*Question, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s order by question_id desc LIMIT ?,?`, field, m.TableName(), condition)
 	pars = append(pars, startSize, pageSize)
 	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
 
@@ -128,7 +142,19 @@ func (m *Question) GetPageListByCondition(condition string, pars []interface{},
 		return
 	}
 	if total > 0 {
-		items, err = m.GetListByCondition(condition, pars, startSize, pageSize)
+		items, err = m.GetListByCondition(``, condition, pars, startSize, pageSize)
+	}
+
+	return
+}
+
+func (m *Question) GetTitlePageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*Question, err error) {
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(`question_id,question_title,sort`, condition, pars, startSize, pageSize)
 	}
 
 	return

+ 168 - 0
models/rag/rag_eta_report.go

@@ -0,0 +1,168 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// RagEtaReport eta报告
+type RagEtaReport struct {
+	RagEtaReportId  int       `gorm:"primaryKey;column:rag_eta_report_id" `
+	ReportId        int       `gorm:"column:report_id" description:"报告id"`
+	ReportChapterId int       `gorm:"column:report_chapter_id" description:"报告章节id"`
+	Title           string    `gorm:"column:title" description:"报告标题(完整标题,含期数)"`
+	Author          string    `gorm:"column:author" description:"作者"`
+	TextContent     string    `gorm:"column:text_content" description:"报告内容(去除html)"`
+	VectorKey       string    `gorm:"column:vector_key" description:"向量库的key"`
+	IsPublished     int       `gorm:"column:is_published;type:tinyint(4);comment:是否已发布,0:未发布,1:已发布;default:1;" description:"是否已发布,0:未发布,1:已发布"`
+	IsDeleted       int       `gorm:"column:is_deleted;type:tinyint(4);comment:是否删除,0:未删除,1:已删除;default:0;" description:"否删除,0:未删除,1:已删除"`
+	PublishTime     time.Time `gorm:"column:publish_time" description:"发布时间"`
+	ModifyTime      time.Time `gorm:"column:modify_time" description:"修改时间"`
+	CreateTime      time.Time `gorm:"column:create_time" description:"新增时间"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *RagEtaReport) TableName() string {
+	return "rag_eta_report"
+}
+
+// RagEtaReportColumns get sql column name.获取数据库列名
+var RagEtaReportColumns = struct {
+	RagEtaReportID  string
+	ReportID        string
+	ReportChapterID string
+	Title           string
+	Author          string
+	TextContent     string
+	VectorKey       string
+	IsPublished     string
+	IsDeleted       string
+	PublishTime     string
+	ModifyTime      string
+	CreateTime      string
+}{
+	RagEtaReportID:  "rag_eta_report_id",
+	ReportID:        "report_id",
+	ReportChapterID: "report_chapter_id",
+	Title:           "title",
+	Author:          "author",
+	TextContent:     "text_content",
+	VectorKey:       "vector_key",
+	IsPublished:     "is_published",
+	IsDeleted:       "is_deleted",
+	PublishTime:     "publish_time",
+	ModifyTime:      "modify_time",
+	CreateTime:      "create_time",
+}
+
+type RagEtaReportView struct {
+	RagEtaReportId  int    `gorm:"primaryKey;column:rag_eta_report_id" `
+	ReportId        int    `gorm:"column:report_id" description:"报告id"`
+	ReportChapterId int    `gorm:"column:report_chapter_id" description:"报告章节id"`
+	Title           string `gorm:"column:title" description:"报告标题(完整标题,含期数)"`
+	Author          string `gorm:"column:author" description:"作者"`
+	Content         string `gorm:"column:content" description:"报告内容(包含html)"`
+	TextContent     string `gorm:"column:text_content" description:"报告内容(去除html)"`
+	VectorKey       string `gorm:"column:vector_key" description:"向量库的key"`
+	IsDeleted       int    `gorm:"column:is_deleted;type:tinyint(4);comment:是否删除,0:未删除,1:已删除;default:0;" description:"否删除,0:未删除,1:已删除"`
+	PublishTime     string `gorm:"column:publish_time" description:"发布时间"`
+	ModifyTime      string `gorm:"column:modify_time" description:"修改时间"`
+	CreateTime      string `gorm:"column:create_time" description:"新增时间"`
+}
+
+func (m *RagEtaReport) ToView() RagEtaReportView {
+	var publishTime, modifyTime, createTime string
+
+	if !m.PublishTime.IsZero() {
+		publishTime = m.PublishTime.Format(utils.FormatDateTime)
+	}
+	if !m.CreateTime.IsZero() {
+		createTime = m.CreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.ModifyTime.IsZero() {
+		modifyTime = m.ModifyTime.Format(utils.FormatDateTime)
+	}
+	return RagEtaReportView{
+		RagEtaReportId:  m.RagEtaReportId,
+		ReportId:        m.ReportId,
+		ReportChapterId: m.ReportChapterId,
+		Title:           m.Title,
+		Author:          m.Author,
+		TextContent:     m.TextContent,
+		VectorKey:       m.VectorKey,
+		PublishTime:     publishTime,
+		ModifyTime:      modifyTime,
+		CreateTime:      createTime,
+	}
+}
+
+func (m *RagEtaReport) ListToViewList(list []*RagEtaReport) (RagEtaReportViewList []RagEtaReportView) {
+	RagEtaReportViewList = make([]RagEtaReportView, 0)
+
+	for _, v := range list {
+		RagEtaReportViewList = append(RagEtaReportViewList, v.ToView())
+	}
+	return
+}
+
+func (m *RagEtaReport) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *RagEtaReport) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *RagEtaReport) GetById(id int) (item *RagEtaReport, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", RagEtaReportColumns.RagEtaReportID), id).First(&item).Error
+
+	return
+}
+
+func (m *RagEtaReport) GetByReportAndChapterId(reportId, reportChapterId int) (item *RagEtaReport, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ? AND %s = ? ", RagEtaReportColumns.ReportID, RagEtaReportColumns.ReportChapterID), reportId, reportChapterId).First(&item).Error
+
+	return
+}
+
+func (m *RagEtaReport) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*RagEtaReport, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE is_deleted=0 %s  order by publish_time desc,report_id desc,report_chapter_id desc LIMIT ?,?`, field, m.TableName(), condition)
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
+func (m *RagEtaReport) GetCountByCondition(condition string, pars []interface{}) (total int, err error) {
+	var intNull sql.NullInt64
+	sqlStr := fmt.Sprintf(`SELECT COUNT(1) total FROM %s WHERE 1=1 AND is_deleted=0 %s`, m.TableName(), condition)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Scan(&intNull).Error
+	if err == nil && intNull.Valid {
+		total = int(intNull.Int64)
+	}
+
+	return
+}
+
+func (m *RagEtaReport) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*RagEtaReport, err error) {
+
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(``, condition, pars, startSize, pageSize)
+	}
+
+	return
+}

+ 11 - 0
models/rag/response/eta_report.go

@@ -0,0 +1,11 @@
+package response
+
+import (
+	"eta/eta_api/models/rag"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type RagEtaReportListListResp struct {
+	List   []rag.RagEtaReportView
+	Paging *paging.PagingItem `description:"分页数据"`
+}

+ 12 - 1
models/report.go

@@ -91,6 +91,7 @@ type Report struct {
 	ReportCreateTime    time.Time `description:"报告时间创建时间"`
 	InheritReportId     int       `description:"待继承的报告ID"`
 	VoiceGenerateType   int       `description:"音频生成方式,0:系统生成,1:人工上传"`
+	RaiReportId         int       `description:"RAI报告ID"`
 	FreeLayoutConfig    string    `description:"'自由布局配置"`
 }
 
@@ -169,6 +170,7 @@ type ReportList struct {
 	ClassifyIdThird     int       `description:"三级分类id"`
 	ClassifyNameThird   string    `description:"三级分类名称"`
 	InheritReportId     int       `description:"待继承的报告ID"`
+	RaiReportId         int       `description:"RAI报告ID"`
 }
 
 func (m *ReportList) AfterFind(db *gorm.DB) (err error) {
@@ -240,7 +242,7 @@ func GetReportListByCondition(condition string, pars []interface{}, startSize, p
 	o := global.DbMap[utils.DbNameReport]
 	sql := `SELECT DISTINCT a.id, a.title, a.author, a.modify_time, a.publish_time,a.classify_id_first,
 a.classify_name_first,a.classify_id_second,a.classify_name_second,a.classify_id_third,
-a.classify_name_third,a.abstract,a.admin_id,a.admin_real_name,a.last_modify_admin_id,a.last_modify_admin_name FROM report as a WHERE 1=1  `
+a.classify_name_third,a.abstract,a.admin_id,a.admin_real_name,a.last_modify_admin_id,a.last_modify_admin_name,a.report_code FROM report as a WHERE 1=1  `
 	if condition != "" {
 		sql += condition
 	}
@@ -446,6 +448,7 @@ type ReportDetail struct {
 	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
 	IsPublicPublish     int8      `description:"是否公开发布,1:是,2:否"`
 	ReportCreateTime    time.Time `description:"报告时间创建时间"`
+	RaiReportId         int       `description:"RAI报告ID"`
 	FreeLayoutConfig    string    `description:"'自由布局配置"`
 }
 
@@ -1003,6 +1006,7 @@ type ElasticReportDetail struct {
 	BodyContent        string `description:"内容"`
 	PublishTime        string `description:"发布时间"`
 	PublishState       int    `description:"发布状态 1-未发布 2-已发布"`
+	IsPublicPublish    int8   `description:"是否公开发布,1:是,2:否"`
 	Author             string `description:"作者"`
 	ClassifyIdFirst    int    `description:"一级分类ID"`
 	ClassifyNameFirst  string `description:"一级分类名称"`
@@ -1696,6 +1700,13 @@ type ReportShartUrlResp struct {
 	UrlToken string `description:"分享链接token"`
 }
 
+func GetAllPublishReportId() (items []int, err error) {
+	o := global.DbMap[utils.DbNameReport]
+	sql := `SELECT  a.id FROM report as a WHERE 1=1 AND state in (2,6) `
+	err = o.Raw(sql).Find(&items).Error
+	return
+}
+
 func InsertOrUpdateReportFreeLayoutContentPage(reportInfo *Report, ormList []*report.ReportFreeLayout) (err error) {
 	tx := global.DbMap[utils.DbNameReport].Begin()
 

+ 29 - 0
models/report/report_free_layout.go

@@ -235,3 +235,32 @@ func GetFreeLayoutPagesByReportId(id int) (list []*ContentPage, err error) {
 	list = ToPageViewList(ormList)
 	return
 }
+
+// GetReportFreeLayoutListByReportId
+// @Description: 根据报告ID和章节ID获取所有的布局列表
+// @author: Roc
+// @datetime 2025-04-16 13:46:38
+// @param reportId int
+// @param chapterId int
+// @return list []*ReportFreeLayout
+// @return err error
+func GetReportFreeLayoutListByReportId(reportId, chapterId int) (list []*ReportFreeLayout, err error) {
+	sql := `select * from report_free_layout  where report_id =? and report_chapter_id=? order by page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId, chapterId).Find(&list).Error
+
+	return
+}
+
+// GetAllReportFreeLayoutListByReportId
+// @Description: 根据报告id获取所有的报告自由布局列表(含章节的)
+// @author: Roc
+// @datetime 2025-04-16 13:46:24
+// @param reportId int
+// @return list []*ReportFreeLayout
+// @return err error
+func GetAllReportFreeLayoutListByReportId(reportId int) (list []*ReportFreeLayout, err error) {
+	sql := `select * from report_free_layout  where report_id =? order by page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId).Find(&list).Error
+
+	return
+}

+ 5 - 3
models/report_chapter.go

@@ -510,9 +510,11 @@ func CountReportChapterByTypeId(typeId int) (count int, err error) {
 // AddReportChapter
 // @Description: 待添加的报告章节
 type AddReportChapter struct {
-	ReportChapter       *ReportChapter
-	GrantList           []*report.ReportChapterGrant
-	GrantPermissionList []*report.ReportChapterPermissionMapping
+	ReportChapter               *ReportChapter
+	GrantList                   []*report.ReportChapterGrant
+	GrantPermissionList         []*report.ReportChapterPermissionMapping
+	ReportChapterFreeLayoutList []*report.ReportFreeLayout
+	InheritReportChapterId      int `description:"继承的章节id"`
 }
 
 // EditReportChapterBaseInfoAndPermissionReq

+ 118 - 0
models/report_rai.go

@@ -0,0 +1,118 @@
+package models
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"time"
+)
+
+type RaiReportNotifyRedis struct {
+	ArticleId  int       `description:"文章ID"`
+	Action     string    `description:"日志类型:add,edit,move"`
+	CreateTime time.Time `description:"创建时间"`
+}
+
+type ArticleDetailResultApi struct {
+	Data ArticleResultApidate `json:"data"`
+	Code int                  `json:"code"`
+	Msg  string               `json:"msg"`
+}
+
+type ArticleResultApidate struct {
+	ArticleId     int                      `json:"id"`
+	Title         string                   `json:"title"`
+	File          string                   `json:"file"`
+	TitleEn       string                   `json:"title_en"`
+	Frequency     string                   `json:"frequency"`
+	CreateDate    string                   `json:"create_date"`
+	UpdateDate    time.Time                   `json:"update_date"`
+	PublishDate   time.Time                `json:"publish_date"`
+	PublishStatus int                      `json:"publish_status" description:"发布状态: 0未发布,2已发布,4已发布"`
+	IndustrId     int                      `json:"industry_id"`
+	SeriesId      int                      `json:"series_id"`
+	Series        ArticleSeries            `json:"series"`
+	Content       ArticleResultApiContent  `json:"content"`
+	Author        ArticleResultApiAuthor   `json:"author"`
+	Industry      ArticleResultApiIndustry `json:"industry"`
+	Type          ArticleResultApiType     `json:"type"`
+	Stock         []string                 `json:"stock"`
+	Field         ArticleField             `json:"field"`
+	Corpus        Corpus                   `json:"corpus"`
+	Cover         string                   `json:"cover"`
+	TypeId        int                      `json:"type_id"`
+	IsActive      bool                     `json:"is_active" description:"是否有效: 0无效,1有效"`
+	PublishArea   string                   `json:"publish_area"`
+}
+
+type ArticleField struct {
+	Id          int    `json:"id"`
+	Name        string `json:"name"`
+	Description string `json:"description"`
+	IndustryId  int    `json:"industry_id"`
+}
+
+type Corpus struct {
+	Id        int    `json:"id"`
+	ArticleId int    `json:"article_id"`
+	Corpus    string `json:"corpus"`
+}
+
+type ArticleSeries struct {
+	Name string `json:"name"`
+}
+type ArticleResultApiContent struct {
+	ArticleId  int    `json:"id"`
+	Body       string `json:"body"`
+	Abstract   string `json:"abstract"`
+	Annotation string `json:"annotation"`
+}
+
+type ArticleResultApiAuthor struct {
+	PhoneNumber string `json:"phone_number"`
+	Name        string `json:"name"`
+}
+
+type ArticleResultApiIndustry struct {
+	Name string `json:"name"`
+}
+
+type ArticleResultApiType struct {
+	Name string `json:"name"`
+}
+
+type ArticleIndustryApi struct {
+	Data []ArticleResultApiIndustrdate `json:"data"`
+	Code int                           `json:"code"`
+	Msg  string                        `json:"msg"`
+}
+
+type ArticleResultApiIndustrdate struct {
+	Id     int                          `json:"id"`
+	Name   string                       `json:"name"`
+	Series []ArticleResultApiSeriesdate `json:"series"`
+}
+
+type ArticleResultApiSeriesdate struct {
+	Id   int    `json:"id"`
+	Name string `json:"name"`
+}
+
+func GetReportByRaiReportId(raiReportId int) (report *Report, err error) {
+	report = new(Report)
+	err = global.DbMap[utils.DbNameReport].Model(&Report{}).Where("rai_report_id = ?", raiReportId).First(report).Error
+	return
+}
+
+type RaiArticleListResultApi struct {
+	Data []ArticleResultApidate `json:"data"`
+	Code int                  `json:"code"`
+	Msg  string               `json:"msg"`
+	Pagination Pagination `json:"pagination"`
+}
+
+type Pagination struct {
+	Total int `json:"total"`
+	Page  int `json:"page"`
+	PageSize  int `json:"page_size"`
+	PageTotal int `json:"page_total"`
+}

+ 25 - 1
models/report_v2.go

@@ -17,7 +17,7 @@ import (
 // @param addReportChapterList []AddReportChapter
 // @return reportId int64
 // @return err error
-func AddReportAndChapter(reportItem *Report, allGrantUserList []*report.ReportGrant, addReportChapterList []AddReportChapter) (reportId int64, err error) {
+func AddReportAndChapter(reportItem *Report, reportFreeLayoutList []*report.ReportFreeLayout, allGrantUserList []*report.ReportGrant, addReportChapterList []AddReportChapter) (reportId int64, err error) {
 	to := global.DbMap[utils.DbNameReport].Begin()
 	defer func() {
 		if err != nil {
@@ -34,6 +34,17 @@ func AddReportAndChapter(reportItem *Report, allGrantUserList []*report.ReportGr
 	}
 	reportId = int64(reportItem.Id)
 
+	// 新增报告分页内容
+	if len(reportFreeLayoutList) > 0 {
+		for _, reportFreeLayout := range reportFreeLayoutList {
+			reportFreeLayout.ReportId = int(reportId)
+		}
+		err = to.CreateInBatches(reportFreeLayoutList, utils.MultiAddNum).Error
+		if err != nil {
+			return
+		}
+	}
+
 	// 新增报告授权
 	if len(allGrantUserList) > 0 {
 		for _, v := range allGrantUserList {
@@ -81,6 +92,19 @@ func AddReportAndChapter(reportItem *Report, allGrantUserList []*report.ReportGr
 				}
 			}
 
+			// 新增报告章节分页内容
+			if len(addReportChapter.ReportChapterFreeLayoutList) > 0 {
+				reportChapterFreeLayoutList := addReportChapter.ReportChapterFreeLayoutList
+				for _, reportChapterFreeLayout := range reportChapterFreeLayoutList {
+					reportChapterFreeLayout.ReportId = int(reportId)
+					reportChapterFreeLayout.ReportChapterId = chapterItem.ReportChapterId
+				}
+				err = to.CreateInBatches(reportChapterFreeLayoutList, utils.MultiAddNum).Error
+				if err != nil {
+					return
+				}
+			}
+
 		}
 	}
 

+ 2 - 0
models/smart_report/smart_resource.go

@@ -107,6 +107,8 @@ func (m *SmartReportResource) GetItemsByCondition(condition string, pars []inter
 // SmartReportResourceEditReq 智能研报资源编辑请求体
 type SmartReportResourceEditReq struct {
 	ResourceId int    `description:"资源ID"`
+	Type       int    `description:"类型 1-版头 2-版尾"`
+	ImgUrl     string `description:"图片资源地址"`
 	ImgName    string `description:"图片名称"`
 	Style      string `description:"版图样式"`
 }

+ 4 - 2
models/system/sys_admin.go

@@ -208,9 +208,10 @@ type SysuserEditEnabledReq struct {
 // 变更状态
 func EditAdminEnabled(item *Admin) (err error) {
 	o := global.DbMap[utils.DbNameMaster]
-	sql := `UPDATE "admin"
+	sql := `UPDATE admin
 			SET
 			  last_updated_time = NOW(),enabled=? WHERE admin_id = ? `
+	sql = utils.ReplaceDriverKeywords("", sql)
 	err = o.Exec(sql, item.Enabled, item.AdminId).Error
 	return
 }
@@ -218,9 +219,10 @@ func EditAdminEnabled(item *Admin) (err error) {
 // DisableAdmin 禁用用户
 func DisableAdmin(adminId int) (err error) {
 	o := global.DbMap[utils.DbNameMaster]
-	sql := `UPDATE "admin"
+	sql := `UPDATE admin
 			SET
 			  last_updated_time = NOW(),disable_time=now(),enabled=0 WHERE admin_id = ? `
+	sql = utils.ReplaceDriverKeywords("", sql)
 	err = o.Exec(sql, adminId).Error
 	return
 }

+ 8 - 0
models/system/sys_role.go

@@ -285,3 +285,11 @@ func GetSysRoleByIdList(id []int) (items []*SysRole, err error) {
 	err = o.Raw(sql, id).Find(&items).Error
 	return
 }
+
+
+// 更新所有管理员的角色信息
+func UpdateAdminRoleInfoByRoleId(roleId int, roleName, roleTypeCode string) (err error) {
+	sql := `UPDATE admin SET role_name=?, role_type_code=? WHERE role_id=?`
+	err = global.DbMap[utils.DbNameMaster].Exec(sql, roleName, roleTypeCode, roleId).Error
+	return
+}

+ 171 - 0
routers/commentsRouter.go

@@ -2914,6 +2914,96 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangClassify",
+            Router: `/purang/classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangIndexList",
+            Router: `/purang/classify/index/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangEdbInfoAdd",
+            Router: `/purang/edb_info/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangAddCheck",
+            Router: `/purang/edb_info/add_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangNameCheck",
+            Router: `/purang/edb_info/name_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "ExportPurangList",
+            Router: `/purang/export`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "GetFrequency",
+            Router: `/purang/frequency`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangIndexData",
+            Router: `/purang/index/data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangSearchList",
+            Router: `/purang/search_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangSingleData",
+            Router: `/purang/single_data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
         beego.ControllerComments{
             Method: "ClassifyAdd",
@@ -3508,6 +3598,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "UpdateChartClassifyResource",
+            Router: `/chart_classify/forum_chart/update`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
         beego.ControllerComments{
             Method: "ChartClassifyItems",
@@ -3535,6 +3634,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "SetChartClassifyResourceStatus",
+            Router: `/chart_classify/resource_status`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
         beego.ControllerComments{
             Method: "AddManualClassify",
@@ -3715,6 +3823,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "AddChartDescription",
+            Router: `/chart_info/description/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "GetChartDescriptionList",
+            Router: `/chart_info/description/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
         beego.ControllerComments{
             Method: "ChartInfoDetail",
@@ -8611,6 +8737,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/question/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"],
         beego.ControllerComments{
             Method: "Edit",
@@ -8629,6 +8764,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"],
+        beego.ControllerComments{
+            Method: "TitleList",
+            Router: `/question/title/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportController"],
+        beego.ControllerComments{
+            Method: "ArticleDel",
+            Router: `/eta_report/article/del`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportController"],
+        beego.ControllerComments{
+            Method: "ArticleDetail",
+            Router: `/eta_report/article/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportController"],
+        beego.ControllerComments{
+            Method: "ArticleList",
+            Router: `/eta_report/article/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
         beego.ControllerComments{
             Method: "ChatRecordList",

+ 2 - 0
routers/router.go

@@ -79,6 +79,7 @@ func init() {
 				&llm.QuestionController{},
 				&llm.AbstractController{},
 				&llm.PromoteController{},
+				&llm.RagEtaReportController{},
 			),
 		),
 		web.NSNamespace("/banner",
@@ -204,6 +205,7 @@ func init() {
 				&data_manage.BaseFromRzdIndexController{},
 				&data_manage.ClarksonsDataController{},
 				&data_manage.BaseFromGprRiskController{},
+				&data_manage.BaseFromPurangController{},
 				&data_manage.BaseFromRadishResearchController{},
 			),
 		),

+ 49 - 0
services/data/base_from_purang.go

@@ -0,0 +1,49 @@
+package data
+
+import (
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/utils"
+	"fmt"
+)
+
+// PurangIndexSource2Edb 新增普瑞数据源到指标库
+func PurangIndexSource2Edb(req data_manage.PurangIndexSource2EdbReq, lang string) (edb *data_manage.EdbInfo, err error, errMsg string, skip bool) {
+	if req.EdbCode == "" {
+		err = fmt.Errorf("指标ID为空")
+		return
+	}
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("PurangIndexSource2Edb新增失败, Err: %s", err.Error())
+			fmt.Println(tips)
+			utils.FileLog.Info(tips)
+		}
+	}()
+	source := utils.DATA_SOURCE_PURANG
+
+	// 是否新增过指标
+	exist, e := data_manage.GetEdbInfoByEdbCode(source, req.EdbCode)
+	if e != nil && !utils.IsErrNoRow(e) {
+		err = fmt.Errorf("获取指标是否存在失败, err: %s", e.Error())
+		return
+	}
+	if exist != nil && exist.EdbInfoId > 0 {
+		skip = true
+		return
+	}
+
+	// 开始结束时间
+	var startDate, endDate string
+
+	// 新增指标库
+	edbInfo, e, msg, _ := EdbInfoAdd(source, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, startDate, endDate, req.AdminId, req.AdminRealName, lang)
+	if e != nil {
+		errMsg = msg
+		err = fmt.Errorf("EdbInfo: 新增指标失败, err: %s", e.Error())
+		return
+	}
+
+	edb = edbInfo
+
+	return
+} 

+ 88 - 15
services/data/base_from_sci_hq.go

@@ -366,8 +366,9 @@ func MoveSciHqClassify(classifyId, parentClassifyId, prevClassifyId, nextClassif
 		classifyInfo.Level = parentClassifyInfo.Level + 1
 		updateCol = append(updateCol, "ParentId", "Level")
 	}
-	var currentSort, prevSort, nextSort int
-	currentSort = classifyInfo.Sort
+	var prevSort, nextSort int
+	//var currentSort, prevSort, nextSort int
+	//currentSort = classifyInfo.Sort
 
 	var prevClassify *data_manage.BaseFromSciHqClassify
 	var nextClassify *data_manage.BaseFromSciHqClassify
@@ -425,25 +426,97 @@ func MoveSciHqClassify(classifyId, parentClassifyId, prevClassifyId, nextClassif
 		}
 	}
 	//移到两个排序值中间操作
-	if prevSort >= currentSort {
-		//往下移动
-		err = data_manage.MoveDownSciHqIndexClassifyBySort(parentClassifyId, prevSort, currentSort)
+	//if prevSort >= currentSort {
+	//	//往下移动
+	//	err = data_manage.MoveDownSciHqIndexClassifyBySort(parentClassifyId, prevSort, currentSort)
+	//	if err != nil {
+	//		err = errors.New("向下移动出错:" + err.Error())
+	//		return
+	//	}
+	//	classifyInfo.Sort = prevSort
+	//} else if nextSort <= currentSort && nextSort != 0 {
+	//	//往上移动
+	//	err = data_manage.MoveUpSciHqIndexClassifyBySort(parentClassifyId, nextSort, currentSort)
+	//	if err != nil {
+	//		err = errors.New("向上移动出错:" + err.Error())
+	//		return
+	//	}
+	//	classifyInfo.Sort = nextSort
+	//}
+	//classifyInfo.ModifyTime = time.Now()
+	//updateCol = append(updateCol, "Sort", "ModifyTime")
+	//err = classifyInfo.Update(updateCol)
+	//if err != nil {
+	//	errMsg = "移动失败"
+	//	err = errors.New("修改失败,Err:" + err.Error())
+	//	return
+	//}
+
+	if prevSort >= 0 {
+		//如果是移动在两个兄弟节点之间
+		if nextSort > 0 {
+			//下一个兄弟节点
+			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+			if prevSort == nextSort || prevSort == classifyInfo.Sort {
+				//变更兄弟节点的排序
+				updateSortStr := `sort + 2`
+
+				//变更分类
+				if prevClassify != nil {
+					_ = data_manage.UpdateSciHqClassifySortByParentId(parentClassifyId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr)
+				} else {
+					_ = data_manage.UpdateSciHqClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+				}
+			} else {
+				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+				if nextSort-prevSort == 1 {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 1`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateSciHqClassifySortByParentId(parentClassifyId, prevClassify.ClassifyId, prevSort, updateSortStr)
+					} else {
+						_ = data_manage.UpdateSciHqClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+				}
+			}
+		}
+
+		classifyInfo.Sort = prevSort + 1
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	} else if prevClassify == nil && nextClassify == nil && parentClassifyId > 0 {
+		//处理只拖动到目录里,默认放到目录底部的情况
+		var maxSort int
+		maxSort, err = data_manage.GetSciHqClassifySortMaxByParentId(parentClassifyId)
 		if err != nil {
-			err = errors.New("向下移动出错:" + err.Error())
+			errMsg = "移动失败"
+			err = errors.New("查询组内排序信息失败,Err:" + err.Error())
 			return
 		}
-		classifyInfo.Sort = prevSort
-	} else if nextSort <= currentSort && nextSort != 0 {
-		//往上移动
-		err = data_manage.MoveUpSciHqIndexClassifyBySort(parentClassifyId, nextSort, currentSort)
-		if err != nil {
-			err = errors.New("向上移动出错:" + err.Error())
+		classifyInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	} else {
+		// 拖动到父级分类的第一位
+		firstClassify, tmpErr := data_manage.GetFirstSciHqClassifyByParentId(parentClassifyId)
+		if tmpErr != nil && !utils.IsErrNoRow(tmpErr) {
+			errMsg = "移动失败"
+			err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
 			return
 		}
-		classifyInfo.Sort = nextSort
+
+		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+		if firstClassify != nil && firstClassify.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = data_manage.UpdateSciHqClassifySortByParentId(parentClassifyId, firstClassify.ClassifyId-1, 0, updateSortStr)
+		}
+		classifyInfo.Sort = 0 //那就是排在第一位
+		classifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
 	}
-	classifyInfo.ModifyTime = time.Now()
-	updateCol = append(updateCol, "Sort", "ModifyTime")
+
 	err = classifyInfo.Update(updateCol)
 	if err != nil {
 		errMsg = "移动失败"

+ 2 - 2
services/data/base_from_ths_hf_classify.go

@@ -85,13 +85,13 @@ func ThsHfMoveClassify(req data_manage.BaseFromThsHfClassifyMoveReq, sysUser *sy
 			return
 		}
 		// 如果是移动目录, 那么校验一下父级目录下是否有重名目录
-		exists, e := data_manage.GetEdbClassifyByParentIdAndName(parentClassifyId, edbClassifyInfo.ClassifyName, classifyId)
+		exists, e := data_manage.GetThsHfClassifyByParentIdAndName(parentClassifyId, edbClassifyInfo.ClassifyName, classifyId)
 		if e != nil && !utils.IsErrNoRow(e) {
 			errMsg = "移动失败"
 			err = fmt.Errorf("获取父级分类下的同名分类失败, Err: %s", e.Error())
 			return
 		}
-		if exists != nil && exists.ClassifyId > 0 {
+		if exists != nil && exists.BaseFromThsHfClassifyId > 0 {
 			errMsg = "移动失败,分类名称已存在"
 			return
 		}

+ 157 - 15
services/data/chart_classify.go

@@ -5,9 +5,11 @@ import (
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data/data_manage_permission"
+	"eta/eta_api/services/eta_forum"
 	"eta/eta_api/utils"
 	"fmt"
 	"strconv"
+	"strings"
 	"time"
 )
 
@@ -269,7 +271,7 @@ func HandleNoPermissionChart(allNodes []*data_manage.ChartClassifyItems, noPermi
 // @return err error
 // @return errMsg string
 // @return isSendEmail bool
-func AddChartClassify(chartClassifyName string, parentId, level, source int, lang string, sysUser *system.Admin) (classifyInfo *data_manage.ChartClassify, err error, errMsg string, isSendEmail bool) {
+func AddChartClassify(chartClassifyName string, parentId, level, source int, isSelected int, lang string, sysUser *system.Admin) (classifyInfo *data_manage.ChartClassify, err error, errMsg string, isSendEmail bool) {
 	isSendEmail = true
 	errMsg = "保存分类失败"
 
@@ -308,6 +310,7 @@ func AddChartClassify(chartClassifyName string, parentId, level, source int, lan
 	}
 	//查询顶级rootId
 	rootId := 0
+	levelPath := ""
 	if parentId > 0 {
 		parentClassify, tErr := data_manage.GetChartClassifyById(parentId)
 		if tErr != nil {
@@ -321,6 +324,8 @@ func AddChartClassify(chartClassifyName string, parentId, level, source int, lan
 			return
 		}
 		rootId = parentClassify.RootId
+		levelPath = parentClassify.LevelPath
+		isSelected = parentClassify.IsSelected
 	}
 
 	classifyInfo = new(data_manage.ChartClassify)
@@ -338,23 +343,34 @@ func AddChartClassify(chartClassifyName string, parentId, level, source int, lan
 	classifyInfo.Sort = maxSort + 1
 	classifyInfo.Source = source
 	classifyInfo.RootId = rootId
-
+	classifyInfo.IsSelected = isSelected
 	newId, err := data_manage.AddChartClassify(classifyInfo)
 	if err != nil {
 		return
 	}
+	updateCols := make([]string, 0)
 	if parentId == 0 { //一级目录的rootId等于自己本身
 		classifyInfo.RootId = int(newId)
-		err = classifyInfo.Update([]string{"RootId"})
-		if err != nil {
-			errMsg = "更新分类失败"
-			return
-		}
+		updateCols = append(updateCols, "RootId")
+	}
+	if parentId > 0 {
+		levelPath = fmt.Sprintf("%s%d,", levelPath, newId)
+	} else {
+		levelPath = fmt.Sprintf("%d,", newId)
+	}
+	updateCols = append(updateCols, "LevelPath")
+	classifyInfo.LevelPath = levelPath
+	err = classifyInfo.Update(updateCols)
+	if err != nil {
+		errMsg = "更新分类失败"
+		return
 	}
-
 	// 目前只有ETA图库需要继承分类权限
 	if classifyInfo.Source == utils.CHART_SOURCE_DEFAULT {
 		go data_manage_permission.InheritParentClassify(5, classifyInfo.Source, classifyInfo.ChartClassifyId, classifyInfo.ParentId, classifyInfo.ChartClassifyName)
+		if isSelected == utils.ChartClassifyIsSelected { // 如果分类设置为精选资源,则需要同步到ETA资源库
+			go eta_forum.ChartClassifySave(classifyInfo.ChartClassifyId)
+		}
 	}
 
 	return
@@ -621,7 +637,9 @@ func moveChartClassify(parentChartClassifyInfo, chartClassifyInfo, prevClassify,
 	// 移动对象为分类, 判断分类是否存在
 	if chartClassifyInfo != nil {
 		oldParentId := chartClassifyInfo.ParentId
+		oldLevelPath := chartClassifyInfo.LevelPath
 		oldLevel := chartClassifyInfo.Level
+		oldSelected := chartClassifyInfo.IsSelected
 		var classifyIds []int
 		if oldParentId != parentClassifyId {
 			//更新子分类对应的level
@@ -779,6 +797,23 @@ func moveChartClassify(parentChartClassifyInfo, chartClassifyInfo, prevClassify,
 					}
 				}
 			}
+
+			//更新分类的level_path
+			if oldParentId != parentClassifyId {
+				if err = UpdateChartClassifyLevelPathWithChildren(chartClassifyInfo, parentChartClassifyInfo, oldParentId, oldLevelPath); err != nil {
+					errMsg = "移动失败"
+					err = errors.New("更新分类level_path失败,Err:" + err.Error())
+					return
+				}
+			}
+			if source == utils.CHART_SOURCE_DEFAULT {
+				if err = UpdateChildClassifySelection(chartClassifyInfo, parentChartClassifyInfo, oldSelected); err != nil {
+					errMsg = "移动失败"
+					err = errors.New("更新子目录精选标识失败,Err:" + err.Error())
+					return
+				}
+				go eta_forum.ChartClassifySaveBatch(chartClassifyInfo.Source)
+			}
 		}
 	} else {
 		if chartInfo == nil {
@@ -898,11 +933,47 @@ func moveChartClassify(parentChartClassifyInfo, chartClassifyInfo, prevClassify,
 				err = errors.New("修改失败,Err:" + err.Error())
 				return
 			}
+
+			// 判断是否为精选目录
+			// 如果该目录不是精选目录,且该图表已经上架,则需撤回该图表
+			if parentChartClassifyInfo.IsSelected == 0 && chartInfo.ForumChartInfoId > 0 {
+				go eta_forum.DeleteChart(chartInfo.ChartInfoId)
+			}
 		}
 	}
 	return
 }
 
+// 新增内部函数
+func UpdateChartClassifyLevelPathWithChildren(chartClassifyInfo *data_manage.ChartClassify, parentChartClassifyInfo *data_manage.ChartClassify, oldParentId int, oldLevelPath string) error {
+	levelPath := fmt.Sprintf("%d,", chartClassifyInfo.ChartClassifyId)
+	if parentChartClassifyInfo != nil {
+		levelPath = fmt.Sprintf("%s%d,", parentChartClassifyInfo.LevelPath, chartClassifyInfo.ChartClassifyId)
+	}
+	chartClassifyInfo.LevelPath = levelPath
+	if err := chartClassifyInfo.Update([]string{"LevelPath"}); err != nil {
+		return fmt.Errorf("修改失败,Err:" + err.Error())
+	}
+
+	// 更新子分类的levelpath
+	tmpList, err := data_manage.GetChartClassifyByLevelPath(oldLevelPath, chartClassifyInfo.Source)
+	if err != nil {
+		return fmt.Errorf("保存分类失败,Err:" + err.Error())
+	}
+    // 把原先的父级levePath,替换成最新的父级序列
+	for _, tmp := range tmpList {
+		after, _ := strings.CutPrefix(tmp.LevelPath, oldLevelPath)
+		if after != "" {
+			tmp.LevelPath = levelPath + after
+			tmp.ModifyTime = time.Now()
+			if e := tmp.Update([]string{"LevelPath", "ModifyTime"}); e != nil {
+				return fmt.Errorf("修改子分类,Err:" + e.Error())
+			}
+		}
+	}
+	return nil
+}
+
 func GetChildChartClassifyByClassifyId(targetClassifyId int) (targetList []*data_manage.ChartClassifyIdItems, err error, errMsg string) {
 	//判断是否是挂在顶级目录下
 	targetClassify, err := data_manage.GetChartClassifyById(targetClassifyId)
@@ -996,8 +1067,35 @@ func GetChartClassifyParentRecursive(list []*data_manage.ChartClassifyItems, cla
 	return res
 }
 
+// 新增处理子分类精选状态的函数
+func UpdateChildClassifySelection(classifyInfo *data_manage.ChartClassify, parentClassifyInfo *data_manage.ChartClassify, oldSelected int) error {
+	// 处理一级目录
+	if classifyInfo.ParentId == 0 {
+		if oldSelected != classifyInfo.IsSelected {
+			return data_manage.UpdateChartClassifyIsSelected(
+				classifyInfo.Source,
+				classifyInfo.IsSelected,
+				classifyInfo.LevelPath,
+			)
+		}
+		return nil
+	}else {
+		// 处理二级及以上目录
+		if classifyInfo.IsSelected != parentClassifyInfo.IsSelected {
+			return data_manage.UpdateChartClassifyIsSelected(
+				classifyInfo.Source,
+				parentClassifyInfo.IsSelected,
+				classifyInfo.LevelPath,
+			)
+		}
+	}
+
+	
+	return nil
+}
+
 // 修改图表分类,可以修改父级
-func EditChartClassifyV2(chartClassifyId, parentId, source int, chartClassifyName, lang string) (classifyInfo *data_manage.ChartClassify, err error, errMsg string, isSendEmail bool) {
+func EditChartClassifyV2(chartClassifyId, parentId, source int, chartClassifyName string, isSelected int, lang string) (classifyInfo *data_manage.ChartClassify, isDeleteForumChart bool, tipCode string, err error, errMsg string, isSendEmail bool) {
 	isSendEmail = true
 	errMsg = "保存失败"
 
@@ -1006,19 +1104,40 @@ func EditChartClassifyV2(chartClassifyId, parentId, source int, chartClassifyNam
 	if err != nil {
 		return
 	}
-
-	if parentId != classifyInfo.ParentId {
-		parentClassifyInfo, e := data_manage.GetChartClassifyById(parentId)
-		if e != nil {
-			err = e
+	oldSelected := classifyInfo.IsSelected
+	var parentClassifyInfo *data_manage.ChartClassify
+	if parentId > 0 {
+		parentClassifyInfo, err= data_manage.GetChartClassifyById(parentId)
+		if err != nil {
 			return
 		}
+	}
+	if parentId != classifyInfo.ParentId {
 		if classifyInfo.Level != parentClassifyInfo.Level+1 {
 			err = errors.New("父级分类层级异常")
 			return
 		}
 	}
 
+	// 判断取消精选的情况
+	if classifyInfo.Source == utils.CHART_SOURCE_DEFAULT && parentId == 0 && oldSelected == 1 && isSelected == 0 {
+		// 取消精选,如果存在已经上架的分类,则提示用户下架所有的分类
+		count, e := data_manage.GetChartClassifyHasUpChartBySource(classifyInfo.Source, classifyInfo.LevelPath)
+		if e != nil {
+			errMsg = "查询是否存在已经上架的分类失败"
+			err = fmt.Errorf("查询是否存在已经上架的分类失败,Err:%w", e)
+			isSendEmail = false
+			return
+		}
+		if count > 0 {
+			// errMsg = "该分类下存在已经上架的分类,请先下架所有的分类,再取消精选"
+			// err = fmt.Errorf("",errMsg)
+			tipCode = "CHART_CLASSIFY_HAS_UP_CHART"
+			return
+		}
+		isDeleteForumChart = true
+	}
+
 	// 分类来源校验
 	if classifyInfo.Source != source {
 		errMsg = "图表分类异常"
@@ -1029,12 +1148,17 @@ func EditChartClassifyV2(chartClassifyId, parentId, source int, chartClassifyNam
 
 	// 需要变更的字段
 	updateCols := make([]string, 0)
-
+	oldParentId := classifyInfo.ParentId
+	oldLevelPath := classifyInfo.LevelPath
 	if parentId != classifyInfo.ParentId {
 		classifyInfo.ParentId = parentId
 		classifyInfo.ModifyTime = time.Now()
 		updateCols = append(updateCols, "ParentId")
 	}
+	if isSelected != classifyInfo.IsSelected && classifyInfo.Source == utils.CHART_SOURCE_DEFAULT && classifyInfo.ParentId == 0 {
+		classifyInfo.IsSelected = isSelected
+		updateCols = append(updateCols, "IsSelected")
+	}
 
 	// 语言版本校验
 	switch lang {
@@ -1082,6 +1206,24 @@ func EditChartClassifyV2(chartClassifyId, parentId, source int, chartClassifyNam
 		err = classifyInfo.Update(updateCols)
 	}
 
+	if oldParentId != parentId {
+		if err = UpdateChartClassifyLevelPathWithChildren(classifyInfo, parentClassifyInfo, oldParentId, oldLevelPath); err != nil {
+			errMsg = "修改分类失败"
+			err = errors.New("更新分类level_path失败,Err:" + err.Error())
+			return
+		}
+	}
+	// 如果是精选目录,则需要同步到ETA资源库
+	if classifyInfo.Source == utils.CHART_SOURCE_DEFAULT {
+		// 如果当前目录的精选标识发生变化,需要同步更新子目录的精选标识
+		if err = UpdateChildClassifySelection(classifyInfo, parentClassifyInfo, oldSelected); err != nil {
+			errMsg = "修改分类失败"
+			err = fmt.Errorf("更新子目录精选标识失败,Err:%w", err)
+			return
+		}
+		go eta_forum.ChartClassifySaveBatch(classifyInfo.Source)
+	}
+
 	return
 }
 

+ 7 - 0
services/data/chart_info.go

@@ -9,6 +9,7 @@ import (
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data/data_manage_permission"
+	"eta/eta_api/services/eta_forum"
 	"eta/eta_api/utils"
 	"fmt"
 	"math"
@@ -3040,6 +3041,12 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin, lang
 	//修改my eta es数据
 	go EsAddOrEditMyChartInfoByChartInfoId(chartItem.ChartInfoId)
 
+	// 移出精选分类,从资源库下架
+	// 判断是否为精选目录
+	// 如果该目录不是精选目录,且该图表已经上架,则需撤回该图表
+	if chartClassify.IsSelected == 0 && chartItem.ForumChartInfoId > 0 {
+		go eta_forum.DeleteChart(chartItem.ChartInfoId)
+	}
 	return
 }
 

+ 1 - 1
services/data/cross_variety/chart.go

@@ -644,7 +644,7 @@ func AddChartInfo(req request.AddChartReq, sysUser *system.Admin, lang string) (
 		// 分类没有,需要创建新的分类,那么err置空
 		err = nil
 
-		_, err, errMsg, isSendEmail = data.AddChartClassify(sysUser.RealName, 0, 1, source, lang, sysUser)
+		_, err, errMsg, isSendEmail = data.AddChartClassify(sysUser.RealName, 0, 1, source, 0, lang, sysUser)
 		if err != nil {
 			return
 		}

+ 2 - 2
services/data/edb_classify.go

@@ -1108,13 +1108,13 @@ func MoveEdbClassify(req data_manage.MoveEdbClassifyReq, sysUser *system.Admin,
 			return
 		}
 		// 如果是移动目录, 那么校验一下父级目录下是否有重名目录
-		exists, e := data_manage.GetEdbClassifyByParentIdAndName(parentClassifyId, edbClassifyInfo.ClassifyName, classifyId)
+		exists, e := data_manage.GetEdbClassifyByParentIdAndName(parentClassifyId, edbClassifyInfo.ClassifyName, classifyId, int(classifyType))
 		if e != nil && !utils.IsErrNoRow(e) {
 			errMsg = "移动失败"
 			err = fmt.Errorf("获取父级分类下的同名分类失败, Err: %s", e.Error())
 			return
 		}
-		if exists != nil && exists.ClassifyId>0{
+		if exists != nil && exists.ClassifyId > 0 {
 			errMsg = "移动失败,分类名称已存在"
 			return
 		}

+ 13 - 0
services/data/excel/custom_analysis.go

@@ -129,6 +129,11 @@ func AddCustomAnalysisTable(excelName, content, excelImage string, excelClassify
 			err = tmpErr
 			return
 		}
+		sheetFrozen, tmpErr := json.Marshal(sheetInfo.Frozen)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
 		addSheetItem := excel.AddExcelSheetParams{
 			ExcelSheetId: 0,
 			ExcelInfoId:  0,
@@ -137,6 +142,7 @@ func AddCustomAnalysisTable(excelName, content, excelImage string, excelClassify
 			Sort:         k,
 			Config:       string(sheetConf),
 			CalcChain:    string(sheetCalcChain),
+			Frozen:       string(sheetFrozen),
 		}
 
 		lenCellData := len(sheetInfo.CellData)
@@ -337,6 +343,11 @@ func SaveCustomAnalysisTable(excelInfo *excel.ExcelInfo, excelName, content, exc
 			err = tmpErr
 			return
 		}
+		sheetFrozen, tmpErr := json.Marshal(sheetInfo.Frozen)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
 		addSheetItem := excel.AddExcelSheetParams{
 			ExcelSheetId: 0,
 			ExcelInfoId:  excelInfo.ExcelInfoId,
@@ -345,6 +356,7 @@ func SaveCustomAnalysisTable(excelInfo *excel.ExcelInfo, excelName, content, exc
 			Sort:         k,
 			Config:       string(sheetConf),
 			CalcChain:    string(sheetCalcChain),
+			Frozen:       string(sheetFrozen),
 		}
 
 		lenCellData := len(sheetInfo.CellData)
@@ -411,6 +423,7 @@ type LuckySheet struct {
 	DefaultRowHeight int                  `json:"defaultRowHeight"`
 	CellData         []LuckySheetCellData `json:"celldata"`
 	CalcChain        []interface{}        `json:"calcChain"`
+	Frozen           interface{}          `json:"frozen"`
 	//DataVerification struct {
 	//} `json:"dataVerification"`
 	//Hyperlink struct {

+ 17 - 0
services/data/excel/excel_op.go

@@ -222,6 +222,23 @@ func Copy(oldExcelInfo *excelModel.ExcelInfo, excelClassifyId int, excelName str
 			errMsg = "保存失败"
 		}
 
+		// 混合表格-条件格式配置
+		excelRules, e := excelModel.GetExcelRuleMappingsByExcelInfoId(oldExcelInfo.ExcelInfoId)
+		if e != nil {
+			err = fmt.Errorf("获取条件格式配置失败, %v", e)
+			return
+		}
+		if len(excelRules) > 0 {
+			for _, v := range excelRules {
+				v.ExcelInfoRuleMappingId = 0
+				v.ExcelInfoId = excelInfo.ExcelInfoId
+			}
+			mappingOb := new(excelModel.ExcelInfoRuleMapping)
+			if e = mappingOb.CreateMulti(excelRules); e != nil {
+				err = fmt.Errorf("复制条件格式配置失败, %v", e)
+				return
+			}
+		}
 		return
 	}
 

+ 75 - 40
services/data/future_good/profit_chart_info.go

@@ -70,7 +70,7 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, edbInfoList []*data
 
 	latestDate := zlFutureGoodEdbInfoList[0].EndDate
 	latestDateTime, _ := time.ParseInLocation(utils.FormatDate, latestDate, time.Local)
-
+	findDateTimeList := make([]time.Time, 0)
 	earliestDateTime := latestDateTime // 数据的最早日期,目的是为了找出最早的合约
 	for _, barChartInfoDate := range chartInfoDateList {
 		var findDateTime time.Time
@@ -96,14 +96,19 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, edbInfoList []*data
 			return
 		}
 		if findDateTime.Before(earliestDateTime) {
-			earliestDateTime = findDateTime
+		 	earliestDateTime = findDateTime
 		}
+
+		findDateTimeList = append(findDateTimeList, findDateTime)
 	}
 
-	monthNum := (latestDateTime.Year()-earliestDateTime.Year())*12 + int(latestDateTime.Month()-earliestDateTime.Month())
+	monthNumMap := make(map[string]int)
+	for _, findDateTimeTmp := range findDateTimeList {
+		monthNumMap[findDateTimeTmp.Format(utils.FormatDate)] = (latestDateTime.Year()-findDateTimeTmp.Year())*12 + int(latestDateTime.Month()-findDateTimeTmp.Month())
+	}
 
 	// 存储主力合约下的所有月份合约
-	futureGoodEdbInfoDateMap := make(map[int]map[string]*future_good.FutureGoodEdbInfo)
+	futureGoodEdbInfoDateAllMap := make(map[int]map[string]map[string]*future_good.FutureGoodEdbInfo)
 	futureGoodDataListMap := make(map[int][]*data_manage.EdbDataList, 0)
 	// 特殊的商品期货合约(只有M+N的合约,没有固定日期的合约)
 	specialFutureGoodEdbInfoMap := make(map[int]map[int]*future_good.FutureGoodEdbInfo, 0)
@@ -116,7 +121,7 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, edbInfoList []*data
 		}
 	}
 
-	nMap := make(map[string]string)
+	nAllMap := make(map[string][]string)
 	var maxN int // 最大N值
 	for _, v := range zlFutureGoodEdbInfoList {
 		// 如果不是有效的商品期货指标,那么就过滤掉,不做数据查询处理,避免没必要的请求
@@ -130,53 +135,58 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, edbInfoList []*data
 			return
 		}
 
-		childFutureGoodEdbInfoMap, tmpMaxN, tmpErr := getProfitFutureGoodEdbInfoList(earliestDateTime, v, tmpFutureGoodEdbInfoList, isAllChina, monthNum)
+		childFutureGoodEdbInfoAllMap, tmpMaxN, tmpErr := GetProfitFutureGoodEdbInfoList(findDateTimeList, v, tmpFutureGoodEdbInfoList, isAllChina, monthNumMap)
 		if tmpErr != nil {
 			err = tmpErr
 			return
 		}
+	
 		if maxN < tmpMaxN {
 			maxN = tmpMaxN
 		}
-		futureGoodEdbInfoDateMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoMap
+		
+		futureGoodEdbInfoDateAllMap[v.FutureGoodEdbInfoId] = childFutureGoodEdbInfoAllMap
 
 		if v.FutureGoodEdbType == 2 {
 			specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId] = make(map[int]*future_good.FutureGoodEdbInfo)
 		}
 		// 获取数据
-		for date, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap {
-			nMap[date] = date
-			dataList := make([]*data_manage.EdbDataList, 0)
-
-			tmpDataList, tmpErr := future_good.GetFutureGoodEdbDataListByDate(childFutureGoodEdbInfo.FutureGoodEdbInfoId, "", "")
-			if tmpErr != nil {
-				return
-			}
-			for _, tmpData := range tmpDataList {
-				dataList = append(dataList, &data_manage.EdbDataList{
-					EdbDataId:     tmpData.FutureGoodEdbDataId,
-					EdbInfoId:     tmpData.FutureGoodEdbInfoId,
-					DataTime:      tmpData.DataTime.Format(utils.FormatDate),
-					DataTimestamp: tmpData.DataTimestamp,
-					Value:         tmpData.Close,
-				})
-			}
-			futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId] = dataList
-
-			if childFutureGoodEdbInfo.FutureGoodEdbType == 2 {
-				specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId][childFutureGoodEdbInfo.Month] = childFutureGoodEdbInfo
+		for findDateTime, childFutureGoodEdbInfoMap := range childFutureGoodEdbInfoAllMap {
+			for date, childFutureGoodEdbInfo := range childFutureGoodEdbInfoMap {
+				nAllMap[findDateTime] = append(nAllMap[findDateTime], date)
+				dataList := make([]*data_manage.EdbDataList, 0)
+				if _, ok := futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId]; !ok {
+					tmpDataList, tmpErr := future_good.GetFutureGoodEdbDataListByDate(childFutureGoodEdbInfo.FutureGoodEdbInfoId, "", "")
+					if tmpErr != nil {
+						return
+					}
+					for _, tmpData := range tmpDataList {
+						dataList = append(dataList, &data_manage.EdbDataList{
+							EdbDataId:     tmpData.FutureGoodEdbDataId,
+							EdbInfoId:     tmpData.FutureGoodEdbInfoId,
+							DataTime:      tmpData.DataTime.Format(utils.FormatDate),
+							DataTimestamp: tmpData.DataTimestamp,
+							Value:         tmpData.Close,
+						})
+					}
+					futureGoodDataListMap[childFutureGoodEdbInfo.FutureGoodEdbInfoId] = dataList
+				}
+				if childFutureGoodEdbInfo.FutureGoodEdbType == 2 {
+					specialFutureGoodEdbInfoMap[v.FutureGoodEdbInfoId][childFutureGoodEdbInfo.Month] = childFutureGoodEdbInfo
+				}
 			}
 		}
 	}
 
 	// 找出所有的N值,并进行正序排列
-	dateList := make([]string, 0)
-	for _, n := range nMap {
-		dateList = append(dateList, n)
+	dateListMap := make(map[string][]string, 0)
+	for key, dateList := range nAllMap {
+		sort.Slice(dateList, func(i, j int) bool {
+			return dateList[i] < dateList[j]
+		})
+		dateListMap[key] = dateList
 	}
-	sort.Slice(dateList, func(i, j int) bool {
-		return dateList[i] < dateList[j]
-	})
+	
 	var reqEdbInfoIds []int
 	for _, v := range edbInfoList {
 		reqEdbInfoIds = append(reqEdbInfoIds, v.EdbInfoId)
@@ -188,7 +198,7 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, edbInfoList []*data
 	}
 	var edbIdList []int
 	futureGoodNameMap := make(map[int]map[int]string)
-	edbIdList, yDataList, futureGoodNameMap, err = ProfitChartChartData(baseEdbInfo, baseDataListMap, futureGoodEdbInfoDateMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateList, maxN, reqEdbInfoIds)
+	edbIdList, yDataList, futureGoodNameMap, err = ProfitChartChartData(baseEdbInfo, baseDataListMap, futureGoodEdbInfoDateAllMap, futureGoodDataListMap, chartInfoDateList, baseEdbInfo.EndDate, specialFutureGoodEdbInfoMap, formulaStr, tagEdbIdMap, dateListMap, maxN, reqEdbInfoIds)
 
 	// todo 最后处理数据
 	tmpXDataList, newYDataList, err := handleProfitResultData(xDataList, futureGoodNameMap, yDataList, earliestDateTime, edbIdList)
@@ -207,7 +217,7 @@ func GetProfitChartEdbData(baseEdbInfo *data_manage.EdbInfo, edbInfoList []*data
 }
 
 // ProfitChartChartData 获取数据
-func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[int][]*data_manage.EdbDataList, futureGoodEdbInfoMap map[int]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*data_manage.EdbDataList, chartInfoDateList []request.ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateList []string, maxN int, reqEdbInfoIds []int) (edbIdList []int, yDataList []data_manage.YData, futureGoodNameMap map[int]map[int]string, err error) {
+func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[int][]*data_manage.EdbDataList, futureGoodEdbInfoAllMap map[int]map[string]map[string]*future_good.FutureGoodEdbInfo, futureGoodEdbDataListMap map[int][]*data_manage.EdbDataList, chartInfoDateList []request.ChartInfoDateReq, latestDate string, specialFutureGoodEdbInfoMap map[int]map[int]*future_good.FutureGoodEdbInfo, formulaStr string, tagEdbIdMap map[string]int, dateListMap map[string][]string, maxN int, reqEdbInfoIds []int) (edbIdList []int, yDataList []data_manage.YData, futureGoodNameMap map[int]map[int]string, err error) {
 	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
 	//earliestDateTime time.Time
 	// ETA指标数据
@@ -286,6 +296,7 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 		if isFind {
 			maxDate = realDateTime
 		}
+		fmt.Println("findDateTime", findDateTime, "realDateTime", realDateTime, "findDataValue", findDataValue, "isFind", isFind)
 		edbIdList = make([]int, 0) //普通指标ID
 		for _, edbInfoId := range reqEdbInfoIds {
 			if edbInfoId == baseEdbInfo.EdbInfoId {
@@ -317,6 +328,7 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 		mList := make([]int, 0) // 间隔月份
 
 		tmpNameMap := make(map[int]string)
+		dateList := dateListMap[findDateTime.Format(utils.FormatDate)]
 		// 最小开始的n值
 		//minN := (findDateTime.Year()-earliestDateTime.Year())*12 + int(findDateTime.Month()-earliestDateTime.Month())
 		for _, date := range dateList {
@@ -330,17 +342,20 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 			// 获取当前日期相对开始日期的期数
 			tmpN := (currDate.Year()-realDateTime.Year())*12 + int(currDate.Month()-realDateTime.Month())
 			if tmpN <= 0 {
+				fmt.Println("realDateTime跳出循环",realDateTime,"tmpN", tmpN, "currDate", currDate, "realDateTime", realDateTime)
 				continue
 			}
 
 			// 如果期数大于最大期数,那么就退出当前匹配
 			if tmpN >= maxN {
+				fmt.Println("realDateTime跳出循环",realDateTime,"tmpN >= maxN",tmpN, "maxN", maxN)
 				break
 			}
 
 			zlAndChildEdbId := make(map[int]int)
 			childFutureGoodEdbInfoIdList := make([]int, 0)
-			for zlFutureGoodEdbInfoId, futureGoodEdbInfoList := range futureGoodEdbInfoMap {
+			for zlFutureGoodEdbInfoId, futureGoodEdbInfoListMap := range futureGoodEdbInfoAllMap {
+				futureGoodEdbInfoList := futureGoodEdbInfoListMap[findDateTime.Format(utils.FormatDate)]
 				// 判断是否特殊合约
 				if childFutureGoodEdbInfoIdMap, ok := specialFutureGoodEdbInfoMap[zlFutureGoodEdbInfoId]; ok {
 					if childFutureGoodEdbInfo, ok2 := childFutureGoodEdbInfoIdMap[tmpN]; ok2 {
@@ -363,10 +378,14 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 
 			calculateMap := make(map[int]float64)
 			for _, childFutureGoodEdbInfoId := range childFutureGoodEdbInfoIdList {
+				fmt.Println("查找childFutureGoodEdbInfoId", childFutureGoodEdbInfoId,"日期为", realDateTime.Format(utils.FormatDate), "的值")
 				tmpFindDataValue, tmpIsFind := edbDataMap[childFutureGoodEdbInfoId][realDateTime.Format(utils.FormatDate)]
-
+				
 				if tmpIsFind && tmpFindDataValue != 0 {
 					calculateMap[childFutureGoodEdbInfoId] = tmpFindDataValue
+					fmt.Println("已找到tmpFindDataValue", tmpFindDataValue, "tmpIsFind", tmpIsFind)
+				} else {
+					fmt.Println("未找到tmpFindDataValue", tmpFindDataValue, "tmpIsFind", tmpIsFind)
 				}
 			}
 			// 合约的数据不全,不参与计算
@@ -378,8 +397,8 @@ func ProfitChartChartData(baseEdbInfo *data_manage.EdbInfo, baseDataListMap map[
 
 			for tag, zlEdbId := range tagEdbIdMap {
 				if tag == "A" {
-					nameTmp := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbName, "(")
-					nameTmpEn := strings.Split(futureGoodEdbInfoMap[zlEdbId][date].FutureGoodEdbNameEn, "(")
+					nameTmp := strings.Split(futureGoodEdbInfoAllMap[zlEdbId][findDateTime.Format(utils.FormatDate)][date].FutureGoodEdbName, "(")
+					nameTmpEn := strings.Split(futureGoodEdbInfoAllMap[zlEdbId][findDateTime.Format(utils.FormatDate)][date].FutureGoodEdbNameEn, "(")
 					if len(nameTmp) > 1 && len(nameTmpEn) > 1 {
 						nameTmp[1] = strings.Trim(nameTmp[1], ")")
 						nameTmpEn[1] = strings.Trim(nameTmpEn[1], ")")
@@ -563,6 +582,22 @@ func getProfitFutureGoodEdbInfoList(earliestDateTime time.Time, zlFutureGoodEdbI
 	return
 }
 
+func GetProfitFutureGoodEdbInfoList(earliestDateTimeList []time.Time, zlFutureGoodEdbInfo *future_good.FutureGoodEdbInfo, tmpFutureGoodEdbInfoList []*future_good.FutureGoodEdbInfo, isAllChina bool, monthNumMap  map[string]int) (futureGoodEdbInfoDateAllMap map[string]map[string]*future_good.FutureGoodEdbInfo, newMaxN int, err error) {
+	futureGoodEdbInfoDateAllMap = make(map[string]map[string]*future_good.FutureGoodEdbInfo)
+	for _, earliestDateTime := range earliestDateTimeList {
+		futureGoodEdbInfoDateMap := make(map[string]*future_good.FutureGoodEdbInfo)
+		var newMaxNTmp int
+		futureGoodEdbInfoDateMap, newMaxNTmp, err = getProfitFutureGoodEdbInfoList(earliestDateTime, zlFutureGoodEdbInfo, tmpFutureGoodEdbInfoList, isAllChina, monthNumMap[earliestDateTime.Format(utils.FormatYearMonthDate)])
+		if err != nil {
+			return
+		}
+		if newMaxN < newMaxNTmp {
+			newMaxN = newMaxNTmp
+		}
+		futureGoodEdbInfoDateAllMap[earliestDateTime.Format(utils.FormatDate)] = futureGoodEdbInfoDateMap
+	}
+	return
+}
 // handleProfitResultData 处理成最终的结果数据
 func handleProfitResultData(xDataListInit []data_manage.XData, futureNameMap map[int]map[int]string, yDataList []data_manage.YData, earliestDateTime time.Time, allEdbInfoIds []int) (xDataList []data_manage.XData, newYDataList []data_manage.YData, err error) {
 	newYDataList = yDataList

+ 1 - 0
services/elastic.go

@@ -102,6 +102,7 @@ func EsAddOrEditReport(indexName, docId string, item *models.ElasticReportDetail
 			"BodyContent":        item.BodyContent,
 			"PublishTime":        item.PublishTime,
 			"PublishState":       item.PublishState,
+			"IsPublicPublish":    item.IsPublicPublish,
 			"Author":             item.Author,
 			"ClassifyIdFirst":    item.ClassifyIdFirst,
 			"ClassifyNameFirst":  item.ClassifyNameFirst,

+ 288 - 0
services/elastic/rag_question.go

@@ -0,0 +1,288 @@
+package elastic
+
+import (
+	"context"
+	"encoding/json"
+	"eta/eta_api/models/rag"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/olivere/elastic/v7"
+	"time"
+)
+
+// 问题索引
+var EsRagQuestionName = utils.EsRagQuestionName
+
+type RagQuestionItem struct {
+	QuestionId      int       `gorm:"column:question_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"question_id"`
+	QuestionTitle   string    `gorm:"column:question_title;type:varchar(255);comment:问题标题;" description:"问题标题"`
+	QuestionContent string    `gorm:"column:question_content;type:varchar(255);comment:问题内容;" description:"问题内容"`
+	Sort            int       `gorm:"column:sort;type:int(11);comment:排序;default:0;" description:"排序"`
+	SysUserId       int       `gorm:"column:sys_user_id;type:int(11);comment:添加人id;default:0;" description:"添加人id"`
+	SysUserRealName string    `gorm:"column:sys_user_real_name;type:varchar(255);comment:添加人真实名称;" description:"添加人真实名称"`
+	ModifyTime      time.Time `gorm:"column:modify_time;type:datetime;default:NULL;" description:"modify_time"`
+	CreateTime      time.Time `gorm:"column:create_time;type:datetime;default:NULL;" description:"create_time"`
+}
+
+func (m *RagQuestionItem) ToView() rag.QuestionView {
+	var modifyTime, createTime string
+
+	if !m.CreateTime.IsZero() {
+		createTime = m.CreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.ModifyTime.IsZero() {
+		modifyTime = m.ModifyTime.Format(utils.FormatDateTime)
+	}
+
+	return rag.QuestionView{
+		QuestionId:      m.QuestionId,
+		QuestionTitle:   m.QuestionTitle,
+		QuestionContent: m.QuestionContent,
+		Sort:            m.Sort,
+		SysUserId:       m.SysUserId,
+		SysUserRealName: m.SysUserRealName,
+		ModifyTime:      modifyTime,
+		CreateTime:      createTime,
+	}
+}
+
+func (m *RagQuestionItem) ToViewList(list []*RagQuestionItem) (wechatArticleViewList []rag.QuestionView) {
+	wechatArticleViewList = make([]rag.QuestionView, 0)
+
+	for _, v := range list {
+		wechatArticleViewList = append(wechatArticleViewList, v.ToView())
+	}
+	return
+}
+
+// RagQuestionEsAddOrEdit
+// @Description: 新增/编辑微信文章
+// @author: Roc
+// @datetime 2025-03-13 10:24:05
+// @param docId string
+// @param item WechatArticleAndPlatform
+// @return err error
+func RagQuestionEsAddOrEdit(docId string, item RagQuestionItem) (err error) {
+	if docId == "" {
+		return
+	}
+	if EsRagQuestionName == `` {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("WechatArticleEsAddOrEdit Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Index().Index(EsRagQuestionName).Id(docId).BodyJson(item).Refresh("true").Do(context.Background())
+	if err != nil {
+		fmt.Println("新增失败:", err.Error())
+		return err
+	}
+	if resp.Status == 0 {
+		fmt.Println("新增成功", resp.Result)
+		err = nil
+	} else {
+		fmt.Println("WechatArticleEsAddOrEdit", resp.Status, resp.Result)
+	}
+
+	return
+}
+
+// WechatArticleEsDel
+// @Description: 删除微信文章
+// @author: Roc
+// @datetime 2025-03-13 10:23:55
+// @param docId string
+// @return err error
+func RagQuestionEsDel(docId string) (err error) {
+	if docId == "" {
+		return
+	}
+	if EsRagQuestionName == `` {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EsDeleteEdbInfoData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Delete().Index(EsRagQuestionName).Id(docId).Refresh(`true`).Do(context.Background())
+	if err != nil {
+		return
+	}
+	if resp.Status == 0 {
+		fmt.Println("删除成功")
+	} else {
+		fmt.Println("WechatArticleEsDel", resp.Status, resp.Result)
+	}
+
+	return
+}
+
+// RagQuestionEsSearch
+// @Description: 搜索
+// @author: Roc
+// @datetime 2025-03-13 19:54:54
+// @param keywordStr string
+// @param tagIdList []int
+// @param platformIdList []int
+// @param from int
+// @param size int
+// @param sortMap map[string]string
+// @return total int64
+// @return list []*RagQuestionItem
+// @return err error
+func RagQuestionEsSearch(keywordStr string, from, size int, sortMap map[string]string) (total int64, list []*RagQuestionItem, err error) {
+	indexName := EsRagQuestionName
+	list = make([]*RagQuestionItem, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("SearchEdbInfoData Err:", err.Error())
+		}
+	}()
+
+	query := elastic.NewBoolQuery()
+
+	// 名字匹配
+	if keywordStr != `` {
+		query = query.Must(elastic.NewMultiMatchQuery(keywordStr, "QuestionContent"))
+	}
+
+	// 排序
+	sortList := make([]*elastic.FieldSort, 0)
+	// 如果没有关键字,那么就走指标id倒序
+
+	for orderKey, orderType := range sortMap {
+		switch orderType {
+		case "asc":
+			sortList = append(sortList, elastic.NewFieldSort(orderKey).Asc())
+		case "desc":
+			sortList = append(sortList, elastic.NewFieldSort(orderKey).Desc())
+
+		}
+
+	}
+
+	return searchRagQuestion(indexName, query, sortList, from, size)
+}
+
+// searchRagQuestion 查询es中的数据
+func searchRagQuestion(indexName string, query elastic.Query, sortList []*elastic.FieldSort, from, size int) (total int64, list []*RagQuestionItem, err error) {
+	total, err = searchRagQuestionTotal(indexName, query)
+	if err != nil {
+		return
+	}
+
+	// 获取列表数据
+	list, err = searchRagQuestionList(indexName, query, sortList, from, size)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// searchEdbInfoDataTotal
+// @Description: 查询es中的数量
+// @author: Roc
+// @datetime 2024-12-23 11:19:04
+// @param indexName string
+// @param query elastic.Query
+// @return total int64
+// @return err error
+func searchRagQuestionTotal(indexName string, query elastic.Query) (total int64, err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("searchEdbInfoDataTotal Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	//根据条件数量统计
+	requestTotalHits := client.Count(indexName).Query(query)
+	total, err = requestTotalHits.Do(context.Background())
+	if err != nil {
+		return
+	}
+
+	return
+}
+
+// searchEdbInfoDataList
+// @Description: 查询es中的明细数据
+// @author: Roc
+// @datetime 2024-12-23 11:18:48
+// @param indexName string
+// @param query elastic.Query
+// @param sortList []*elastic.FieldSort
+// @param from int
+// @param size int
+// @return list []*data_manage.EdbInfoList
+// @return err error
+func searchRagQuestionList(indexName string, query elastic.Query, sortList []*elastic.FieldSort, from, size int) (list []*RagQuestionItem, err error) {
+	list = make([]*RagQuestionItem, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("searchEdbInfoDataList Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+	// 高亮
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("Content"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+
+	//request := client.Search(indexName).Highlight(highlight).From(from).Size(size) // sets the JSON request
+	request := client.Search(indexName).From(from).Size(size) // sets the JSON request
+
+	// 如果有指定排序,那么就按照排序来
+	if len(sortList) > 0 {
+		for _, v := range sortList {
+			request = request.SortBy(v)
+		}
+	}
+
+	searchMap := make(map[string]string)
+
+	searchResp, err := request.Query(query).Do(context.Background())
+	if err != nil {
+		return
+	}
+	//fmt.Println(searchResp)
+	//fmt.Println(searchResp.Status)
+	if searchResp.Status != 0 {
+		return
+	}
+	//total = searchResp.TotalHits()
+	if searchResp.Hits != nil {
+		for _, v := range searchResp.Hits.Hits {
+			if _, ok := searchMap[v.Id]; !ok {
+				itemJson, tmpErr := v.Source.MarshalJSON()
+				if tmpErr != nil {
+					err = tmpErr
+					fmt.Println("movieJson err:", err)
+					return
+				}
+				item := new(RagQuestionItem)
+				tmpErr = json.Unmarshal(itemJson, &item)
+				if tmpErr != nil {
+					fmt.Println("json.Unmarshal movieJson err:", tmpErr)
+					err = tmpErr
+					return
+				}
+				if len(v.Highlight["QuestionContent"]) > 0 {
+					item.QuestionContent = v.Highlight["QuestionContent"][0]
+				}
+				list = append(list, item)
+				searchMap[v.Id] = v.Id
+			}
+		}
+	}
+
+	return
+}

+ 80 - 0
services/eta_forum/chart_classify.go

@@ -0,0 +1,80 @@
+package eta_forum
+
+import (
+	"encoding/json"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/utils"
+	"fmt"
+)
+
+// ChartClassifySave 上传图表分类信息
+func ChartClassifySave(chartClassifyId int) (err error) {
+	if utils.BusinessCode == "" || (utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeDebug && utils.BusinessCode != utils.BusinessCodeSandbox) {
+		return
+	}
+	//查询分类信息
+	chartClassifyInfo, err := data_manage.GetChartClassifyById(chartClassifyId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			err = fmt.Errorf("分类不存在")
+			return
+		}
+		err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	if chartClassifyInfo.IsSelected != utils.ChartClassifyIsSelected {
+		return
+	}
+
+	reqJson, err := json.Marshal(chartClassifyInfo) 
+	if err != nil {
+		err = fmt.Errorf("参数解析异常,Err:" + err.Error())
+		return
+	}
+	respItem, err := ChartClassifySaveLib(string(reqJson))
+	if err != nil {
+		err = fmt.Errorf("上传图表分类信息失败,Err:" + err.Error())
+		return
+	}
+	if respItem.Ret != 200 {
+		err = fmt.Errorf("上传图表分类信息失败,Err:%v,errMsg:%v", respItem.Msg, respItem.ErrMsg)
+		return
+	}
+	return
+}
+
+type ChartClassifySaveBatchReq struct {
+	List []*data_manage.ChartClassify
+}
+
+//批量上传图表分类信息
+func ChartClassifySaveBatch(source int) (err error) {
+	if utils.BusinessCode == "" || (utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeDebug && utils.BusinessCode != utils.BusinessCodeSandbox) {
+		return
+	}
+
+	//查询分类信息
+	chartClassifyList, err := data_manage.GetChartClassifyInfoSelectedBySource(source)
+	if err != nil {
+		err = fmt.Errorf("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	req := ChartClassifySaveBatchReq{
+		List: chartClassifyList,
+	}
+	reqJson, err := json.Marshal(req)
+	if err != nil {
+		err = fmt.Errorf("参数解析异常,Err:" + err.Error())
+		return
+	}
+	respItem, err := ChartClassifySaveBatchLib(string(reqJson))
+	if err != nil {
+		err = fmt.Errorf("上传图表分类信息失败,Err:" + err.Error())
+		return
+	}	
+	if respItem.Ret != 200 {
+		err = fmt.Errorf("上传图表分类信息失败,Err:%v,errMsg:%v", respItem.Msg, respItem.ErrMsg)
+		return
+	}
+	return
+}

+ 531 - 27
services/eta_forum/eta_forum_hub.go

@@ -8,6 +8,8 @@ import (
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
+	"strconv"
+	"time"
 )
 
 type UploadChartToForumReq struct {
@@ -21,7 +23,7 @@ type UpdateChartToForumReq struct {
 
 type ChartSaveLibReq struct {
 	ChartInfo               *data_manage.ChartInfo
-	Description             string `description:"逻辑简述"`
+	Description             []*data_manage.ChartDescriptionReq `description:"逻辑简述"`
 	EdbInfoList             []*data_manage.EdbInfo
 	ChartThemeList          []*chart_theme.ChartTheme
 	EdbInfoDataList         []*AddEdbDataReq
@@ -45,6 +47,7 @@ type ChartSaveLibResp struct {
 }
 
 type AddEdbDataReq struct {
+	EdbInfoId int
 	EdbCode  string
 	EdbType  int
 	DataList []*data_manage.EdbDataBase
@@ -56,10 +59,18 @@ type DeleteChartReq struct {
 
 // UploadChart 上传图表接口
 func UploadChart(chartInfoId int, description string, uploaderInfo *system.Admin) (err error, errMsg string) {
+	// 设置缓存 防止重复上传
+	cacheKey := "eta_forum_task:UploadChart:" + strconv.Itoa(chartInfoId)
+	if utils.Rc.Get(cacheKey) != nil {
+		err = fmt.Errorf("系统处理中,请稍后重试!")
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 10*time.Minute)
 	defer func() {
 		if err != nil {
 			go alarm_msg.SendAlarmMsg(fmt.Sprintf("上传图表至社区失败:Err:%v,ErrMsg:%s", err, errMsg), 3)
 		}
+		utils.Rc.Delete(cacheKey)
 	}()
 	// 查询图表信息
 	chartInfo, err := data_manage.GetChartInfoById(chartInfoId)
@@ -82,15 +93,17 @@ func UploadChart(chartInfoId int, description string, uploaderInfo *system.Admin
 
 	//查询图表指标
 	//获取原图表关联的指标信息列表
-	chartMappingList, err := data_manage.GetChartMappingList(chartInfoId)
+	chartMappingList, err := data_manage.GetChartMappingListOrderByEdbInfoId(chartInfoId)
 	if err != nil {
 		errMsg = "获取图表关联的指标信息失败"
 		err = fmt.Errorf("获取图表关联的指标信息失败,Err:" + err.Error())
 		return
 	}
 	edbIds := make([]int, 0)
+	edbInfoStr := ""
 	for _, v := range chartMappingList {
 		edbIds = append(edbIds, v.EdbInfoId)
+		edbInfoStr += strconv.Itoa(v.EdbInfoId) + ","
 	}
 	chartSeriesList := make([]*data_manage.ChartSeries, 0)
 	chartSeriesEdbList := make([]*data_manage.ChartSeriesEdbMapping, 0)
@@ -116,20 +129,27 @@ func UploadChart(chartInfoId int, description string, uploaderInfo *system.Admin
 		edbInfoDataList []*AddEdbDataReq
 	)
 	//查询指标详情
-	edbInfoList, edbMappingList, edbInfoDataList, err = GetEdbListByEdbInfoId(edbIds)
+	edbInfoList, edbMappingList, edbInfoDataList, err = GetEdbListByEdbInfoId(edbIds, true)
 	if err != nil {
 		errMsg = "获取指标详情失败"
 		err = fmt.Errorf("获取指标详情失败,Err:" + err.Error())
 		return
 	}
 
+	descriptionList, err, errMsg := getChartDescriptionWithAdminNameByChartInfoId(chartInfoId)
+	if err != nil {
+		errMsg = "获取图表简介失败"
+		err = fmt.Errorf("获取图表简介失败,Err:" + err.Error())
+		return
+	}
+
 	req := new(ChartSaveLibReq)
 	req.ChartInfo = chartInfo
 	req.ChartEdbMapping = chartMappingList
 	req.EdbInfoList = edbInfoList
 	req.EdbInfoDataList = edbInfoDataList
 	req.EdbInfoCalculateMapping = edbMappingList
-	req.Description = description
+	req.Description = descriptionList
 	req.ChartSeries = chartSeriesList
 	req.ChartSeriesEdbMapping = chartSeriesEdbList
 
@@ -161,12 +181,19 @@ func UploadChart(chartInfoId int, description string, uploaderInfo *system.Admin
 
 	if respItem.Data != nil && respItem.Data.ChartInfoId != 0 {
 		// 更新社区返回的图表ID
-		err = data_manage.SetForumChartInfoId(chartInfoId, respItem.Data.ChartInfoId)
+		err = data_manage.SetForumChartInfoId(chartInfoId, respItem.Data.ChartInfoId, utils.ChartClassifyResourceStatusUp)
 		if err != nil {
 			errMsg = "更新图表ID失败"
 			err = fmt.Errorf("更新图表ID失败,Err:" + err.Error())
 			return
 		}
+		// 新增投研资源库里的图表和指标绑定关系
+		err = data_manage.AddForumChartEdbMapping(chartInfoId, edbInfoStr)
+		if err != nil {
+			errMsg = "新增投研资源库里的图表和指标绑定关系失败"
+			err = fmt.Errorf("新增投研资源库里的图表和指标绑定关系失败,Err:" + err.Error())
+			return
+		}
 	}
 
 	return
@@ -174,10 +201,18 @@ func UploadChart(chartInfoId int, description string, uploaderInfo *system.Admin
 
 // UpdateChart 更新社区里的图表接口
 func UpdateChart(chartInfoId int) (err error, errMsg string) {
+	// 设置缓存 防止重复更新
+	cacheKey := "eta_forum_task:UpdateChart:" + strconv.Itoa(chartInfoId)
+	if utils.Rc.Get(cacheKey) != nil {
+		err = fmt.Errorf("系统处理中,请稍后重试!")
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 10*time.Minute)
 	defer func() {
 		if err != nil {
 			go alarm_msg.SendAlarmMsg(fmt.Sprintf("同步图表信息至社区失败:Err:%v,ErrMsg:%s", err, errMsg), 3)
 		}
+		utils.Rc.Delete(cacheKey)
 	}()
 	// 查询图表信息
 	chartInfo, err := data_manage.GetChartInfoById(chartInfoId)
@@ -201,15 +236,17 @@ func UpdateChart(chartInfoId int) (err error, errMsg string) {
 
 	//查询图表指标
 	//获取原图表关联的指标信息列表
-	chartMappingList, err := data_manage.GetChartMappingList(chartInfoId)
+	chartMappingList, err := data_manage.GetChartMappingListOrderByEdbInfoId(chartInfoId)
 	if err != nil {
 		errMsg = "获取图表关联的指标信息失败"
 		err = fmt.Errorf("获取图表关联的指标信息失败,Err:" + err.Error())
 		return
 	}
 	edbIds := make([]int, 0)
+	edbInfoStr := ""
 	for _, v := range chartMappingList {
 		edbIds = append(edbIds, v.EdbInfoId)
+		edbInfoStr += strconv.Itoa(v.EdbInfoId) + ","
 	}
 	chartSeriesList := make([]*data_manage.ChartSeries, 0)
 	chartSeriesEdbList := make([]*data_manage.ChartSeriesEdbMapping, 0)
@@ -234,12 +271,30 @@ func UpdateChart(chartInfoId int) (err error, errMsg string) {
 		edbInfoDataList []*AddEdbDataReq
 	)
 	//查询指标详情
-	edbInfoList, edbMappingList, edbInfoDataList, err = GetEdbListByEdbInfoId(edbIds)
+	isGetEdbData := false
+	// 查询投研资源库里的图表和指标绑定关系
+	oldEdbInfoStr, err := data_manage.GetForumEdbInfoIdsByChartInfoId(chartInfoId)
+	if err != nil {
+		errMsg = "获取投研资源库里的图表和指标绑定关系失败"
+		err = fmt.Errorf("获取投研资源库里的图表和指标绑定关系失败,Err:" + err.Error())
+		return
+	}
+	if oldEdbInfoStr != edbInfoStr { // 图表更换过指标需要重新获取指标数据
+		isGetEdbData = true
+	}
+	//查询指标详情
+	edbInfoList, edbMappingList, edbInfoDataList, err = GetEdbListByEdbInfoId(edbIds, isGetEdbData)
 	if err != nil {
 		errMsg = "获取指标详情失败"
 		err = fmt.Errorf("获取指标详情失败,Err:" + err.Error())
 		return
 	}
+	descriptionList, err, errMsg := getChartDescriptionWithAdminNameByChartInfoId(chartInfoId)
+	if err != nil {
+		errMsg = "获取图表简介失败"
+		err = fmt.Errorf("获取图表简介失败,Err:" + err.Error())
+		return
+	}
 	req := new(ChartSaveLibReq)
 	req.ChartInfo = chartInfo
 	req.ChartInfo.ChartInfoId = chartInfo.ForumChartInfoId
@@ -249,6 +304,7 @@ func UpdateChart(chartInfoId int) (err error, errMsg string) {
 	req.EdbInfoCalculateMapping = edbMappingList
 	req.ChartSeries = chartSeriesList
 	req.ChartSeriesEdbMapping = chartSeriesEdbList
+	req.Description = descriptionList
 	// 查询创建者信息
 	creatorInfo, _ := system.GetSysAdminById(chartInfo.SysUserId)
 	if creatorInfo != nil {
@@ -274,20 +330,29 @@ func UpdateChart(chartInfoId int) (err error, errMsg string) {
 		return
 	}
 
+	// 更新投研资源库里的图表和指标绑定关系
+	err = data_manage.UpdateForumChartEdbMapping(chartInfoId, edbInfoStr)
+	if err != nil {
+		errMsg = "更新投研资源库里的图表和指标绑定关系失败"
+		err = fmt.Errorf("更新投研资源库里的图表和指标绑定关系失败,Err:" + err.Error())
+		return
+	}
 	return
 }
 
-func GetEdbListByEdbInfoId(edbInfoIds []int) (edbInfoList []*data_manage.EdbInfo, edbMappingList []*data_manage.EdbInfoCalculateMapping, edbInfoDataList []*AddEdbDataReq, err error) {
+func GetEdbListByEdbInfoId(edbInfoIds []int, isGetEdbData bool) (edbInfoList []*data_manage.EdbInfo, edbMappingList []*data_manage.EdbInfoCalculateMapping, edbInfoDataList []*AddEdbDataReq, err error) {
 	//查询指标信息
 	//查询指标映射
 	//查询所有指标数据
 	//查询这个指标相关的mapping信息放到数组里,
 	//将得到的指标ID信息放到数组里
+	chartEdbInfoIdMap := make(map[int]struct{}) //只查询图表上直接相关的指标数据
 	hasFindMap := make(map[int]struct{})
 	edbInfoIdMap := make(map[int]struct{})
 	edbMappingList = make([]*data_manage.EdbInfoCalculateMapping, 0)
 	edbMappingMap := make(map[int]struct{})
 	for _, edbInfoId := range edbInfoIds {
+		chartEdbInfoIdMap[edbInfoId] = struct{}{}
 		edbMappingList, err = traceEdbInfoByEdbInfoId(edbInfoId, hasFindMap, edbInfoIdMap, edbMappingList, edbMappingMap)
 		if err != nil {
 			err = fmt.Errorf(" traceEdbInfoByEdbInfoId err: %s", err.Error())
@@ -305,27 +370,32 @@ func GetEdbListByEdbInfoId(edbInfoIds []int) (edbInfoList []*data_manage.EdbInfo
 		err = fmt.Errorf(" GetEdbInfoByIdList err: %s", err.Error())
 		return
 	}
+	if isGetEdbData {
+		for _, v := range edbInfoList {
+			if _, ok := chartEdbInfoIdMap[v.EdbInfoId]; !ok {
+				continue
+			}
+			var dataList []*data_manage.EdbDataBase
+			if v.Source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
+				dataList, err = data_manage.GetEdbDataBaseMongoByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
+			} else if v.Source == utils.DATA_SOURCE_THS && v.SubSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
+				dataList, err = data_manage.GetThsHfEdbDataBaseMongoByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
+			} else {
+				dataList, err = data_manage.GetEdbDataBaseByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
+			}
 
-	for _, v := range edbInfoList {
-		var dataList []*data_manage.EdbDataBase
-		if v.Source == utils.DATA_SOURCE_BUSINESS && utils.UseMongo {
-			dataList, err = data_manage.GetEdbDataBaseMongoByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
-		} else if v.Source == utils.DATA_SOURCE_THS && v.SubSource == utils.DATA_SUB_SOURCE_HIGH_FREQUENCY && utils.UseMongo {
-			dataList, err = data_manage.GetThsHfEdbDataBaseMongoByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
-		} else {
-			dataList, err = data_manage.GetEdbDataBaseByEdbInfoId(v.EdbInfoId, v.Source, v.SubSource)
-		}
+			if err != nil {
+				err = fmt.Errorf("查询指标数据失败 Err: %s", err.Error())
+				return
+			}
 
-		if err != nil {
-			err = fmt.Errorf("查询指标数据失败 Err: %s", err.Error())
-			return
+			tmp := new(AddEdbDataReq)
+			tmp.EdbInfoId = v.EdbInfoId
+			tmp.EdbCode = v.EdbCode
+			tmp.EdbType = v.EdbType
+			tmp.DataList = dataList
+			edbInfoDataList = append(edbInfoDataList, tmp)
 		}
-
-		tmp := new(AddEdbDataReq)
-		tmp.EdbCode = v.EdbCode
-		tmp.EdbType = v.EdbType
-		tmp.DataList = dataList
-		edbInfoDataList = append(edbInfoDataList, tmp)
 	}
 	return
 }
@@ -397,6 +467,19 @@ func traceEdbInfoByEdbInfoId(edbInfoId int, hasFindMap map[int]struct{}, edbInfo
 
 // DeleteChart 上传图表接口
 func DeleteChart(chartInfoId int) (err error, errMsg string) {
+	// 设置缓存 防止重复撤回
+	cacheKey := "eta_forum_task:DeleteChart:" + strconv.Itoa(chartInfoId)
+	if utils.Rc.Get(cacheKey) != nil {
+		err = fmt.Errorf("系统处理中,请稍后重试!")
+		return
+	}
+	utils.Rc.SetNX(cacheKey, 1, 30*time.Second)
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg(fmt.Sprintf("撤回图表失败:%v,ErrMsg:%s", err, errMsg), 3)
+		}
+		utils.Rc.Delete(cacheKey)
+	}()
 	// 查询图表信息
 	chartInfo, err := data_manage.GetChartInfoById(chartInfoId)
 	if err != nil {
@@ -438,12 +521,46 @@ func DeleteChart(chartInfoId int) (err error, errMsg string) {
 	}
 
 	// 更新社区返回的图表ID
-	err = data_manage.SetForumChartInfoId(chartInfoId, 0)
+	err = data_manage.SetForumChartInfoId(chartInfoId, 0, utils.ChartClassifyResourceStatusDown)
 	if err != nil {
 		errMsg = "撤回失败"
 		err = fmt.Errorf("更新图表ID失败,Err:" + err.Error())
 		return
 	}
+	// 删除投研资源库里的图表和指标绑定关系
+	err = data_manage.DeleteForumChartEdbMapping(chartInfoId)
+	if err != nil {
+		errMsg = "删除投研资源库里的图表和指标绑定关系失败"
+		err = fmt.Errorf("删除投研资源库里的图表和指标绑定关系失败,Err:" + err.Error())
+		return
+	}
+	return
+}
+
+// DeleteChart 上传图表接口
+func DeleteChartByForumChartInfoId(forumChartInfoId int) (err error, errMsg string) {
+	// 查询图表信息
+	req := new(DeleteChartReq)
+	req.ChartInfoId = forumChartInfoId
+
+	// 添加计算指标
+	reqJson, err := json.Marshal(req)
+	if err != nil {
+		errMsg = "参数解析异常"
+		err = fmt.Errorf("参数解析异常,Err:" + err.Error())
+		return
+	}
+	respItem, err := ChartDeleteLib(string(reqJson))
+	if err != nil {
+		errMsg = "撤回失败"
+		err = fmt.Errorf("撤回失败,Err:" + err.Error())
+		return
+	}
+	if respItem.Ret != 200 {
+		errMsg = "撤回失败"
+		err = fmt.Errorf(respItem.ErrMsg)
+		return
+	}
 
 	return
 }
@@ -570,3 +687,390 @@ func ChartThemeTypeSave(theme *chart_theme.ChartThemeType) (err error) {
 	}
 	return
 }
+
+// ChartBatchUpdate 批量上传和更新图表信息
+func ChartBatchUpdateAndUpload(chartClassifyInfo *data_manage.ChartClassify, sysUser *system.Admin) (err error) {
+	if utils.BusinessCode == "" || (utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeDebug && utils.BusinessCode != utils.BusinessCodeSandbox) {
+		return
+	}
+	var tmpErr []error
+	deleteCache := true
+	cacheKey := "eta_forum_task:EtaForumChartUpdateByClassifyId:" + strconv.Itoa(chartClassifyInfo.ChartClassifyId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+		stack := ""
+		if err != nil {
+			stack = fmt.Sprintln(stack + err.Error())
+		}
+		if len(tmpErr) > 0 {
+			for _, v := range tmpErr {
+				stack = fmt.Sprintln(stack + v.Error())
+			}
+		}
+		if stack != "" {
+			utils.FileLog.Error("批量更新资源库图表信息失败"+"<br/>"+stack)
+			go alarm_msg.SendAlarmMsg("批量更新资源库图表信息失败"+"<br/>"+stack, 3)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		err = fmt.Errorf("系统处理中,请稍后重试!")
+		return
+	}
+	chartClassifyInfoList, err := data_manage.GetChartClassifyByLevelPath(chartClassifyInfo.LevelPath, chartClassifyInfo.Source)
+	if err != nil {
+		return
+	}
+	chartClassifyIdList := make([]string, 0)
+	upChartClassifyIdList := make([]string, 0)
+	for _, v := range chartClassifyInfoList {
+		chartClassifyIdList = append(chartClassifyIdList, strconv.Itoa(v.ChartClassifyId))
+		if v.ResourceStatus == utils.ChartClassifyResourceStatusUp {
+			upChartClassifyIdList = append(upChartClassifyIdList, strconv.Itoa(v.ChartClassifyId))
+		}
+	}
+	condition := " and forum_chart_info_id > 0 and source=1 and chart_classify_id in ?"
+	// 查询需要更新的图表信息总数
+	total, err := data_manage.GetChartInfoCountByCondition(condition, []interface{}{chartClassifyIdList})
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		// 更新图表数据
+		offset := 0
+		pageSize := 100
+		success := 0
+		
+		// 循环更新100个图表数据
+		for i := 0; offset < total; i++ {
+			// 查询需要更新的图表信息
+			chartInfos, e := data_manage.GetChartInfoListByCondition(condition, []interface{}{chartClassifyIdList}, offset, pageSize)
+			if e != nil {
+				err = fmt.Errorf("查询需要更新的图表信息失败: %v", e)
+				return
+			}
+			if len(chartInfos) == 0 {
+				break
+			}
+			// 循环更新图表数据
+			for _, chartInfo := range chartInfos {
+				// 更新图表数据
+				er, msg := UpdateChart(chartInfo.ChartInfoId)
+				if er != nil {
+					er = fmt.Errorf("图表ID %d, 更新图表数据失败: %s, %v", chartInfo.ChartInfoId, msg, er)
+					tmpErr = append(tmpErr, er)
+					continue
+				}
+				success += 1
+			}
+
+			offset += pageSize
+		}
+		utils.FileLog.Info("更新图表数据完成, 更新图表数据总数:", success)
+	}
+	// 更新指标数据
+	utils.Rc.LPush(utils.CACHE_KEY_EDB_DATA_UPDATE_LOG, []byte("1"))
+
+	// 批量上传已经上架的分类下的图表到资源库中
+	// 查询需要新增的图表ID
+	condition = " and source=1 and forum_chart_info_id=0 and resource_status !=2 and chart_classify_id in ?"
+	total, err = data_manage.GetChartInfoCountByCondition(condition, []interface{}{upChartClassifyIdList})
+	if err != nil {
+		return
+	}
+	if total == 0 {
+		return
+	}
+	offset := 0
+	pageSize := 100
+	success := 0
+	// 计算需要分多少页
+	pageNum := total / pageSize
+	if total % pageSize != 0 {
+		pageNum += 1
+	}
+	// 循环更新100个图表数据
+	for i := 0; i < pageNum; i++ {
+		chartInfos, e := data_manage.GetChartInfoListByCondition(condition, []interface{}{upChartClassifyIdList}, offset, pageSize)
+		if e != nil {
+			err = fmt.Errorf("查询需要新增的图表信息失败: %v", e)
+			return
+		}
+		if len(chartInfos) == 0 {
+			break
+		}
+		// 循环更新图表数据
+		for _, chartInfo := range chartInfos {
+			// 更新图表数据
+			er, msg := UploadChart(chartInfo.ChartInfoId, "", sysUser)
+			if er != nil {
+				er = fmt.Errorf("图表ID %d, 上传图表数据失败: %s, %v", chartInfo.ChartInfoId, msg, er)
+				tmpErr = append(tmpErr, er)
+				continue
+			}
+			success += 1
+		}
+	}
+	utils.FileLog.Info("批量上传已经上架的分类下的图表到资源库中完成, 新增图表数据总数:", success)
+	
+	return
+}
+
+// 批量删除资源库图表信息
+func ChartInfoDeleteBatch(chartClassifyInfo *data_manage.ChartClassify, sysUser *system.Admin) (err error) {
+	if utils.BusinessCode == "" || (utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeDebug && utils.BusinessCode != utils.BusinessCodeSandbox) {
+		return
+	}
+	var tmpErr []error
+	deleteCache := true
+	cacheKey := "eta_forum_task:ChartInfoDeleteBatch:" + strconv.Itoa(chartClassifyInfo.ChartClassifyId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+		stack := ""
+		if err != nil {
+			stack = fmt.Sprintln(stack + err.Error())
+		}
+		if len(tmpErr) > 0 {
+			for _, v := range tmpErr {
+				stack = fmt.Sprintln(stack + v.Error())
+			}
+		}
+		if stack != "" {
+			utils.FileLog.Error("批量删除资源库图表信息失败"+"<br/>"+stack)
+			go alarm_msg.SendAlarmMsg("批量删除资源库图表信息失败"+"<br/>"+stack, 3)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		err = fmt.Errorf("系统处理中,请稍后重试!")
+		return
+	}
+	// 查询所有子分类
+	chartClassifyInfoList, err := data_manage.GetChartClassifyByLevelPath(chartClassifyInfo.LevelPath, chartClassifyInfo.Source)
+	if err != nil {
+		return
+	}
+	if len(chartClassifyInfoList) == 0 {
+		return
+	}
+	chartClassifyIdList := make([]string, 0)
+	for _, v := range chartClassifyInfoList {
+		chartClassifyIdList = append(chartClassifyIdList, strconv.Itoa(v.ChartClassifyId))
+	}
+	// 批量删除图表信息
+	condition := " AND source=? AND chart_classify_id in ? AND forum_chart_info_id > 0"
+	total, err := data_manage.GetChartInfoCountByCondition(condition, []interface{}{chartClassifyInfo.Source, chartClassifyIdList})
+	if err != nil {
+		return
+	}	
+	if total == 0 {
+		return
+	}
+	
+	offset := 0
+	pageSize := 100
+	success := 0
+
+	// 计算需要分多少页
+	pageNum := total / pageSize
+	if total % pageSize != 0 {
+		pageNum += 1
+	}
+	// 循环更新100个图表数据
+	for i := 0; i < pageNum; i++ {
+		// 查询需要更新的图表信息
+		chartInfos, e := data_manage.GetChartInfoListByCondition(condition, []interface{}{chartClassifyInfo.Source, chartClassifyIdList}, offset, pageSize)
+		if e != nil {
+			err = fmt.Errorf("查询需要更新的图表信息失败: %v", e)
+			return
+		}
+		if len(chartInfos) == 0 {
+			break
+		}
+		// 循环更新图表数据
+		for _, chartInfo := range chartInfos {
+			// 删除图表数据
+			er, msg := DeleteChart(chartInfo.ChartInfoId)
+			if er != nil {
+				er = fmt.Errorf("图表ID %d, 删除图表数据失败: %s, %v", chartInfo.ChartInfoId, msg, er)
+				tmpErr = append(tmpErr, er)
+				continue
+			}
+			success += 1
+		}
+
+	//	offset += pageSize
+	}
+	fmt.Println("删除图表数据完成, 删除图表数据总数:", success)
+	return	
+}
+
+// 批量删除资源库图表信息
+func ChartInfoDeleteBatchByChartInfoIds(chartInfoIds []int, chartClassifyId int) (err error) {
+	if utils.BusinessCode == "" || (utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeDebug && utils.BusinessCode != utils.BusinessCodeSandbox) {
+		return
+	}
+	var tmpErr []error
+	defer func() {
+		stack := ""
+		if err != nil {
+			stack = fmt.Sprintln(stack + err.Error())
+		}
+		if len(tmpErr) > 0 {
+			for _, v := range tmpErr {
+				stack = fmt.Sprintln(stack + v.Error())
+			}
+		}
+		if stack != "" {
+			utils.FileLog.Error("批量删除资源库图表信息失败"+"<br/>"+stack)
+			go alarm_msg.SendAlarmMsg("批量删除资源库图表信息失败"+"<br/>"+stack, 3)
+		}
+	}()
+	// 查询是否精选分类
+	chartClassifyInfo, err := data_manage.GetChartClassifyById(chartClassifyId)
+	if err != nil {
+		return
+	}
+	if chartClassifyInfo.IsSelected == 1 {
+		utils.FileLog.Info("精选分类,不需要删除图表信息")
+		return
+	}
+	// 非精选,需要下架图表,批量删除图表信息
+	condition := " AND source=1 AND chart_info_id in ? AND forum_chart_info_id > 0"
+	total, err := data_manage.GetChartInfoCountByCondition(condition, []interface{}{chartInfoIds})
+	if err != nil {
+		return
+	}	
+	if total == 0 {
+		utils.FileLog.Info("没有需要下架的图表")
+		return
+	}
+	
+	offset := 0
+	pageSize := 100
+	success := 0
+
+	// 计算需要分多少页
+	pageNum := total / pageSize
+	if total % pageSize != 0 {
+		pageNum += 1
+	}
+	// 循环更新100个图表数据
+	for i := 0; i < pageNum; i++ {
+		// 查询需要更新的图表信息
+		chartInfos, e := data_manage.GetChartInfoListByCondition(condition, []interface{}{chartInfoIds}, offset, pageSize)
+		if e != nil {
+			err = fmt.Errorf("查询需要更新的图表信息失败: %v", e)
+			return
+		}
+		if len(chartInfos) == 0 {
+			break
+		}
+		// 循环更新图表数据
+		for _, chartInfo := range chartInfos {
+			// 删除图表数据
+			er, msg := DeleteChart(chartInfo.ChartInfoId)
+			if er != nil {
+				er = fmt.Errorf("图表ID %d, 删除图表数据失败: %s, %v", chartInfo.ChartInfoId, msg, er)
+				tmpErr = append(tmpErr, er)
+				continue
+			}
+			success += 1
+		}
+
+		//offset += pageSize
+	}
+	fmt.Println("删除图表数据完成, 删除图表数据总数:", success)
+	return	
+}
+
+// 修改分类状态,一键上架至资源库
+func SetChartClassifyResourceStatusUp(chartClassifyInfo *data_manage.ChartClassify, sysUser *system.Admin) (errMsg string, err error) {
+	if chartClassifyInfo.ResourceStatus == utils.ChartClassifyResourceStatusUp {
+		errMsg = "分类状态已为已上架, 无需重复操作"
+		err = fmt.Errorf("%s", errMsg)
+		return
+	}
+	// 判断是否是精选资源
+	if chartClassifyInfo.IsSelected != utils.ChartClassifyIsSelected {
+		errMsg = "分类状态已为已上架, 无需重复操作"
+		err = fmt.Errorf("%s", errMsg)
+		return
+	}
+	// 查询分类下的所有图表信息
+	err = data_manage.UpdateChartClassifyResourceStatus(chartClassifyInfo.Source, utils.ChartClassifyResourceStatusUp, chartClassifyInfo.LevelPath)
+	if err != nil {
+		errMsg = "更新分类状态失败"
+		err = fmt.Errorf("更新分类状态失败,Err:%w", err)
+		return
+	}
+
+	go ChartBatchUpdateAndUpload(chartClassifyInfo, sysUser)
+	
+	return
+}
+
+// 修改分类,一键从资源库下架
+func SetChartClassifyResourceStatusDown(chartClassifyInfo *data_manage.ChartClassify, sysUser *system.Admin) (errMsg string, err error) {
+	if chartClassifyInfo.ResourceStatus == utils.ChartClassifyResourceStatusDown {
+		errMsg = "分类状态已为已下架, 无需重复操作"
+		err = fmt.Errorf("%s", errMsg)
+		return
+	}
+	// 查询分类下的所有图表信息
+	err = data_manage.UpdateChartClassifyResourceStatus(chartClassifyInfo.Source, utils.ChartClassifyResourceStatusDown, chartClassifyInfo.LevelPath)
+	if err != nil {
+		errMsg = "更新分类状态失败"
+		err = fmt.Errorf("更新分类状态失败,Err:%w", err)
+		return
+	}
+
+	go ChartInfoDeleteBatch(chartClassifyInfo, sysUser)
+	
+	return
+}
+
+func getChartDescriptionWithAdminNameByChartInfoId(chartInfoId int) (list []*data_manage.ChartDescriptionReq, err error, errMsg string) {
+	descriptionList, err := data_manage.GetChartDescriptionByChartInfoId(chartInfoId)
+	if err != nil {
+		errMsg = "获取图表简介失败"
+		err = fmt.Errorf("获取图表简介失败,Err:" + err.Error())
+		return
+	}
+	// 查询创建者信息
+	adminIdList := make([]int, 0)
+	for _, v := range descriptionList {
+		adminIdList = append(adminIdList, v.SysUserId)
+	}
+	adminList, err := system.GetAdminListByIdList(adminIdList)
+	if err != nil {
+		errMsg = "获取创建者信息失败"
+		err = fmt.Errorf("获取创建者信息失败,Err:" + err.Error())
+		return
+	}
+	adminMap := make(map[int]string)
+	for _, v := range adminList {
+		adminMap[v.AdminId] = v.AdminName
+	}
+	for _, v := range descriptionList {
+		adminName, ok := adminMap[v.SysUserId]
+		if !ok {
+			adminName = ""
+		}
+		list = append(list, &data_manage.ChartDescriptionReq{
+			Id: v.Id,
+			ChartInfoId: v.ChartInfoId,
+			Description: v.Description,
+			AdminName:   adminName,
+			SysUserId:   v.SysUserId,
+			SysUserRealName: v.SysUserRealName,
+			ModifyTime: v.ModifyTime,
+			CreateTime: v.CreateTime,
+		})
+	}
+	return
+}

+ 20 - 1
services/eta_forum/eta_forum_hub_lib.go

@@ -189,7 +189,26 @@ func GroupDeleteLib(req string) (resp *models.BaseResponse, err error) {
 	return
 }
 
-// post
+// ChartClassifySaveLib 上传图表分类信息
+func ChartClassifySaveLib(req string) (resp *models.BaseResponse, err error) {
+	_, resultByte, err := post(req, "/v1/chart_classify/save")
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// ChartClassifySaveBatchLib 批量上传图表分类信息
+func ChartClassifySaveBatchLib(req string) (resp *models.BaseResponse, err error) {
+	_, resultByte, err := post(req, "/v1/chart_classify/batch_save")
+	err = json.Unmarshal(resultByte, &resp)
+	if err != nil {
+		return
+	}
+	return
+}
+
 func post(paramStr string, urlStr string) (resp *models.BaseResponse, result []byte, err error) {
 	if utils.ETA_FORUM_HUB_URL == "" {
 		err = fmt.Errorf("ETA社区桥接服务地址为空")

+ 15 - 15
services/excel/lucky_sheet.go

@@ -9,6 +9,7 @@ import (
 	"fmt"
 	"os"
 	"reflect"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -824,13 +825,12 @@ func getExcelFontConf(cellInfo LuckySheetDataValue) xlsx.Font {
 		}
 		familyName = tmpFamilyName
 	}
-
 	return xlsx.Font{
 		Size: fontSize,
 		Name: familyName,
 		//Family: v2.FontFamily,
 		//Charset:   0,
-		Color:     strings.TrimPrefix(cellInfo.FontColor, "#"),
+		Color:     strings.TrimPrefix(getColor(cellInfo.FontColor), "#"),
 		Bold:      isBold,
 		Italic:    isItalic,
 		Underline: isUnderline,
@@ -1429,22 +1429,22 @@ func getExcelizeAlignmentConf(cellInfo LuckySheetDataValue) *excelize.Alignment
 
 // getColor 获取hex颜色
 func getColor(bgStr string) string {
-	if strings.Contains(bgStr, "(") {
-		arr := strings.Split(bgStr, ",")
-		if len(arr) != 3 {
+	isRgb := strings.HasPrefix(bgStr, "rgb")
+	re := regexp.MustCompile(`\(([^)]+)\)`)
+	matches := re.FindStringSubmatch(bgStr)
+	if len(matches) != 2 {
+		return bgStr
+	}
+	if isRgb {
+		arr := strings.Split(matches[1], ",")
+		if len(arr) < 3 {
 			return bgStr
 		}
-
-		// 第一位
-		tmpFirstArr := strings.Split(arr[0], "(")
-		arr[0] = tmpFirstArr[len(tmpFirstArr)-1]
-
-		// 最后一位
-		tmpLastArr := strings.Split(arr[2], ")")
-		arr[2] = tmpLastArr[0]
-
 		rgbArr := make([]int64, 0)
-		for _, v := range arr {
+		for i, v := range arr {
+			if i >= 3 {
+				continue
+			}
 			tmpInt, err := strconv.Atoi(utils.TrimStr(v))
 			if err != nil {
 				return bgStr

+ 89 - 13
services/llm/facade/llm_service.go

@@ -49,7 +49,11 @@ func LLMKnowledgeBaseSearchDocs(search LLMKnowledgeSearch) (resp bus_response.Se
 
 // AIGCBaseOnPromote aigc 生成内容
 func AIGCBaseOnPromote(aigc AIGC) (resp bus_response.AIGCEtaResponse, err error) {
-	mapping, queryErr := rag.GetArticleKbMapping(aigc.ArticleId)
+	param := make(map[string]interface{})
+	if aigc.LLMModel != "" {
+		param["LLM"] = aigc.LLMModel
+	}
+	mapping, queryErr := rag.GetArticleKbMapping(aigc.ArticleId, aigc.Source)
 	if queryErr != nil && !errors.Is(queryErr, gorm.ErrRecordNotFound) {
 		utils.FileLog.Error("获取文章知识库信息失败,err: %v", queryErr)
 		err = fmt.Errorf("获取文章知识库信息失败,err: %v", queryErr)
@@ -58,20 +62,37 @@ func AIGCBaseOnPromote(aigc AIGC) (resp bus_response.AIGCEtaResponse, err error)
 		var kbId string
 		var file *os.File
 		if mapping.Id == 0 || mapping.KbId == "" {
-			article, fileErr := rag.GetArticleById(aigc.ArticleId)
-			if fileErr != nil {
-				// 找不到就处理失败
-				utils.FileLog.Error("公众号文章不存在")
-				err = fmt.Errorf("公众号文章不存在")
-				return
+			var title, textContent string
+			switch aigc.Source {
+			case 0:
+				article, fileErr := rag.GetArticleById(aigc.ArticleId)
+				if fileErr != nil {
+					// 找不到就处理失败
+					utils.FileLog.Error("公众号文章不存在")
+					err = fmt.Errorf("公众号文章不存在")
+					return
+				}
+				textContent = article.TextContent
+				title = article.Title
+			case 1:
+				ragEtaReportObj := rag.RagEtaReport{}
+				article, fileErr := ragEtaReportObj.GetById(aigc.ArticleId)
+				if fileErr != nil {
+					// 找不到就处理失败
+					utils.FileLog.Error("ETA文章不存在")
+					err = fmt.Errorf("ETA文章不存在")
+					return
+				}
+				textContent = article.TextContent
+				title = article.Title
 			}
-			if article.TextContent == "" {
+			if textContent == "" {
 				utils.FileLog.Error("暂不支持纯文本以外的内容生成")
 				err = fmt.Errorf("暂不支持纯文本以外的内容生成")
 				return
 			}
 			// 文章加入到知识库
-			path, fileErr := localService.CreateArticleFile(article)
+			path, fileErr := localService.CreateArticleFile(title, textContent)
 			if fileErr != nil {
 				utils.FileLog.Error("创建文章文件失败,err: %v", fileErr)
 				err = fmt.Errorf("创建文章文件失败,err: %v", fileErr)
@@ -85,7 +106,7 @@ func AIGCBaseOnPromote(aigc AIGC) (resp bus_response.AIGCEtaResponse, err error)
 				utils.FileLog.Error("打开文件失败,err:", err)
 				return
 			}
-			uploadResp, httpErr := llmService.UploadFileToTemplate([]*os.File{file}, nil)
+			uploadResp, httpErr := llmService.UploadFileToTemplate([]*os.File{file}, param)
 			if httpErr != nil {
 				utils.FileLog.Error("上传文件失败,err:", err.Error())
 				err = fmt.Errorf("上传文件失败,err:%v", httpErr)
@@ -99,6 +120,7 @@ func AIGCBaseOnPromote(aigc AIGC) (resp bus_response.AIGCEtaResponse, err error)
 				return
 			}
 			err = rag.CreateArticleKbMapping(rag.ArticleKbMapping{
+				Source:          aigc.Source,
 				WechatArticleId: aigc.ArticleId,
 				KbId:            data.Id,
 				CreatedTime:     time.Now(),
@@ -111,9 +133,9 @@ func AIGCBaseOnPromote(aigc AIGC) (resp bus_response.AIGCEtaResponse, err error)
 			kbId = mapping.KbId
 		}
 		//知识库对话
-		response, httpErr := llmService.FileChat(aigc.Promote, kbId, nil)
+		response, httpErr := llmService.FileChat(aigc.Promote, kbId, aigc.LLMModel, nil)
 		if httpErr != nil {
-			utils.FileLog.Error("内容生成失败,err:", err.Error())
+			utils.FileLog.Error("内容生成失败,err:", httpErr.Error())
 			err = fmt.Errorf("内容生成失败,err:%v", httpErr)
 			return
 		}
@@ -129,7 +151,59 @@ func AIGCBaseOnPromote(aigc AIGC) (resp bus_response.AIGCEtaResponse, err error)
 			return
 		}
 		if gcResp.Code == 404 {
-			response, httpErr = llmService.FileChat(aigc.Promote, kbId, nil)
+			param["PrevId"] = kbId
+			var title, textContent string
+			switch aigc.Source {
+			case 0:
+				article, fileErr := rag.GetArticleById(aigc.ArticleId)
+				if fileErr != nil {
+					// 找不到就处理失败
+					utils.FileLog.Error("公众号文章不存在")
+					err = fmt.Errorf("公众号文章不存在")
+					return
+				}
+				textContent = article.TextContent
+				title = article.Title
+			case 1:
+				ragEtaReportObj := rag.RagEtaReport{}
+				article, fileErr := ragEtaReportObj.GetById(aigc.ArticleId)
+				if fileErr != nil {
+					// 找不到就处理失败
+					utils.FileLog.Error("ETA文章不存在")
+					err = fmt.Errorf("ETA文章不存在")
+					return
+				}
+				textContent = article.TextContent
+				title = article.Title
+			}
+			if textContent == "" {
+				utils.FileLog.Error("暂不支持纯文本以外的内容生成")
+				err = fmt.Errorf("暂不支持纯文本以外的内容生成")
+				return
+			}
+			// 文章加入到知识库
+			path, fileErr := localService.CreateArticleFile(title, textContent)
+			if fileErr != nil {
+				utils.FileLog.Error("创建文章文件失败,err: %v", fileErr)
+				err = fmt.Errorf("创建文章文件失败,err: %v", fileErr)
+				return
+			}
+			defer func() {
+				_ = os.Remove(path)
+			}()
+			file, err = os.Open(path)
+			if err != nil {
+				utils.FileLog.Error("打开文件失败,err:", err)
+				return
+			}
+			_, httpErr = llmService.UploadFileToTemplate([]*os.File{file}, param)
+			if httpErr != nil {
+				utils.FileLog.Error("上传文件失败,err:", err.Error())
+				err = fmt.Errorf("上传文件失败,err:%v", httpErr)
+				return
+			}
+
+			response, httpErr = llmService.FileChat(aigc.Promote, kbId, aigc.LLMModel, nil)
 			if httpErr != nil {
 				utils.FileLog.Error("内容生成失败,err:%v", httpErr.Error())
 				err = fmt.Errorf("内容生成失败,err:%v", httpErr)
@@ -163,7 +237,9 @@ type LLMKnowledgeSearch struct {
 
 type AIGC struct {
 	Promote   string
+	Source    int
 	ArticleId int
+	LLMModel  string
 }
 
 func dealFileChatResp(response eta_llm_http.BaseResponse) (httpResponse bus_response.FileChatBaseResponse, err error) {

+ 4 - 5
services/llm/promote_service.go

@@ -1,14 +1,13 @@
 package llm
 
 import (
-	"eta/eta_api/models/rag"
 	"eta/eta_api/utils"
 	"fmt"
 	"os"
 )
 
-func CreateArticleFile(item *rag.WechatArticle) (tmpFilePath string, err error) {
-	if item.TextContent == `` {
+func CreateArticleFile(title, textContent string) (tmpFilePath string, err error) {
+	if textContent == `` {
 		err = fmt.Errorf("生成文章原文文本失败,文章内容为空")
 		return
 	}
@@ -19,9 +18,9 @@ func CreateArticleFile(item *rag.WechatArticle) (tmpFilePath string, err error)
 		err = fmt.Errorf("存储目录创建失败,Err:" + err.Error())
 		return
 	}
-	fileName := utils.RemoveSpecialChars(item.Title) + `.md`
+	fileName := utils.RemoveSpecialChars(title) + `.md`
 	tmpFilePath = uploadDir + "/" + fileName
-	err = utils.SaveToFile(item.TextContent, tmpFilePath)
+	err = utils.SaveToFile(textContent, tmpFilePath)
 	if err != nil {
 		err = fmt.Errorf("生成临时文件失败,Err:" + err.Error())
 		return

+ 272 - 0
services/llm_report.go

@@ -0,0 +1,272 @@
+package services
+
+import (
+	"eta/eta_api/models"
+	"eta/eta_api/models/rag"
+	"eta/eta_api/utils"
+	"fmt"
+	"golang.org/x/net/html"
+	"golang.org/x/net/html/atom"
+	"regexp"
+	"strings"
+	"time"
+)
+
+// ReportAddOrModifyKnowledge
+// @Description: ETA报告加入/修改到知识库
+// @author: Roc
+// @datetime 2025-04-07 14:41:45
+// @param reportId int
+// @param reportChapterId int
+func ReportAddOrModifyKnowledge(reportId, reportChapterId int) {
+	if reportId <= 0 {
+		return
+	}
+	var err error
+	defer func() {
+		if err != nil {
+			//fmt.Println("ReportAddOrModifyKnowledge error:", err)
+			utils.FileLog.Error("ReportAddOrModifyKnowledge error:", err)
+		}
+	}()
+
+	var title, author, htmlContent string
+	var publishTime time.Time
+
+	if reportChapterId > 0 {
+		chapterInfo, tmpErr := models.GetReportChapterInfoById(reportChapterId)
+		if tmpErr != nil {
+			return
+		}
+		title = chapterInfo.Title
+		author = chapterInfo.Author
+		publishTime = chapterInfo.PublishTime
+		htmlContent = chapterInfo.Content
+	} else {
+		reportInfo, tmpErr := models.GetReportByReportId(reportId)
+		if tmpErr != nil {
+			return
+		}
+		title = reportInfo.Title
+		author = reportInfo.Author
+		publishTime = reportInfo.PublishTime
+		htmlContent = reportInfo.Content
+	}
+
+	err = handleReportAddOrModifyKnowledge(reportId, reportChapterId, title, author, htmlContent, publishTime)
+
+	return
+}
+
+// ReportAddOrModifyKnowledgeByReportId
+// @Description: ETA报告加入/修改到知识库(只传id的情况)
+// @author: Roc
+// @datetime 2025-04-07 15:41:15
+// @param reportId int
+func ReportAddOrModifyKnowledgeByReportId(reportId int) {
+	if reportId <= 0 {
+		return
+	}
+	errList := make([]string, 0)
+	defer func() {
+		if len(errList) > 0 {
+			utils.FileLog.Error("ReportAddOrModifyKnowledge error,报告ID:%d:%s", reportId, strings.Join(errList, "\n"))
+		}
+	}()
+
+	reportInfo, err := models.GetReportByReportId(reportId)
+	if err != nil {
+		errList = append(errList, err.Error())
+		return
+	}
+
+	// 如果是单篇报告,那么直接处理
+	if reportInfo.HasChapter == 0 {
+		err = handleReportAddOrModifyKnowledge(reportId, 0, reportInfo.Title, reportInfo.Author, reportInfo.Content, reportInfo.PublishTime)
+		if err != nil {
+			errList = append(errList, err.Error())
+		}
+		return
+	}
+
+	// 章节类型的报告,需要查询出来后再处理
+	chapterInfoList, err := models.GetPublishedChapterListByReportId(reportId)
+	if err != nil {
+		errList = append(errList, err.Error())
+		return
+	}
+	for _, v := range chapterInfoList {
+		err = handleReportAddOrModifyKnowledge(reportId, v.ReportChapterId, v.Title, reportInfo.Author, v.Content, v.PublishTime)
+		if err != nil {
+			errList = append(errList, fmt.Sprintf("第%d章:%s,异常:\n%s", v.ReportChapterId, v.Title, err.Error()))
+			continue
+		}
+	}
+
+	return
+}
+
+// handleReportAddOrModifyKnowledge
+// @Description: 处理ETA报告加入/修改到知识库
+// @author: Roc
+// @datetime 2025-04-07 15:33:38
+// @param reportId int
+// @param reportChapterId int
+// @param title string
+// @param author string
+// @param htmlContent string
+// @param publishTime time.Time
+// @return err error
+func handleReportAddOrModifyKnowledge(reportId, reportChapterId int, title, author, htmlContent string, publishTime time.Time) (err error) {
+	htmlContent = html.UnescapeString(htmlContent)
+	doc, err := html.Parse(strings.NewReader(htmlContent))
+	if err != nil {
+		return
+	}
+	// 只获取文本内容
+	content := &strings.Builder{}
+	getArticleContent(content, doc)
+
+	textContent := content.String()
+	textContent = regexp.MustCompile(`\n+`).ReplaceAllString(textContent, "\n")
+	textContent = strings.Trim(textContent, "\n")
+
+	publishTimeStr := `未知`
+	if !publishTime.IsZero() {
+		title = fmt.Sprintf("%s(%s)", title, publishTime.Format(utils.FormatMonthDayUnSpace))
+		publishTimeStr = publishTime.Format(utils.FormatDateTime)
+	}
+
+	textContent = fmt.Sprintf("标题:%s\n发布时间:%s\n%s", title, publishTimeStr, textContent)
+
+	obj := rag.RagEtaReport{}
+	item, err := obj.GetByReportAndChapterId(reportId, reportChapterId)
+	if err != nil && !utils.IsErrNoRow(err) {
+		// 查询异常,且不是没找到数据的报错
+		return
+	}
+
+	if err == nil {
+		// 标记删除了的话,那就不处理了
+		if item.IsDeleted == 1 {
+			return
+		}
+		item.Title = title
+		item.Author = author
+		item.TextContent = textContent
+		item.IsPublished = 1
+		//item.PublishTime = publishTime
+		item.ModifyTime = time.Now()
+		//err = item.Update([]string{"title", "author", "text_content", "is_published", "publish_time", "modify_time"})
+		err = item.Update([]string{"title", "author", "text_content", "is_published", "modify_time"})
+	} else {
+		// 无数据的时候,需要新增
+		err = nil
+		item = &rag.RagEtaReport{
+			RagEtaReportId:  0,
+			ReportId:        reportId,
+			ReportChapterId: reportChapterId,
+			Title:           title,
+			Author:          author,
+			TextContent:     textContent,
+			VectorKey:       "",
+			IsPublished:     1,
+			IsDeleted:       0,
+			PublishTime:     publishTime,
+			ModifyTime:      time.Now(),
+			CreateTime:      time.Now(),
+		}
+		err = item.Create()
+	}
+
+	return
+}
+
+// ReportUnPublishedKnowledge
+// @Description: 知识库取消发布
+// @author: Roc
+// @datetime 2025-04-07 14:58:25
+// @param reportId int
+// @param reportChapterId int
+func ReportUnPublishedKnowledge(reportId, reportChapterId int) {
+	if reportId <= 0 && reportChapterId <= 0 {
+		return
+	}
+	var err error
+	defer func() {
+		if err != nil {
+			//fmt.Println("ReportAddOrModifyKnowledge error:", err)
+			utils.FileLog.Error("ReportAddOrModifyKnowledge error:", err)
+		}
+	}()
+
+	obj := rag.RagEtaReport{}
+	item, err := obj.GetByReportAndChapterId(reportId, reportChapterId)
+	if err != nil && !utils.IsErrNoRow(err) {
+		// 查询异常,且不是没找到数据的报错
+		return
+	}
+
+	if item.RagEtaReportId > 0 {
+		item.IsPublished = 0
+		item.ModifyTime = time.Now()
+		err = item.Update([]string{"is_published", "modify_time"})
+	}
+
+	return
+}
+
+// ReportUnPublishedKnowledgeByReportId
+// @Description: ETA报告取消发布同步到知识库(只传报告id的情况)
+// @author: Roc
+// @datetime 2025-04-07 15:41:15
+// @param reportId int
+func ReportUnPublishedKnowledgeByReportId(reportId int) {
+	errList := make([]string, 0)
+	defer func() {
+		if len(errList) > 0 {
+			utils.FileLog.Error("ReportUnPublishedKnowledgeByReportId error,报告ID:%d:%s", reportId, strings.Join(errList, "\n"))
+		}
+	}()
+
+	obj := rag.RagEtaReport{}
+	list, err := obj.GetListByCondition(``, ` AND report_id = ? `, []interface{}{reportId}, 0, 1000)
+	if err != nil && !utils.IsErrNoRow(err) {
+		// 查询异常,且不是没找到数据的报错
+		return
+	}
+
+	for _, item := range list {
+		item.IsPublished = 0
+		item.ModifyTime = time.Now()
+		err = item.Update([]string{"is_published", "modify_time"})
+		if err != nil {
+			errList = append(errList, fmt.Sprintf("第%d章:%s,异常:\n%s", item.ReportChapterId, item.Title, err.Error()))
+			continue
+		}
+	}
+
+	return
+}
+
+func getArticleContent(content *strings.Builder, htmlContentNode *html.Node) {
+	if htmlContentNode.Type == html.TextNode {
+		cleanData := strings.TrimSpace(htmlContentNode.Data)
+		if cleanData != `` && cleanData != "</p>" {
+			content.WriteString(cleanData)
+		}
+	} else if htmlContentNode.Type == html.ElementNode {
+		switch htmlContentNode.DataAtom {
+		case atom.Ul:
+			content.WriteString("\n")
+		case atom.Br:
+			// 遇到 <br> 标签时添加换行符
+			content.WriteString("\n")
+		case atom.P:
+			content.WriteString("\n")
+		}
+	}
+	for c := htmlContentNode.FirstChild; c != nil; c = c.NextSibling {
+		getArticleContent(content, c)
+	}
+}

+ 20 - 0
services/material/material.go

@@ -1,6 +1,7 @@
 package materialService
 
 import (
+	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/data_manage/excel"
@@ -168,6 +169,25 @@ func AddToMaterial(req material.SaveAsMaterialReq, opUserId int, opUserName stri
 		return
 	}
 
+	// 兼容部分内网客户替换OSS地址
+	conf, e := models.GetBusinessConfByKey(models.BusinessConfOssUrlReplace)
+	if e != nil && !utils.IsErrNoRow(e) {
+		errMsg = "操作失败"
+		err = fmt.Errorf("获取内网配置失败, %v", e)
+		return
+	}
+	if conf != nil && conf.ConfVal != "" {
+		var urlReplace models.OssUrlReplace
+		if e := json.Unmarshal([]byte(conf.ConfVal), &urlReplace); e != nil {
+			errMsg = "操作失败"
+			err = fmt.Errorf("内网配置解析失败, %v", e)
+			return
+		}
+		if urlReplace.IsReplace && urlReplace.OssUrlOrigin != "" {
+			oldRsourceUrl = strings.ReplaceAll(oldRsourceUrl, urlReplace.OssUrlOrigin, urlReplace.OssUrlNew)
+		}
+	}
+
 	resourceUrl, err, errMsg := uploadToMaterial(oldRsourceUrl)
 	if err != nil {
 		return

+ 6 - 4
services/report.go

@@ -143,7 +143,7 @@ func UpdateReportEs(reportId int, publishState int) (err error) {
 		if len(chapterList) > 0 {
 			// 更新章节的es数据
 			for _, chapterInfo := range chapterList {
-				err = updateReportChapterEsByChapter(chapterInfo)
+				err = updateReportChapterEsByChapter(chapterInfo, reportInfo.IsPublicPublish)
 				if err != nil {
 					return
 				}
@@ -183,6 +183,7 @@ func UpdateReportEs(reportId int, publishState int) (err error) {
 		BodyContent:        utils.TrimHtml(html.UnescapeString(reportInfo.Content)),
 		PublishTime:        reportInfo.PublishTime.Format(utils.FormatDateTime),
 		PublishState:       publishState,
+		IsPublicPublish:    reportInfo.IsPublicPublish,
 		Author:             reportInfo.Author,
 		ClassifyIdFirst:    reportInfo.ClassifyIdFirst,
 		ClassifyNameFirst:  reportInfo.ClassifyNameFirst,
@@ -241,7 +242,7 @@ func addCategoryAliasToArr(categoryArr []string) (aliasArr []string, err error)
 // @datetime 2024-06-20 13:16:22
 // @param reportChapterId int
 // @return err error
-func UpdateReportChapterEs(reportChapterId int) (err error) {
+func UpdateReportChapterEs(reportChapterId int, isPublicPublish int8) (err error) {
 	if reportChapterId <= 0 {
 		return
 	}
@@ -250,7 +251,7 @@ func UpdateReportChapterEs(reportChapterId int) (err error) {
 		return
 	}
 
-	err = updateReportChapterEsByChapter(chapterInfo)
+	err = updateReportChapterEsByChapter(chapterInfo, isPublicPublish)
 	if err != nil {
 		return
 	}
@@ -264,7 +265,7 @@ func UpdateReportChapterEs(reportChapterId int) (err error) {
 // @datetime 2024-06-20 13:16:11
 // @param chapterInfo *models.ReportChapter
 // @return err error
-func updateReportChapterEsByChapter(chapterInfo *models.ReportChapter) (err error) {
+func updateReportChapterEsByChapter(chapterInfo *models.ReportChapter, isPublicPublish int8) (err error) {
 	// 章节对应的品种
 	obj := report.ReportChapterPermissionMapping{}
 	permissionList, tmpErr := obj.GetPermissionItemListById(chapterInfo.ReportChapterId)
@@ -289,6 +290,7 @@ func updateReportChapterEsByChapter(chapterInfo *models.ReportChapter) (err erro
 		BodyContent:        utils.TrimHtml(html.UnescapeString(chapterInfo.Content)),
 		PublishTime:        chapterInfo.PublishTime.Format(utils.FormatDateTime),
 		PublishState:       chapterInfo.PublishState,
+		IsPublicPublish:    isPublicPublish,
 		Author:             chapterInfo.Author,
 		ClassifyIdFirst:    chapterInfo.ClassifyIdFirst,
 		ClassifyNameFirst:  chapterInfo.ClassifyNameFirst,

+ 4 - 0
services/report_approve.go

@@ -1,6 +1,7 @@
 package services
 
 import (
+	"eta/eta_api/cache"
 	"eta/eta_api/models"
 	"eta/eta_api/models/report_approve"
 	"eta/eta_api/models/smart_report"
@@ -867,6 +868,9 @@ func AfterReportApprovePass(reportType, reportId int) (err error) {
 		//_ = CreateVideo(reportInfo)
 		_ = UpdateReportEs(reportInfo.Id, models.ReportStatePublished)
 
+		// 报告发布成功后,需要将相关信息入知识库
+		go cache.RagEtaReportOpToCache(reportInfo.Id, 0, `publish`)
+
 		return
 	}
 

+ 404 - 0
services/report_rai.go

@@ -0,0 +1,404 @@
+package services
+
+import (
+	"encoding/json"
+	"eta/eta_api/cache"
+	"eta/eta_api/models"
+	"eta/eta_api/models/report"
+	"eta/eta_api/services/alarm_msg"
+	"eta/eta_api/utils"
+	"fmt"
+	"html"
+	"io/ioutil"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func AutoInsertRaiReport() {
+	for {
+		utils.Rc.Brpop(utils.FICC_ARTICLE_UPDATE_KEY, func(b []byte) {
+			var log models.RaiReportNotifyRedis
+			if err := json.Unmarshal(b, &log); err != nil {
+				utils.FileLog.Info("获取权益报告并更新处理Redis队列消息失败: json unmarshal wrong!", err.Error())
+				go alarm_msg.SendAlarmMsg(fmt.Sprintf("获取权益报告并更新处理Redis队列消息失败: json unmarshal wrong! %s", err.Error()), 2)
+			}
+			// 这里直接go出去会出现并发,导致文章md5ID唯一索引限制报错
+			err := HandleInsertRaiReport(log.ArticleId)
+			if err != nil {
+				utils.FileLog.Info("获取权益报告并更新处理Redis队列消息失败: HandleInsertRaiReport ", err.Error())
+				go alarm_msg.SendAlarmMsg(fmt.Sprintf("获取权益报告并更新处理Redis队列消息失败: HandleInsertRaiReport %s", err.Error()), 2)
+			}
+		})
+	}
+}
+
+func HandleInsertRaiReport(raiReportId int) (err error) {
+	// 设置缓存,防止重复处理
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf("处理同步过来的文章失败"+"HandleArticleListByApi ErrMsg:%s artcleId:%d", err.Error(), raiReportId)
+			utils.FileLog.Info(msg, 2)
+			go alarm_msg.SendAlarmMsg(msg, 2)
+		}
+	}()
+
+	body, err := getRaiReportLib(fmt.Sprintf("%s/articles/%d", utils.RaiReportLibUrl, raiReportId))
+	if err != nil {
+		fmt.Println(err)
+		err = fmt.Errorf("获取权益报告失败, Err: %s", err.Error())
+		return
+	}
+	var articleResultDate models.ArticleDetailResultApi
+	err = json.Unmarshal(body, &articleResultDate)
+	if err != nil {
+		fmt.Println("Getres.PublicGetDate Err:", err.Error())
+		return err
+	}
+
+	articleResult := articleResultDate.Data
+	err = handleInsertRaiReport(articleResult)
+	if err != nil {
+		return err
+	}
+
+	return
+}
+
+func handleInsertRaiReport(articleResult models.ArticleResultApidate) (err error) {
+	raiReportId := articleResult.ArticleId
+	// 设置缓存,防止重复处理
+	cacheKey := fmt.Sprintf("rai_report_notify_redis_%d", raiReportId)
+	cacheValue := utils.Rc.GetStr(cacheKey)
+	if cacheValue != "" {
+		return nil
+	}
+	utils.Rc.SetNX(cacheKey, "1", 10*time.Second)
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf("处理同步过来的文章失败"+"HandleArticleListByApi ErrMsg:%s artcleId:%d", err.Error(), raiReportId)
+			utils.FileLog.Info(msg, 2)
+			go alarm_msg.SendAlarmMsg(msg, 2)
+		}
+		utils.Rc.Delete(cacheKey)
+	}()
+	// var clueApiUrl string
+	// clueApiUrl = fmt.Sprint(utils.RaiReportLibUrl, "articles/", raiReportId)
+	// fmt.Println(clueApiUrl)
+
+	// body, err := getRaiReportLib(clueApiUrl)
+	// if err != nil {
+	// 	fmt.Println(err)
+	// 	err = fmt.Errorf("获取权益报告失败, Err: %s", err.Error())
+	// 	return
+	// }
+	// var articleResultDate models.ArticleDetailResultApi
+	// err = json.Unmarshal(body, &articleResultDate)
+	// if err != nil {
+	// 	fmt.Println("Getres.PublicGetDate Err:", err.Error())
+	// 	return err
+	// }
+
+	// articleResult := articleResultDate.Data
+	// 判断是否是固收研究
+	if articleResult.IndustrId != 12 {
+		return nil
+	}
+	// 根据分类名称查找分类信息
+	classifyItemList, e := models.GetReportClassifyByClassifyName([]string{articleResult.Industry.Name, articleResult.Series.Name})
+	if e != nil {
+		err = fmt.Errorf("GetReportClassifyByClassifyName err: %s", e.Error())
+		return err
+	}
+	classifyMap := make(map[string]*models.Classify)
+	for _, v := range classifyItemList {
+		classifyMap[v.ClassifyName] = v
+	}
+	classifyFirst, ok := classifyMap[articleResult.Industry.Name]
+	if !ok {
+		err = fmt.Errorf("一级分类不存在")
+		return err
+	}
+	classifySecond, ok := classifyMap[articleResult.Series.Name]
+	if !ok {
+		// 新增二级分类
+		err, _, _ = AddReportClassify(articleResult.Series.Name, classifyFirst.Id, []int{})
+		if err != nil {
+			err = fmt.Errorf("添加二级分类失败, Err: %s", err.Error())
+			return err
+		}
+		item, err := models.GetClassifyByName(articleResult.Series.Name, classifyFirst.Id)
+		if err != nil {
+			err = fmt.Errorf("添加二级分类失败, Err: %s", err.Error())
+			return err
+		}
+		classifySecond = item
+	}
+	// 判断分类的层级关系是否合理
+	if classifyFirst.Id != classifySecond.ParentId {
+		err = fmt.Errorf("分类层级关系不合理")
+		return err
+	}
+	// 判断报告是否已存在, 如果存在则更新报告,如果不存在则创建报告
+	reportInfo, err := models.GetReportByRaiReportId(articleResult.ArticleId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		return err
+	}
+	if err == nil && reportInfo.Id > 0 {
+		var contentSub string
+		if articleResult.Content.Body != "" {
+			contentSub, err = GetReportContentSub(articleResult.Content.Body)
+			if err != nil {
+				go alarm_msg.SendAlarmMsg("ContentSub 失败,Err:"+err.Error(), 3)
+			}
+		}
+		state := reportInfo.State
+
+		publishSource := `publish` //同步至知识库
+
+		// 报告已存在,更新报告
+		if (articleResult.PublishStatus == 2 || articleResult.PublishStatus == 4) && articleResult.IsActive {
+			// 报告状态为未发布,则更新报告
+			state = models.ReportStatePublished
+			reportInfo.PublishTime = articleResult.PublishDate
+		} else if !articleResult.IsActive {
+			publishSource = `un_publish` //同步至知识库
+
+			// 删除报告
+			err = models.DeleteReport(reportInfo.Id)
+			if err != nil {
+				err = fmt.Errorf("删除报告失败, Err: %s", err.Error())
+				return
+			}
+			go UpdateReportEs(reportInfo.Id, 1)
+			return
+		} else {
+			publishSource = `un_publish` //同步至知识库
+
+			// 报告状态为未发布,则更新报告
+			state = models.ReportStateUnpublished
+			reportInfo.PublishTime = articleResult.PublishDate
+		}
+		// 过滤Abstracthtml标签,把<p>标签去掉
+		abstract := strings.ReplaceAll(articleResult.Content.Abstract, "<p>", "")
+		abstract = strings.ReplaceAll(abstract, "</p>", "")
+
+		reportInfo.ClassifyIdFirst = classifyFirst.Id
+		reportInfo.ClassifyNameFirst = articleResult.Industry.Name
+		reportInfo.ClassifyIdSecond = classifySecond.Id
+		reportInfo.ClassifyNameSecond = articleResult.Series.Name
+		reportInfo.Title = articleResult.Title
+		reportInfo.Abstract = abstract
+		reportInfo.Author = articleResult.Author.Name
+		reportInfo.Frequency = articleResult.Frequency
+		reportInfo.State = state
+		reportInfo.Content = html.EscapeString(articleResult.Content.Body)
+		reportInfo.ContentSub = html.EscapeString(contentSub)
+		//updateTime, _ := time.ParseInLocation(utils.FormatDateTime, articleResult.UpdateDate, time.Local)
+		reportInfo.ModifyTime = articleResult.UpdateDate
+
+		// 报告更新
+		updateCols := []string{"ClassifyIdFirst", "ClassifyNameFirst", "ClassifyIdSecond", "ClassifyNameSecond", "Title", "Abstract", "Author", "Frequency", "State", "Content", "ContentSub", "ModifyTime", "PublishTime"}
+		err = reportInfo.UpdateReport(updateCols)
+		if err != nil {
+			err = fmt.Errorf("更新报告失败, Err: %s", err.Error())
+			return
+		}
+		go UpdateReportEs(reportInfo.Id, state)
+		if state == models.ReportStatePublished {
+			// 报告权限处理
+			go handleReportPermission(int64(reportInfo.Id), reportInfo.ClassifyIdSecond)
+		} else {
+			// 重置小程序详情页海报
+			_ = ResetMiniProgramReportDetailCover(reportInfo.Id)
+		}
+
+		// 报告发布成功后,需要将相关信息入知识库
+		go cache.RagEtaReportOpToCache(reportInfo.Id, 0, publishSource)
+
+	} else if reportInfo.Id == 0 {
+		err = nil
+		// 报告不存在,创建报告
+		// 判断状态
+		if (articleResult.PublishStatus == 2 || articleResult.PublishStatus == 4) && articleResult.IsActive {
+			var contentSub string
+			if articleResult.Content.Body != "" {
+				contentSub, err = GetReportContentSub(articleResult.Content.Body)
+				if err != nil {
+					go alarm_msg.SendAlarmMsg("ContentSub 失败,Err:"+err.Error(), 3)
+				}
+			}
+
+			// 已发布状态
+			state := models.ReportStatePublished
+
+			// 协作方式,1:个人,2:多人协作。默认:1
+			collaborateType := 1
+
+			// 报告布局,1:常规布局,2:智能布局。默认:1
+			reportLayout := 1
+
+			// 是否公开发布,1:是,2:否
+			isPublicPublish := 1
+
+			abstract := strings.ReplaceAll(articleResult.Content.Abstract, "<p>", "")
+			abstract = strings.ReplaceAll(abstract, "</p>", "")
+
+			item := new(models.Report)
+			item.AddType = 1
+			item.ReportVersion = 2
+			item.ClassifyIdFirst = classifyFirst.Id
+			item.ClassifyNameFirst = articleResult.Industry.Name
+			item.ClassifyIdSecond = classifySecond.Id
+			item.ClassifyNameSecond = articleResult.Series.Name
+			item.Title = articleResult.Title
+			item.Abstract = abstract
+			item.Author = articleResult.Author.Name
+			item.Frequency = articleResult.Frequency
+			item.State = state
+			item.Content = html.EscapeString(articleResult.Content.Body)
+			item.Stage = 0
+			item.ContentSub = html.EscapeString(contentSub)
+			item.CreateTime = time.Now().Format(utils.FormatDateTime)
+			//updateTime, _ := time.ParseInLocation(utils.FormatDateTime, articleResult.UpdateDate, time.Local)
+			item.ModifyTime = articleResult.UpdateDate
+			item.ReportVersion = 2
+			item.AdminId = 0
+			item.AdminRealName = ""
+			item.PublishTime = articleResult.PublishDate
+
+			item.ClassifyIdThird = 0
+			item.ClassifyNameThird = ""
+
+			item.LastModifyAdminId = 0
+			item.LastModifyAdminName = ""
+			item.ContentModifyTime = time.Now()
+			item.NeedSplice = 1
+			item.ContentStruct = ""
+			item.HeadImg = ""
+			item.EndImg = ""
+			item.CanvasColor = ""
+			item.HeadResourceId = 0
+			item.EndResourceId = 0
+			item.CollaborateType = int8(collaborateType)
+			item.ReportLayout = int8(reportLayout)
+			item.IsPublicPublish = int8(isPublicPublish)
+			createTime, _ := time.ParseInLocation(utils.FormatDate, articleResult.CreateDate, time.Local)
+			item.ReportCreateTime = createTime
+			item.RaiReportId = articleResult.ArticleId
+			// 新增报告及章节
+			var reportId int64
+			allGrantUserList := make([]*report.ReportGrant, 0)
+			reportFreeLayoutList := make([]*report.ReportFreeLayout, 0)
+			reportId, err = models.AddReportAndChapter(item, reportFreeLayoutList, allGrantUserList, []models.AddReportChapter{})
+			if err != nil {
+				err = fmt.Errorf("新增报告及章节失败, Err: " + err.Error())
+				return
+			}
+			reportCode := utils.MD5(strconv.Itoa(int(reportId)))
+			item.ReportCode = reportCode
+
+			// 修改唯一编码
+			{
+				go models.ModifyReportCode(reportId, reportCode)
+			}
+
+			// 报告权限处理
+			go handleReportPermission(reportId, item.ClassifyIdSecond)
+
+			// 更新报告Es
+			_ = UpdateReportEs(int(reportId), 2)
+
+			// 报告发布成功后,需要将相关信息入知识库
+			go cache.RagEtaReportOpToCache(int(reportId), 0, `publish`)
+		}
+	}
+
+	return
+}
+
+// get公共请求方法
+func getRaiReportLib(url string) (body []byte, err error) {
+	if url == "" {
+		err = fmt.Errorf("url is empty")
+		return
+	}
+	if utils.RaiReportLibAuthorization == "" {
+		err = fmt.Errorf("authorization is empty")
+		return
+	}
+	defer func() {
+		if err != nil {
+			go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", url+"Get ErrMsg:"+err.Error(), utils.EmailSendToUsers)
+		}
+	}()
+	method := "GET"
+	client := &http.Client{}
+	req, err := http.NewRequest(method, url, nil)
+	if err != nil {
+		return
+	}
+	req.Header.Add("Authorization", utils.RaiReportLibAuthorization)
+	res, err := client.Do(req)
+	if err != nil {
+		return
+	}
+	defer res.Body.Close()
+	body, err = ioutil.ReadAll(res.Body)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// 导入固收研究的历史报告,前提是需要确认报告的分类都已经建好,并且已绑定固定收益品种
+func RaiReportInit() (err error) {
+	fmt.Println("开始导入固收研究的历史报告")
+	defer func() {
+		if err != nil {
+			msg := fmt.Sprintf("导入固收研究的历史报告失败, Err: %s", err.Error())
+			fmt.Println(msg)
+			go alarm_msg.SendAlarmMsg(msg, 2)
+		}
+	}()
+	// 分页调用接口获取历史报告
+	totalPage := 1
+	pageSize := 10
+	successCount := 0
+	failCount := 0
+	for i := 0; i <= totalPage; i++ {
+		pageindex := i * pageSize
+		body, er := getRaiReportLib(fmt.Sprintf("%s/articles/mp?take=%d&skip=%d&publish_status=2&industry_id=12", utils.RaiReportLibUrl, pageSize, pageindex))
+		if er != nil {
+			err = fmt.Errorf("获取权益报告失败, Err: %s", er.Error())
+			return
+		}
+		// 解析内容,获取真实的分页总数
+		var articleResultDate models.RaiArticleListResultApi
+		err = json.Unmarshal(body, &articleResultDate)
+		if err != nil {
+			err = fmt.Errorf("获取权益报告失败json解析失败, Err: %s", err.Error())
+			return
+		}
+		if articleResultDate.Code != 0 {
+			err = fmt.Errorf("获取权益报告失败, Err: %s", articleResultDate.Msg)
+			return
+		}
+		totalPage = articleResultDate.Pagination.PageTotal
+		articleResultList := articleResultDate.Data
+		for _, articleResult := range articleResultList {
+			err = handleInsertRaiReport(articleResult)
+			if err != nil {
+				fmt.Printf("导入固收研究的历史报告失败%d, Err: %s\n", articleResult.ArticleId, err.Error())
+				failCount++
+				continue
+			} else {
+				fmt.Printf("导入固收研究的历史报告成功%d\n", articleResult.ArticleId)
+				successCount++
+			}
+		}
+	}
+	fmt.Printf("导入固收研究的历史报告完成,成功%d,失败%d\n", successCount, failCount)
+	return nil
+}

+ 110 - 11
services/report_v2.go

@@ -4,6 +4,7 @@ import (
 	"archive/zip"
 	"encoding/json"
 	"errors"
+	"eta/eta_api/cache"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
 	"eta/eta_api/models/data_manage/excel"
@@ -65,6 +66,8 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 
 	errMsg = "生成报告失败"
 
+	reportFreeLayoutList := make([]*report.ReportFreeLayout, 0)
+
 	// 报告继承
 	if inheritReportId > 0 {
 		inheritReport, tmpErr := models.GetReportByReportId(inheritReportId)
@@ -101,6 +104,25 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 				reportInfo.EndImg = inheritReport.EndImg
 			}
 			reportInfo.InheritReportId = inheritReport.Id
+			pages, tmpErr := report.GetReportFreeLayoutListByReportId(inheritReport.Id, 0)
+			if tmpErr != nil {
+				errMsg = "获取自由布局内容页失败"
+				err = tmpErr
+				return
+			}
+			for _, v := range pages {
+				reportFreeLayoutList = append(reportFreeLayoutList, &report.ReportFreeLayout{
+					Id:              0,
+					ReportId:        0,
+					ReportChapterId: 0,
+					Page:            v.Page,
+					IsChapter:       v.IsChapter,
+					Content:         v.Content,
+					ContentStruct:   v.ContentStruct,
+					CreateTime:      time.Now(),
+					ModifyTime:      time.Now(),
+				})
+			}
 		}
 	}
 
@@ -109,7 +131,7 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 
 	// 新增报告及章节
 	var reportId int64
-	reportId, err = models.AddReportAndChapter(reportInfo, allGrantUserList, addChapterList)
+	reportId, err = models.AddReportAndChapter(reportInfo, reportFreeLayoutList, allGrantUserList, addChapterList)
 	if err != nil {
 		err = errors.New("新增报告及章节失败, Err: " + err.Error())
 		return
@@ -144,6 +166,7 @@ func AddReportAndChapter(reportInfo *models.Report, inheritReportId int, grantAd
 // @return errMsg string
 func EditReport(reportInfo *models.Report, req models.EditReq, sysUser *system.Admin) (err error, errMsg string) {
 	errMsg = `保存失败`
+	oldIsPublicPublish := reportInfo.IsPublicPublish
 	//var stage int
 	//if reportInfo.ClassifyNameFirst != req.ClassifyNameFirst || reportInfo.ClassifyNameSecond != req.ClassifyNameSecond {
 	//	// 报告期数
@@ -278,6 +301,26 @@ func EditReport(reportInfo *models.Report, req models.EditReq, sysUser *system.A
 		go handleReportPermission(int64(reportInfo.Id), minClassifyId)
 	}
 
+	// 更新es里的章节数据
+	if oldIsPublicPublish != reportInfo.IsPublicPublish {
+		if reportInfo.HasChapter == 1 {
+			// 晨周报
+			chapterList, tmpErr := models.GetPublishedChapterListByReportId(reportInfo.Id)
+			if tmpErr != nil {
+				return
+			}
+			if len(chapterList) > 0 {
+				// 更新章节的es数据
+				for _, chapterInfo := range chapterList {
+					err = updateReportChapterEsByChapter(chapterInfo, reportInfo.IsPublicPublish)
+					if err != nil {
+						return
+					}
+				}
+			}
+		}
+	}
+
 	return
 }
 
@@ -331,6 +374,24 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		return
 	}
 
+	// 每篇章节对应的分页内容页
+	chapterFreeLayoutListMap := make(map[int][]*report.ReportFreeLayout)
+	{
+		allReportFreeLayoutList, tmpErr := report.GetAllReportFreeLayoutListByReportId(inheritReportId)
+		if tmpErr != nil {
+			errMsg = "获取自由布局内容页失败"
+			err = tmpErr
+			return
+		}
+		for _, reportFreeLayout := range allReportFreeLayoutList {
+			chapterFreeLayoutList, ok := chapterFreeLayoutListMap[reportFreeLayout.ReportChapterId]
+			if !ok {
+				chapterFreeLayoutList = make([]*report.ReportFreeLayout, 0)
+			}
+			chapterFreeLayoutListMap[reportFreeLayout.ReportChapterId] = append(chapterFreeLayoutList, reportFreeLayout)
+		}
+	}
+
 	// 待添加的章节
 	chapterTypeList := make([]*models.ReportChapterType, 0)
 	// 待添加的章节类型id列表
@@ -474,20 +535,22 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		// 继承的报告章节内容
 		for i := 0; i < len(inheritReportChapters); i++ {
 			customChapter := inheritReportChapters[i]
+			// 继承的报告章节ID
+			inheritReportChapterId := customChapter.ReportChapterId
 
 			// 授权用户列表
-			tmpGrantList, ok := grantListMap[customChapter.ReportChapterId]
+			tmpGrantList, ok := grantListMap[inheritReportChapterId]
 			if !ok {
 				tmpGrantList = make([]*report.ReportChapterGrant, 0)
 			}
-			oldChapterIdGrantListMap[customChapter.ReportChapterId] = tmpGrantList
+			oldChapterIdGrantListMap[inheritReportChapterId] = tmpGrantList
 
 			// 关联品种列表
-			chapterPermissionList, ok := chapterPermissionListMap[customChapter.ReportChapterId]
+			chapterPermissionList, ok := chapterPermissionListMap[inheritReportChapterId]
 			if !ok {
 				chapterPermissionList = make([]*report.ReportChapterPermissionMapping, 0)
 			}
-			oldChapterPermissionListMap[customChapter.ReportChapterId] = chapterPermissionList
+			oldChapterPermissionListMap[inheritReportChapterId] = chapterPermissionList
 
 			// 判断该章节是否是系统章节,如果是的话,那就是需要额外创建的
 			if customChapter.TypeId > 0 {
@@ -509,9 +572,10 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 			customChapter.ContentModifyTime = time.Now()
 
 			customAddChapter := models.AddReportChapter{
-				ReportChapter:       customChapter,
-				GrantList:           tmpGrantList,
-				GrantPermissionList: chapterPermissionList,
+				ReportChapter:          customChapter,
+				GrantList:              tmpGrantList,
+				GrantPermissionList:    chapterPermissionList,
+				InheritReportChapterId: inheritReportChapterId,
 			}
 			customAddChapterList = append(customAddChapterList, customAddChapter)
 		}
@@ -520,6 +584,9 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 	// 最大排序
 	var maxSort int
 	for _, typeItem := range chapterTypeList {
+		// 继承的章节ID
+		inheritReportChapterId := 0
+
 		v, ok := inheritChapterMap[typeItem.ReportChapterTypeId]
 
 		// 章节授权用户
@@ -530,6 +597,8 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		chapterItem := new(models.ReportChapter)
 
 		if ok && v != nil {
+			inheritReportChapterId = v.ReportChapterId
+
 			// 如果存在继承的章节,那么就从继承的章节内容中获取
 			chapterItem.AddType = 2
 			chapterItem.Title = v.Title
@@ -574,6 +643,8 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 					return
 				}
 				if chapterNewest != nil {
+					inheritReportChapterId = chapterNewest.ReportChapterId
+
 					chapterItem.AddType = 2
 					chapterItem.Title = chapterNewest.Title
 					chapterItem.ReportType = chapterNewest.ReportType
@@ -669,9 +740,10 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		}
 
 		addChapter := models.AddReportChapter{
-			ReportChapter:       chapterItem,
-			GrantList:           tmpGrantList,
-			GrantPermissionList: tmpChapterPermissionList,
+			ReportChapter:          chapterItem,
+			GrantList:              tmpGrantList,
+			GrantPermissionList:    tmpChapterPermissionList,
+			InheritReportChapterId: inheritReportChapterId,
 		}
 
 		chapterList = append(chapterList, addChapter)
@@ -684,6 +756,27 @@ func getAddChapter(reportInfo *models.Report, minClassifyId, inheritReportId int
 		chapterList = append(chapterList, addChapterItem)
 	}
 
+	for k, chapterItem := range chapterList {
+		reportFreeLayoutList, ok := chapterFreeLayoutListMap[chapterItem.InheritReportChapterId]
+		if ok {
+			tmpReportFreeLayoutList := make([]*report.ReportFreeLayout, 0)
+			for _, v := range reportFreeLayoutList {
+				tmpReportFreeLayoutList = append(tmpReportFreeLayoutList, &report.ReportFreeLayout{
+					Id:              0,
+					ReportId:        0,
+					ReportChapterId: 0,
+					Page:            v.Page,
+					IsChapter:       v.IsChapter,
+					Content:         v.Content,
+					ContentStruct:   v.ContentStruct,
+					CreateTime:      time.Now(),
+					ModifyTime:      time.Now(),
+				})
+			}
+			chapterList[k].ReportChapterFreeLayoutList = tmpReportFreeLayoutList
+		}
+	}
+
 	//hasGrantUserMap := make(map[int]bool)
 	//for _, grantList := range typeGrantListMap {
 	//	for _, grant := range grantList {
@@ -1264,6 +1357,9 @@ func PublishReport(reportId int, reportUrl string, sysUser *system.Admin) (tips
 		go handleReportPermission(int64(reportInfo.Id), minClassifyId)
 	}
 
+	// 报告发布成功后,需要将相关信息入知识库
+	go cache.RagEtaReportOpToCache(reportInfo.Id, 0, `publish`)
+
 	return
 }
 
@@ -1382,6 +1478,9 @@ func PublishChapterReport(reportInfo *models.Report, reportUrl string, sysUser *
 		go Report2pdfAndJpeg(reportPdfUrl, reportId, 1)
 	}
 
+	// 报告发布成功后,需要将相关信息入知识库
+	go cache.RagEtaReportOpToCache(reportInfo.Id, 0, `publish`)
+
 	return
 }
 

+ 3 - 2
services/smart_report.go

@@ -152,7 +152,7 @@ async def main():
     page = await browser.newPage()
     await page.setViewport({
         'width': %d,
-        'height': 1080,
+        'height': 1697
     })
     await page.goto('%s', {
         'waitUntil': 'networkidle0',
@@ -164,6 +164,7 @@ async def main():
 
     await page.pdf({
 		'width': %d,
+        'height': 1697,
         'path': "%s",
         'printBackground': True,
         'margin': {
@@ -223,7 +224,7 @@ async def main():
         # 设置视口大小
         await page.setViewport({
             'width': %d,
-            'height': 1080
+            'height': 1697
         })
         
         # 导航到页面

+ 34 - 2
services/task.go

@@ -68,12 +68,18 @@ func Task() {
 
 	go llm.SaveAllChatRecordsToDB()
 
-	// 定时任务进行微信文章操作
-	go HandleWechatArticleOp()
+	// 定时任务进行微信公众号添加、文章刷新操作(暂不处理了,改成其他服务处理)
+	//go HandleWechatArticleOp()
 
 	// 定时任务进行微信文章LLM操作
 	go HandleWechatArticleLLmOp()
 
+	// 队列任务将eta报告同步到知识库操作
+	go HandleEtaReportKnowledgeLLmOp()
+
+	// 权益报告监听入库
+	go AutoInsertRaiReport()
+
 	// TODO:数据修复
 	//FixNewEs()
 	fmt.Println("task end")
@@ -645,3 +651,29 @@ func HandleWechatArticleLLmOp() {
 		})
 	}
 }
+
+// HandleEtaReportKnowledgeLLmOp
+// @Description: 处理eta报告加入知识库操作
+func HandleEtaReportKnowledgeLLmOp() {
+	defer func() {
+		if err := recover(); err != nil {
+			fmt.Println("[HandleEtaReportKnowledgeLLmOp]", err)
+		}
+	}()
+	for {
+		utils.Rc.Brpop(utils.CACHE_ETA_REPORT_KNOWLEDGE, func(b []byte) {
+			ragEtaReportOpOp := new(cache.RagEtaReportOpOp)
+			if err := json.Unmarshal(b, &ragEtaReportOpOp); err != nil {
+				fmt.Println("json unmarshal wrong!")
+				return
+			}
+			switch ragEtaReportOpOp.Source {
+			case `publish`:
+				ReportAddOrModifyKnowledgeByReportId(ragEtaReportOpOp.ReportId)
+			case `un_publish`:
+				ReportUnPublishedKnowledgeByReportId(ragEtaReportOpOp.ReportId)
+			}
+
+		})
+	}
+}

+ 70 - 10
services/wechat_platform.go

@@ -271,7 +271,7 @@ func GenerateArticleAbstract(item *rag.WechatArticle) {
 
 	// 生成临时文件
 	dateDir := time.Now().Format("20060102")
-	uploadDir := utils.STATIC_DIR + "ai/" + dateDir
+	uploadDir := "./static/ai/" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		err = fmt.Errorf("存储目录创建失败,Err:" + err.Error())
@@ -382,7 +382,7 @@ func ReGenerateArticleAbstract(item *rag.WechatArticle) {
 
 	// 生成临时文件
 	dateDir := time.Now().Format("20060102")
-	uploadDir := utils.STATIC_DIR + "ai/" + dateDir
+	uploadDir := "./static/ai/" + dateDir
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		err = fmt.Errorf("存储目录创建失败,Err:" + err.Error())
@@ -534,7 +534,7 @@ func getAnswerByContent(wechatArticleId int, docId string) (answer string, addAr
 	addArticleChatRecordList = make([]*rag.WechatArticleChatRecord, 0)
 
 	questionObj := rag.Question{}
-	questionList, err := questionObj.GetListByCondition(``, []interface{}{}, 0, 100)
+	questionList, err := questionObj.GetListByCondition(``, ``, []interface{}{}, 0, 100)
 	if err != nil {
 		err = fmt.Errorf("获取问题列表失败,Err:" + err.Error())
 		return
@@ -610,14 +610,15 @@ func ArticleToKnowledge(item *rag.WechatArticle) {
 
 	// 生成临时文件
 	//dateDir := time.Now().Format("20060102")
-	//uploadDir := utils.STATIC_DIR + "ai/article/" + dateDir
-	uploadDir := utils.STATIC_DIR + "ai/article"
+	//uploadDir :=   "./static/ai/article/" + dateDir
+	uploadDir := "./static/ai/article"
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		err = fmt.Errorf("存储目录创建失败,Err:" + err.Error())
 		return
 	}
-	fileName := utils.RemoveSpecialChars(item.Title) + `.md`
+	//fileName := utils.RemoveSpecialChars(item.Title) + `.md`
+	fileName := utils.MD5(item.Title) + `.md`
 	tmpFilePath := uploadDir + "/" + fileName
 	err = utils.SaveToFile(item.TextContent, tmpFilePath)
 	if err != nil {
@@ -675,8 +676,8 @@ func AbstractToKnowledge(wechatArticleItem *rag.WechatArticle, abstractItem *rag
 
 	// 生成临时文件
 	//dateDir := time.Now().Format("20060102")
-	//uploadDir := utils.STATIC_DIR + "ai/article/" + dateDir
-	uploadDir := utils.STATIC_DIR + "ai/abstract"
+	//uploadDir :=  + "./static/ai/article/" + dateDir
+	uploadDir := "./static/ai/abstract"
 	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		err = fmt.Errorf("存储目录创建失败,Err:" + err.Error())
@@ -1014,8 +1015,8 @@ func AddOrEditEsWechatArticleAbstract(articleAbstractId int) {
 	err = elastic.WechatArticleAbstractEsAddOrEdit(strconv.Itoa(articleAbstractId), esItem)
 }
 
-// AddOrEditEsWechatArticleAbstract
-// @Description: 新增/编辑微信文章摘要入ES
+// DelEsWechatArticleAbstract
+// @Description: 删除ES中的微信文章摘要
 // @author: Roc
 // @datetime 2025-03-13 14:13:47
 // @param articleAbstractId int
@@ -1034,3 +1035,62 @@ func DelEsWechatArticleAbstract(articleAbstractId int) {
 
 	err = elastic.WechatArticleAbstractEsDel(strconv.Itoa(articleAbstractId))
 }
+
+// AddOrEditEsRagQuestion
+// @Description: 新增/编辑知识库问题入ES
+// @author: Roc
+// @datetime 2025-03-28 11:25:50
+// @param questionId int
+func AddOrEditEsRagQuestion(questionId int) {
+	if utils.EsWechatArticleName == `` {
+		return
+	}
+
+	var err error
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("添加公众号微信信息到ES失败,err:%v", err)
+			fmt.Println("添加公众号微信信息到ES失败,err:", err)
+		}
+	}()
+	obj := rag.Question{}
+	questionInfo, err := obj.GetByID(questionId)
+	if err != nil {
+		err = fmt.Errorf("获取公众号文章信息失败,Err:" + err.Error())
+		return
+	}
+
+	esItem := elastic.RagQuestionItem{
+		QuestionId:      questionInfo.QuestionId,
+		QuestionTitle:   questionInfo.QuestionTitle,
+		QuestionContent: questionInfo.QuestionContent,
+		Sort:            questionInfo.Sort,
+		SysUserId:       questionInfo.SysUserId,
+		SysUserRealName: questionInfo.SysUserRealName,
+		ModifyTime:      questionInfo.ModifyTime,
+		CreateTime:      questionInfo.CreateTime,
+	}
+
+	err = elastic.RagQuestionEsAddOrEdit(strconv.Itoa(questionInfo.QuestionId), esItem)
+}
+
+// DelEsRagQuestion
+// @Description: 删除ES中的知识库问题
+// @author: Roc
+// @datetime 2025-03-28 11:26:40
+// @param questionId int
+func DelEsRagQuestion(questionId int) {
+	if utils.EsWechatArticleAbstractName == `` {
+		return
+	}
+
+	var err error
+	defer func() {
+		if err != nil {
+			utils.FileLog.Error("添加公众号微信信息到ES失败,err:%v", err)
+			fmt.Println("添加公众号微信信息到ES失败,err:", err)
+		}
+	}()
+
+	err = elastic.RagQuestionEsDel(strconv.Itoa(questionId))
+}

ファイルの差分が大きいため隠しています
+ 0 - 0
static/imgs/ai/article/【专题报告】关税来袭黑色怎么看.md


ファイルの差分が大きいため隠しています
+ 0 - 0
static/imgs/ai/article/【东海首席】黑色金属专题报告新一轮钢铁供给侧改革能否全面开启.md


ファイルの差分が大きいため隠しています
+ 0 - 0
static/imgs/ai/article/【开源宏观】财政支出力度如何12月财政数据点评.md


ファイルの差分が大きいため隠しています
+ 0 - 0
static/imgs/ai/article/巴菲特2025股东信1000字精华版来了附全文.md


+ 16 - 0
utils/common.go

@@ -3085,3 +3085,19 @@ func ConvertNumToBase62[T TUint](num T) string {
 
 	return string(result)
 }
+
+// GetFirstNChars
+// @Description: 截取字符串的前N个字符
+// @author: Roc
+// @datetime 2025-03-28 10:17:23
+// @param s string
+// @param max int
+// @return string
+func GetFirstNChars(s string, max int) string {
+	s = strings.NewReplacer("\r", "", "\n", "").Replace(strings.TrimSpace(s))
+	runes := []rune(s)
+	if len(runes) < max {
+		max = len(runes)
+	}
+	return string(runes[:max])
+}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません