ソースを参照

Merge branch 'master' into feature/eta2.5.9_api_stat_websocket

# Conflicts:
#	services/websocket_msg.go
xyxie 3 日 前
コミット
921aef6ecf
100 ファイル変更4950 行追加929 行削除
  1. 4 2
      .gitignore
  2. 84 8
      cache/llm.go
  3. 2 0
      controllers/bi_dashboard.go
  4. 31 2
      controllers/data_manage/ai_predict_model/classify.go
  5. 147 1
      controllers/data_manage/ai_predict_model/index.go
  6. 132 0
      controllers/data_manage/chart_common.go
  7. 113 108
      controllers/data_manage/chart_info.go
  8. 3 3
      controllers/data_manage/data_manage_permission/data_manage_permission.go
  9. 3 3
      controllers/data_manage/data_manage_permission/data_move.go
  10. 1 1
      controllers/data_manage/data_manage_permission/message.go
  11. 1 1
      controllers/data_manage/edb_classify.go
  12. 93 10
      controllers/data_manage/edb_info.go
  13. 30 12
      controllers/data_manage/edb_info_refresh.go
  14. 1 1
      controllers/data_manage/edb_info_relation.go
  15. 6 0
      controllers/data_manage/excel/excel_classify.go
  16. 79 36
      controllers/data_manage/excel/excel_info.go
  17. 75 41
      controllers/data_manage/future_good/future_good_chart_info.go
  18. 9 5
      controllers/data_manage/gpr_risk_data.go
  19. 39 34
      controllers/data_manage/mysteel_chemical_data.go
  20. 1 1
      controllers/data_manage/predict_edb_classify.go
  21. 16 1
      controllers/data_manage/predict_edb_info.go
  22. 11 5
      controllers/data_manage/purang_data.go
  23. 1 0
      controllers/eta_forum/eta_forum.go
  24. 24 3
      controllers/fe_calendar/fe_calendar_matter.go
  25. 206 212
      controllers/llm/abstract.go
  26. 47 0
      controllers/llm/kb_controller.go
  27. 6 1
      controllers/llm/llm_http/response.go
  28. 367 27
      controllers/llm/question.go
  29. 422 0
      controllers/llm/rag_eta_report_abstract.go
  30. 85 1
      controllers/llm/report.go
  31. 0 2
      controllers/llm/user_chat_controller.go
  32. 84 0
      controllers/llm/wechat_platform.go
  33. 13 4
      controllers/material/material.go
  34. 104 17
      controllers/report_chapter.go
  35. 199 48
      controllers/report_v2.go
  36. 6 1
      controllers/sandbox/sandbox.go
  37. 4 1
      controllers/sys_admin.go
  38. 8 1
      controllers/sys_role.go
  39. 18 0
      controllers/sys_user.go
  40. 14 62
      controllers/user_login.go
  41. 71 9
      models/ai_predict_model/ai_predict_model_index.go
  42. 1 0
      models/bi_dashboard/bi_dashboard.go
  43. 1 0
      models/bi_dashboard/bi_dashboard_detail.go
  44. 5 0
      models/business_conf.go
  45. 7 0
      models/chart_permission.go
  46. 12 12
      models/data_manage/base_from_gpr_risk.go
  47. 55 55
      models/data_manage/base_from_purang.go
  48. 6 0
      models/data_manage/chart_edb_mapping.go
  49. 39 0
      models/data_manage/chart_info.go
  50. 1 1
      models/data_manage/data_manage_permission/classify_no_auth_record.go
  51. 1 1
      models/data_manage/data_manage_permission/move.go
  52. 1 1
      models/data_manage/data_manage_permission/move_record.go
  53. 1 1
      models/data_manage/data_manage_permission/no_auth_record.go
  54. 2 2
      models/data_manage/edb_data_mysteel_chemical.go
  55. 7 5
      models/data_manage/edb_info.go
  56. 4 4
      models/data_manage/edb_info_relation.go
  57. 7 0
      models/data_manage/excel/excel_info.go
  58. 4 4
      models/data_manage/mysteel_chemical_classify.go
  59. 38 29
      models/data_manage/mysteel_chemical_index.go
  60. 7 6
      models/data_manage/request/mysteel_chemical_data.go
  61. 1 1
      models/db.go
  62. 4 0
      models/manual_edb.go
  63. 5 2
      models/material/material_classify.go
  64. 164 0
      models/rag/ai_task.go
  65. 115 0
      models/rag/ai_task_record.go
  66. 114 0
      models/rag/article_abstract_history.go
  67. 19 4
      models/rag/question.go
  68. 86 0
      models/rag/question_history.go
  69. 5 5
      models/rag/rag_eta_report.go
  70. 292 0
      models/rag/rag_eta_report_abstract.go
  71. 9 0
      models/rag/request/rag_eta_report.go
  72. 1 1
      models/rag/request/wechat_platform.go
  73. 15 0
      models/rag/response/abstract.go
  74. 5 0
      models/rag/response/question.go
  75. 23 1
      models/rag/tag.go
  76. 82 12
      models/rag/wechat_article_abstract.go
  77. 63 6
      models/report.go
  78. 266 0
      models/report/report_free_layout.go
  79. 19 11
      models/report_chapter.go
  80. 26 2
      models/report_v2.go
  81. 1 1
      models/sandbox/sandbox_classify.go
  82. 2 2
      models/system/sys_role.go
  83. 1 1
      models/system/sys_user.go
  84. 1 1
      models/target.go
  85. 108 0
      routers/commentsRouter.go
  86. 1 0
      routers/router.go
  87. 5 5
      services/ai_predict_model_classify.go
  88. 179 4
      services/ai_predict_model_index.go
  89. 1 0
      services/crm_eta.go
  90. 91 8
      services/data/chart_info.go
  91. 14 14
      services/data/data_manage_permission/data_move.go
  92. 1 1
      services/data/data_manage_permission/message.go
  93. 4 2
      services/data/edb_classify.go
  94. 9 9
      services/data/edb_info.go
  95. 46 1
      services/data/edb_info_relation.go
  96. 1 1
      services/data/excel/excel_info.go
  97. 15 15
      services/data/mysteel_chemical.go
  98. 37 30
      services/data/predict_edb_info_rule.go
  99. 43 4
      services/elastic/elastic.go
  100. 317 0
      services/elastic/rag_eta_report_abstract.go

+ 4 - 2
.gitignore

@@ -3,7 +3,6 @@
 /.idea
 /routers/.DS_Store
 /rdlucklog
-/etalogs
 /conf/*.conf
 /binlog/*
 /*.pdf
@@ -18,5 +17,8 @@
 eta_api.exe
 eta_api.exe~
 /static/tmpFile/*
+/static/imgs/*
+/static/ai/*
 etalogs/
-/.vscode
+/.vscode
+/fix

+ 84 - 8
cache/llm.go

@@ -5,13 +5,15 @@ import (
 	"fmt"
 )
 
-type WechatArticleOp struct {
+// WechatPlatformOp
+// @Description: 微信公众号操作请求
+type WechatPlatformOp struct {
 	Source           string
 	WechatPlatformId int
 }
 
 // AddWechatArticleOpToCache
-// @Description: 将公众号文章操作加入缓存
+// @Description: 将公众号操作加入缓存
 // @param wechatPlatformId
 // @param source
 // @return bool
@@ -21,7 +23,7 @@ func AddWechatArticleOpToCache(wechatPlatformId int, source string) bool {
 		return true
 	}
 
-	record := new(WechatArticleOp)
+	record := new(WechatPlatformOp)
 	record.Source = source
 	record.WechatPlatformId = wechatPlatformId
 	if utils.Re == nil {
@@ -36,23 +38,32 @@ func AddWechatArticleOpToCache(wechatPlatformId int, source string) bool {
 	return false
 }
 
+// WechatArticleOp
+// @Description: 微信公众号文章操作
+type WechatArticleOp struct {
+	Source          string
+	WechatArticleId int
+	QuestionId      int
+}
+
 // AddWechatArticleLlmOpToCache
 // @Description: 将公众号文章llm操作加入缓存
 // @param wechatPlatformId
 // @param source
 // @return bool
-func AddWechatArticleLlmOpToCache(wechatPlatformId int, source string) bool {
+func AddWechatArticleLlmOpToCache(wechatArticleId, questionId 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
+	record.WechatArticleId = wechatArticleId
+	record.QuestionId = questionId
 	if utils.Re == nil {
 		err := utils.Rc.LPush(utils.CACHE_WECHAT_PLATFORM_ARTICLE_KNOWLEDGE, record)
 
-		utils.FileLog.Info(fmt.Sprintf("将公众号文章llm操作加入缓存 加入缓存 AddWechatArticleLlmOpToCache LPush: 操作类型:%s,公众号id:%d", source, wechatPlatformId))
+		utils.FileLog.Info(fmt.Sprintf("将公众号文章llm操作加入缓存 加入缓存 AddWechatArticleLlmOpToCache LPush: 操作类型:%s,公众号稳扎id:%d", source, wechatArticleId))
 		if err != nil {
 			fmt.Println("AddWechatArticleOpToCache LPush Err:" + err.Error())
 		}
@@ -61,7 +72,7 @@ func AddWechatArticleLlmOpToCache(wechatPlatformId int, source string) bool {
 	return false
 }
 
-type RagEtaReportOpOp struct {
+type RagEtaReportOp struct {
 	Source          string
 	ReportId        int
 	ReportChapterId int
@@ -76,7 +87,7 @@ type RagEtaReportOpOp struct {
 // @param source string
 // @return bool
 func RagEtaReportOpToCache(reportId, reportChapterId int, source string) bool {
-	record := new(RagEtaReportOpOp)
+	record := new(RagEtaReportOp)
 	record.Source = source
 	record.ReportId = reportId
 	record.ReportChapterId = reportChapterId
@@ -91,3 +102,68 @@ func RagEtaReportOpToCache(reportId, reportChapterId int, source string) bool {
 	}
 	return false
 }
+
+// RagEtaReportLlmOp
+// @Description:
+type RagEtaReportLlmOp struct {
+	RagEtaReportId int
+	QuestionId     int
+	ForceGenerate  bool
+}
+
+// AddRagEtaReportLlmOpToCache
+// @Description: 将ETA报告llm操作加入缓存
+// @author: Roc
+// @datetime 2025-04-24 13:59:16
+// @param ragEtaReportId int
+// @param questionId int
+// @return bool
+func AddRagEtaReportLlmOpToCache(ragEtaReportId, questionId int, forceGenerate bool) bool {
+	// 如果不在发布和调试模式,那么就不加入缓存
+	if !utils.InArrayByStr([]string{utils.BusinessCodeRelease, utils.BusinessCodeDebug}, utils.BusinessCode) {
+		return true
+	}
+	record := new(RagEtaReportLlmOp)
+	record.RagEtaReportId = ragEtaReportId
+	record.QuestionId = questionId
+	record.ForceGenerate = forceGenerate
+	if utils.Re != nil {
+		return false
+	}
+
+	err := utils.Rc.LPush(utils.CACHE_ETA_REPORT_KNOWLEDGE_LLM, record)
+	utils.FileLog.Info(fmt.Sprintf("将eta报告llm操作加入缓存 加入缓存 RagEtaReportLlmOpToCache LPush: ETA报告id:%d", ragEtaReportId))
+	if err != nil {
+		fmt.Println("RagEtaReportLlmOpToCache LPush Err:" + err.Error())
+	}
+	return true
+}
+
+type AiTaskRecordOp struct {
+	AiTaskRecordId int
+}
+
+// AddAiTaskRecordOpToCache
+// @Description: AI任务操作调度入队列
+// @author: Roc
+// @datetime 2025-04-24 09:41:11
+// @param aiTaskRecordId int
+// @return bool
+func AddAiTaskRecordOpToCache(aiTaskRecordId int) bool {
+	// 如果不在发布和调试模式,那么就不加入缓存
+	if !utils.InArrayByStr([]string{utils.BusinessCodeRelease, utils.BusinessCodeDebug}, utils.BusinessCode) {
+		return true
+	}
+	record := new(AiTaskRecordOp)
+	record.AiTaskRecordId = aiTaskRecordId
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_AI_ARTICLE_ABSTRACT_LLM_TASK, record)
+
+		utils.FileLog.Info(fmt.Sprintf("将AI任务操作调度入队列 加入缓存 AddAiTaskRecordOpToCache LPush: 记录id:%d", aiTaskRecordId))
+		if err != nil {
+			fmt.Println("AddAiTaskRecordOpToCache LPush Err:" + err.Error())
+		}
+		return true
+	}
+	return false
+}

+ 2 - 0
controllers/bi_dashboard.go

@@ -103,6 +103,7 @@ func (this *BIDaShboardController) AddDashboard() {
 			BiDashboardId: int(id),
 			Type:          v.Type,
 			UniqueCode:    v.UniqueCode,
+			Conf:          v.Conf,
 			Sort:          i + 1,
 			CreateTime:    time.Now(),
 			ModifyTime:    time.Now(),
@@ -176,6 +177,7 @@ func (this *BIDaShboardController) EditDashboard() {
 			BiDashboardId: req.BiDashboardId,
 			Type:          v.Type,
 			UniqueCode:    v.UniqueCode,
+			Conf:          v.Conf,
 			Sort:          v.Sort,
 			CreateTime:    time.Now(),
 			ModifyTime:    time.Now(),

+ 31 - 2
controllers/data_manage/ai_predict_model/classify.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/data_manage"
 	dataSourceModel "eta/eta_api/models/data_source"
 	"eta/eta_api/services"
+	"eta/eta_api/services/data"
 	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
@@ -444,15 +445,36 @@ func (this *AiPredictModelClassifyController) Remove() {
 			br.ErrMsg = fmt.Sprintf("获取标的信息失败, %v", e)
 			return
 		}
+		if aiIndex != nil && aiIndex.AiPredictModelIndexId <= 0 {
+			br.Ret = 200
+			br.Msg = "删除成功"
+			br.Success = true
+			return
+		}
+
+		// 获取指标图表
+		var chartIds []int
+		chartTypes := []int{utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY, utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY}
+		for _, v := range chartTypes {
+			ct, e := data_manage.GetAiPredictChartInfoByIndexId(v, req.IndexId)
+			if e != nil && !utils.IsErrNoRow(e) {
+				br.Msg = "操作失败"
+				br.ErrMsg = fmt.Sprintf("获取标的图表信息失败, %v", e)
+				return
+			}
+			if ct != nil && ct.ChartInfoId > 0 {
+				chartIds = append(chartIds, ct.ChartInfoId)
+			}
+		}
 
 		// 删除标的及数据
-		if e = indexOb.RemoveIndexAndData(req.IndexId); e != nil {
+		if e = indexOb.RemoveIndexAndData(req.IndexId, chartIds); e != nil {
 			br.Msg = "操作失败"
 			br.ErrMsg = fmt.Sprintf("删除标的及数据失败, %v", e)
 			return
 		}
 
-		// ES标记删除
+		// ES标记标的/图表删除
 		go func() {
 			indexItem := new(dataSourceModel.SearchDataSource)
 			indexItem.PrimaryId = aiIndex.AiPredictModelIndexId
@@ -470,6 +492,13 @@ func (this *AiPredictModelClassifyController) Remove() {
 				utils.FileLog.Info("AI预测模型-标记删除es失败, %v", e)
 				return
 			}
+
+			if len(chartIds) == 0 {
+				return
+			}
+			for _, v := range chartIds {
+				data.EsDeleteChartInfo(v)
+			}
 		}()
 	}
 

+ 147 - 1
controllers/data_manage/ai_predict_model/index.go

@@ -5,9 +5,11 @@ import (
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	aiPredictModel "eta/eta_api/models/ai_predict_model"
+	"eta/eta_api/models/data_manage"
 	dataSourceModel "eta/eta_api/models/data_source"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
+	"eta/eta_api/services/data"
 	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
@@ -583,7 +585,7 @@ func (this *AiPredictModelIndexController) Import() {
 	}
 
 	// 导入指标
-	if e = services.ImportAiPredictModelIndexAndData(importIndexes); e != nil {
+	if e = services.ImportAiPredictModelIndexAndData(importIndexes, sysUser.AdminId, sysUser.RealName); e != nil {
 		br.Msg = "操作失败"
 		br.ErrMsg = fmt.Sprintf("导入指标数据失败, %v", e)
 		return
@@ -984,3 +986,147 @@ func (this *AiPredictModelIndexController) DashboardDetail() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// SearchByEs
+// @Title 图表模糊搜索(从es获取)
+// @Description  图表模糊搜索(从es获取)
+// @Param   Keyword   query   string  true       "图表名称"
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Param   Source   query   int  true       "来源,14:日度预测,15:月度预测,默认0:全部14+15"
+// @Success 200 {object} data_manage.ChartInfo
+// @router /chart/search_by_es [get]
+func (this *AiPredictModelIndexController) SearchByEs() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	keyword := this.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
+	if keyword == "" {
+		keyword = this.GetString("KeyWord")
+		keyword = strings.TrimSpace(keyword)
+	}
+
+	//只看我的
+	isShowMe, _ := this.GetBool("IsShowMe")
+	showSysId := 0
+	if isShowMe {
+		showSysId = sysUser.AdminId
+	}
+
+	source, _ := this.GetInt("Source")
+	sourceList := make([]int, 0)
+	if source <= 0 {
+		sourceList = append(sourceList, utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY, utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY)
+	} else {
+		sourceList = append(sourceList, source)
+	}
+
+	var searchList []*data_manage.ChartInfoMore
+	var total int64
+	var err error
+
+	// 获取当前账号的不可见指标(AI预测的指标为标的均可见)
+	noPermissionChartIdList := make([]int, 0)
+	//{
+	//	obj := data_manage.EdbInfoNoPermissionAdmin{}
+	//	confList, err := obj.GetAllChartListByAdminId(this.SysUser.AdminId)
+	//	if err != nil && !utils.IsErrNoRow(err) {
+	//		br.Msg = "获取失败"
+	//		br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
+	//		return
+	//	}
+	//	for _, v := range confList {
+	//		noPermissionChartIdList = append(noPermissionChartIdList, v.ChartInfoId)
+	//	}
+	//}
+
+	if keyword != "" {
+		searchList, total, err = data.EsSearchChartInfo(keyword, showSysId, sourceList, noPermissionChartIdList, startSize, pageSize)
+	} else {
+		total, searchList, err = data_manage.ChartInfoSearchByEmptyKeyWord(showSysId, sourceList, noPermissionChartIdList, startSize, pageSize)
+		if err != nil && !utils.IsErrNoRow(err) {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	finalList := make([]*data_manage.ChartInfoMore, 0)
+	if len(searchList) > 0 {
+		chartInfoIds := ""
+		chartEdbMap := make(map[int][]*data_manage.ChartEdbInfoMapping)
+		for _, v := range searchList {
+			chartInfoIds += strconv.Itoa(v.ChartInfoId) + ","
+		}
+		if chartInfoIds != "" {
+			chartInfoIds = strings.Trim(chartInfoIds, ",")
+			//判断是否需要展示英文标识
+			edbList, e := data_manage.GetChartEdbMappingListByChartInfoIds(chartInfoIds)
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取图表,指标信息失败,Err:" + e.Error()
+				return
+			}
+			for _, v := range edbList {
+				chartEdbMap[v.ChartInfoId] = append(chartEdbMap[v.ChartInfoId], v)
+			}
+		}
+
+		for _, v := range searchList {
+			tmp := new(data_manage.ChartInfoMore)
+			tmp.ChartInfo = v.ChartInfo
+			// 图表数据权限
+			tmp.HaveOperaAuth = true
+			//判断是否需要展示英文标识
+			if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
+				tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
+			}
+			tmp.SearchText = v.SearchText
+			if tmp.SearchText == "" {
+				tmp.SearchText = v.ChartName
+			}
+			finalList = append(finalList, tmp)
+		}
+	}
+	//新增搜索词记录
+	{
+		searchKeyword := new(data_manage.SearchKeyword)
+		searchKeyword.KeyWord = keyword
+		searchKeyword.CreateTime = time.Now()
+		go data_manage.AddSearchKeyword(searchKeyword)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, int(total))
+	resp := data_manage.ChartInfoListByEsResp{
+		Paging: page,
+		List:   finalList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 132 - 0
controllers/data_manage/chart_common.go

@@ -9,6 +9,7 @@ import (
 	"eta/eta_api/controllers/data_manage/line_feature"
 	"eta/eta_api/controllers/data_manage/range_analysis"
 	"eta/eta_api/models"
+	aiPredictModel "eta/eta_api/models/ai_predict_model"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
@@ -290,6 +291,29 @@ func (this *ChartInfoController) CommonChartInfoDetailFromUniqueCode() {
 		br.Success = true
 		br.Msg = "获取成功"
 		br.Data = resp
+	case utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY, utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY:
+		resp, isOk, msg, errMsg := GetAiPredictChartInfoDetailFromUniqueCode(chartInfo, isCache)
+		if !isOk {
+			if strings.Contains(errMsg, utils.ErrNoRow()) {
+				endInfoList := make([]*data_manage.ChartEdbInfoMapping, 0)
+				resp.EdbInfoList = endInfoList
+				resp.ChartInfo = chartInfo
+				resp.Status = false
+
+				br.Data = resp
+				br.Ret = 200
+				br.Success = true
+				br.Msg = "获取成功"
+				return
+			}
+			br.Msg = msg
+			br.ErrMsg = errMsg
+			return
+		}
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
 	default:
 		br.Msg = "错误的图表"
 		br.ErrMsg = "错误的图表"
@@ -408,6 +432,114 @@ func (this *ChartInfoController) GeneralChartToken() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = token
+	return
+}
 
+// GetAiPredictChartInfoDetailFromUniqueCode 根据编码获取AI预测模型图表详情
+func GetAiPredictChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCache bool) (resp *data_manage.ChartInfoDetailFromUniqueCodeResp, isOk bool, msg, errMsg string) {
+	var err error
+	msg = "获取成功"
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("UniqueCode获取图表详情失败, %v", err)
+			msg = "获取失败"
+			errMsg = fmt.Sprintf(tips)
+			utils.FileLog.Info(tips)
+		}
+	}()
+	if chartInfo == nil {
+		err = fmt.Errorf("图表信息不存在")
+		return
+	}
+	if chartInfo.Source != utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY && chartInfo.Source != utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY {
+		err = fmt.Errorf("图表来源有误, Source: %d", chartInfo.Source)
+		return
+	}
+	resp = new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+
+	// 获取图表标的
+	edbMappings, e := data_manage.GetChartEdbMappingsByChartInfoId(chartInfo.ChartInfoId)
+	if e != nil {
+		err = fmt.Errorf("获取图表指标关联失败, %v", e)
+		return
+	}
+	if len(edbMappings) == 0 {
+		err = fmt.Errorf("图表指标关联不存在, %v", e)
+		return
+	}
+	indexId := edbMappings[0].EdbInfoId
+	if indexId <= 0 {
+		err = fmt.Errorf("图表标的有误")
+		return
+	}
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	indexItem, e := indexOb.GetItemById(indexId)
+	if e != nil {
+		err = fmt.Errorf("获取图表标的失败, %v", e)
+		return
+	}
+	if indexItem != nil && indexItem.AiPredictModelIndexId <= 0 {
+		err = fmt.Errorf("图表标的不存在, IndexId: %d", indexId)
+		return
+	}
+
+	// 获取标的数据
+	indexData := make([]*aiPredictModel.AiPredictModelData, 0)
+	dataSource := aiPredictModel.ModelDataSourceDaily
+	if chartInfo.Source == utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY {
+		dataSource = aiPredictModel.ModelDataSourceMonthly
+	}
+	dataOb := new(aiPredictModel.AiPredictModelData)
+	dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
+	dataPars := make([]interface{}, 0)
+	dataPars = append(dataPars, indexItem.IndexCode)
+	list, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
+	if e != nil {
+		err = fmt.Errorf("获取标的数据失败, %v", e)
+		return
+	}
+	for _, v := range list {
+		if v.Source == dataSource {
+			indexData = append(indexData, v)
+			continue
+		}
+	}
+
+	//判断是否存在缓存,如果存在缓存,那么直接从缓存中获取
+	key := data.GetChartInfoDataKey(chartInfo.ChartInfoId)
+	if utils.Re == nil && isCache {
+		if utils.Re == nil && utils.Rc.IsExist(key) {
+			if chartData, e := utils.Rc.RedisBytes(key); e == nil {
+				e = json.Unmarshal(chartData, &resp)
+				if e != nil || resp == nil {
+					return
+				}
+				isOk = true
+				return
+			}
+		}
+	}
+
+	// 图表详情
+	chartDetail, e := services.GetAiPredictChartDetailByData(indexItem, indexData, dataSource)
+	if e != nil {
+		err = fmt.Errorf("获取图表详情失败, %v", e)
+		return
+	}
+	resp.ChartInfo = chartDetail.ChartInfo
+	resp.EdbInfoList = chartDetail.EdbInfoList
+	resp.XEdbIdValue = chartDetail.XEdbIdValue
+	resp.YDataList = chartDetail.YDataList
+	resp.XDataList = chartDetail.XDataList
+	resp.BarChartInfo = chartDetail.BarChartInfo
+	resp.CorrelationChartInfo = chartDetail.CorrelationChartInfo
+	resp.DataResp = chartDetail.DataResp
+	resp.Status = true
+
+	if utils.Re == nil {
+		jsonData, _ := json.Marshal(resp)
+		_ = utils.Rc.Put(key, jsonData, 10*time.Minute)
+	}
+	isOk = true
 	return
 }

+ 113 - 108
controllers/data_manage/chart_info.go

@@ -20,6 +20,7 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -876,18 +877,18 @@ func (this *ChartInfoController) ChartInfoMove() {
 		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 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 {
@@ -1455,47 +1456,19 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 			br.ErrMsg = "标识线配置异常" + err.Error()
 			return
 		}
-		for i := range markerLines {
-			if markerLines[i].EdbType == 0 && markerLines[i].TimeIntervalType == 0 && markerLines[i].Axis != 3 {
-				// 图上第一个指标且时间区间跟随图表
-				if markerLines[i].MarkLineType == 2 {
-					if edbList[0].IsAxis == 1 {
-						value, err := data.MarkerLineCalculate(markerLines[i], edbList[0].DataList, chartInfo)
-						if err != nil {
-							br.Msg = "标识线配置异常"
-							br.ErrMsg = "标识线配置异常" + err.Error()
-							return
-						}
-						markerLines[i].Value = value
-					} else {
-						// 其他的都走指标计算
-						edbInfo, err := data_manage.GetEdbInfoById(markerLines[i].EdbInfoId)
-						if err != nil {
-							br.Msg = "指标计算标识线获取指标信息异常"
-							br.ErrMsg = "指标计算标识线获取指标信息异常" + err.Error()
-							return
-						}
-						// 判断时间区间不为跟随图表的情况
-						if markerLines[i].TimeIntervalType != 0 {
-							startDate = markerLines[i].StartDate.Date
-							endDate = markerLines[i].EndDate.Date
-						}
-						dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, startDate, endDate)
-						if err != nil {
-							br.Msg = "指标计算标识线获取指标数据异常"
-							br.ErrMsg = "指标计算标识线获取指标数据异常" + err.Error()
-							return
-						}
-						value, err := data.MarkerLineCalculate(markerLines[i], dataList, chartInfo)
-						if err != nil {
-							br.Msg = "标识线配置异常"
-							br.ErrMsg = "标识线配置异常" + err.Error()
-							return
-						}
-						markerLines[i].Value = value
-					}
+		for i, markerLine := range markerLines {
+			switch markerLine.MarkLineType { //1:固定 2:指标计算
+			case 2:
+				tmpMarkerLine, tmpErr := data.GetMarkerLine(markerLine, edbList, chartInfo, startDate, endDate)
+				if tmpErr != nil {
+					br.Msg = "标识线配置异常"
+					br.ErrMsg = "标识线配置异常," + tmpErr.Error()
+					return
 				}
+				markerLine = tmpMarkerLine
 			}
+
+			markerLines[i] = markerLine
 		}
 
 		markerLineStr, err := json.Marshal(markerLines)
@@ -1833,7 +1806,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 	}
 
 	// 图表当前分类的分类树
-	
+
 	classifyLevels := make([]string, 0)
 	{
 		list, e := data_manage.GetChartClassifyAllBySource(utils.CHART_SOURCE_DEFAULT)
@@ -2988,58 +2961,78 @@ func (this *ChartInfoController) ChartInfoBase64Upload() {
 			return
 		}
 	}
-
+	NotBackendGenerate, _ := this.GetBool("NotBackendGenerate", false)
+	if NotBackendGenerate {
+		b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imgData)
+		if !b {
+			br.Msg = "图片格式不正确"
+			br.ErrMsg = "图片格式不正确"
+			return
+		}
+		re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
+		base64Str := re.ReplaceAllString(imgData, "")
+		base64Str = strings.Replace(base64Str, " ", "", -1)
+		imgData = base64Str
+	}
 	//var saveToOssPath string
 	randStr := utils.GetRandStringNoSpecialChar(28)
 	var fileName, outFileName string
 	fileName = randStr + ".txt"
-	fileName = uploadDir + fileName
-	err := utils.SaveToFile(imgData, fileName)
-	if err != nil {
-		br.Msg = "图片保存失败"
-		br.ErrMsg = "图片保存失败,Err:" + err.Error()
-		return
-	}
 	outFileName = randStr + ".png"
-
-	doneChannel := make(chan bool, 1)
-	errorChannel := make(chan error, 1)
-
-	cmd := exec.Command("highcharts-export-server", "--infile", fileName, "--constr", "Chart", "--scale", "2", "--workers", "10", "--workLimit", "3", "--outfile", outFileName)
-
-	go func() {
-		output, err := cmd.CombinedOutput()
+	fileName = uploadDir + fileName
+	if NotBackendGenerate {
+		err := utils.SaveBase64ToFile(imgData, outFileName)
 		if err != nil {
-			utils.FileLog.Info("execute command failed, output: , error: \n" + string(output) + err.Error())
-			errorChannel <- err
+			br.Msg = "图片保存失败"
+			br.ErrMsg = "图片保存失败,Err:" + err.Error()
 			return
 		}
-		doneChannel <- true
-	}()
+	} else {
+		err := utils.SaveToFile(imgData, fileName)
+		if err != nil {
+			br.Msg = "图片保存失败"
+			br.ErrMsg = "图片保存失败,Err:" + err.Error()
+			return
+		}
+		doneChannel := make(chan bool, 1)
+		errorChannel := make(chan error, 1)
 
-	select {
-	case <-time.After(30 * time.Second):
-		utils.FileLog.Info("执行超过30秒 杀死超时进程")
-		e := cmd.Process.Kill()
-		if e != nil {
-			fmt.Println("cmd kill err: ", e.Error())
-			utils.FileLog.Info(fmt.Sprintf("cmd kill err: %s", e.Error()))
-			br.Msg = "图片生成失败"
-			br.ErrMsg = "图片生成失败, 执行超时" + e.Error()
+		cmd := exec.Command("highcharts-export-server", "--infile", fileName, "--constr", "Chart", "--scale", "2", "--workers", "10", "--workLimit", "3", "--outfile", outFileName)
+
+		go func() {
+			output, err := cmd.CombinedOutput()
+			if err != nil {
+				utils.FileLog.Info("execute command failed, output: , error: \n" + string(output) + err.Error())
+				errorChannel <- err
+				return
+			}
+			doneChannel <- true
+		}()
+		select {
+		case <-time.After(30 * time.Second):
+			utils.FileLog.Info("执行超过30秒 杀死超时进程")
+			e := cmd.Process.Kill()
+			if e != nil {
+				fmt.Println("cmd kill err: ", e.Error())
+				utils.FileLog.Info(fmt.Sprintf("cmd kill err: %s", e.Error()))
+				br.Msg = "图片生成失败"
+				br.ErrMsg = "图片生成失败, 执行超时" + e.Error()
+				return
+			}
+			fmt.Println("timeout kill process")
+		case <-doneChannel:
+			fmt.Println("done")
+		case err := <-errorChannel:
+			br.Msg = "文件上传失败"
+			br.ErrMsg = fmt.Sprintf("execute command failure err: %s", err.Error())
+			fmt.Println("execute command failure err:" + err.Error())
 			return
 		}
-		fmt.Println("timeout kill process")
-	case <-doneChannel:
-		fmt.Println("done")
-	case err := <-errorChannel:
-		br.Msg = "文件上传失败"
-		br.ErrMsg = fmt.Sprintf("execute command failure err: %s", err.Error())
-		fmt.Println("execute command failure err:" + err.Error())
-		return
+		defer func() {
+			os.Remove(fileName)
+		}()
 	}
-
 	defer func() {
-		os.Remove(fileName)
 		os.Remove(outFileName)
 	}()
 
@@ -3071,7 +3064,7 @@ func (this *ChartInfoController) ChartInfoBase64Upload() {
 		br.ErrMsg = "初始化OSS服务失败"
 		return
 	}
-	resourceUrl, err = ossClient.UploadFile(outFileName, outFileName, "")
+	resourceUrl, err := ossClient.UploadFile(outFileName, outFileName, "")
 	if err != nil {
 		br.Msg = "文件上传失败"
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
@@ -3560,7 +3553,7 @@ func (this *ChartInfoController) PreviewBarChartInfo() {
 						value, err := data.MarkerLineCalculate(markerLines[i], edbList[0].DataList, chartInfo)
 						if err != nil {
 							br.Msg = "标识线配置异常"
-							br.ErrMsg = "标识线配置异常" + err.Error()
+							br.ErrMsg = "标识线配置异常," + err.Error()
 							return
 						}
 						markerLines[i].Value = value
@@ -4474,7 +4467,7 @@ func (this *ChartInfoController) UpdateToForum() {
 
 	// 更新指标数据
 	utils.Rc.LPush(utils.CACHE_KEY_EDB_DATA_UPDATE_LOG, []byte("1"))
-	
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -4650,6 +4643,19 @@ func (this *ChartInfoController) ChartInfoImgSetBySvg() {
 		br.ErrMsg = "图片参数错误,Img Is Empty"
 		return
 	}
+	NotBackendGenerate, _ := this.GetBool("NotBackendGenerate", false)
+	if NotBackendGenerate {
+		b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imgData)
+		if !b {
+			br.Msg = "图片格式不正确"
+			br.ErrMsg = "图片格式不正确"
+			return
+		}
+		re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
+		base64Str := re.ReplaceAllString(imgData, "")
+		base64Str = strings.Replace(base64Str, " ", "", -1)
+		imgData = base64Str
+	}
 	chartInfoId, _ := this.GetInt("ChartInfoId", 0)
 	if chartInfoId <= 0 {
 		br.Msg = "图片参数错误"
@@ -4659,7 +4665,7 @@ func (this *ChartInfoController) ChartInfoImgSetBySvg() {
 	resp := new(models.ResourceResp)
 
 	// 通过svg图片生成图片资源地址
-	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData)
+	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData, NotBackendGenerate)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = err.Error()
@@ -5141,7 +5147,7 @@ func (this *ChartInfoController) ModifyChartList() {
 		br.ErrMsg = "更新图表分类失败,Err:" + err.Error()
 		return
 	}
-	
+
 	go eta_forum.ChartInfoDeleteBatchByChartInfoIds(chartIds, req.ChartClassifyId)
 
 	br.Ret = 200
@@ -5186,12 +5192,12 @@ func (this *ChartInfoController) GetChartDescriptionList() {
 	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),
+			Id:              v.Id,
+			Description:     v.Description,
+			ChartInfoId:     v.ChartInfoId,
+			SysUserId:       v.SysUserId,
+			SysUserRealName: v.SysUserRealName,
+			CreateTime:      v.CreateTime.Format(utils.FormatDateTime),
 		})
 	}
 	response.List = list
@@ -5220,7 +5226,7 @@ func (this *ChartInfoController) AddChartDescription() {
 		br.ErrMsg = "请登录,SysUser Is Empty"
 		br.Ret = 408
 		return
-	}	
+	}
 
 	var req data_manage.ChartDescriptionAddReq
 	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
@@ -5249,12 +5255,12 @@ func (this *ChartInfoController) AddChartDescription() {
 		return
 	}
 	item := &data_manage.ChartDescription{
-		ChartInfoId: req.ChartInfoId,
-		Description: req.Description,
-		SysUserId: sysUser.AdminId,
+		ChartInfoId:     req.ChartInfoId,
+		Description:     req.Description,
+		SysUserId:       sysUser.AdminId,
 		SysUserRealName: sysUser.RealName,
-		ModifyTime: time.Now(),
-		CreateTime: time.Now(),
+		ModifyTime:      time.Now(),
+		CreateTime:      time.Now(),
 	}
 	err = data_manage.AddChartDescription(item)
 	if err != nil {
@@ -5267,4 +5273,3 @@ func (this *ChartInfoController) AddChartDescription() {
 	br.Success = true
 	br.Msg = "添加成功"
 }
-

+ 3 - 3
controllers/data_manage/data_manage_permission/data_manage_permission.go

@@ -203,7 +203,7 @@ func (c *DataMangePermissionController) SetEdbChartClassifyPermission() {
 // GetEdbChartPermission
 // @Title 指标/图表/表格权限设置接口
 // @Description 指标/图表/表格权限设置接口
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
 // @Param   SubSource   query   int  false       "子来源 :目前作用于ETA表格,2024-3-26 14:12:09"
 // @Param   DataId   query   int  false       "资产id"
 // @Success 200 {object} data_manage.ChartListResp
@@ -262,7 +262,7 @@ func (c *DataMangePermissionController) GetEdbChartPermission() {
 // GetEdbChartClassifyPermission
 // @Title 获取指标/图表/表格分类权限设置接口
 // @Description 获取指标/图表/表格分类权限设置接口
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
 // @Param   SubSource   query   int  false       "子来源 :目前作用于ETA表格,2024-3-26 14:12:09"
 // @Param   UserId   query   int  false       "用户id"
 // @Success 200 {object} data_manage.ChartListResp
@@ -336,7 +336,7 @@ func (c *DataMangePermissionController) GetEdbChartClassifyPermission() {
 // GetEdbChartNoPermission
 // @Title 根据资产id获取其分类没有权限的用户id列表接口
 // @Description 根据资产id获取其分类没有权限的用户id列表接口
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
 // @Param   SubSource   query   int  false       "子来源 :目前作用于ETA表格,2024-3-26 14:12:09"
 // @Param   DataId   query   int  false       "资产id"
 // @Success 200 {object} data_manage.ChartListResp

+ 3 - 3
controllers/data_manage/data_manage_permission/data_move.go

@@ -13,7 +13,7 @@ import (
 // EdbChartClassifyList
 // @Title 获取指标/图表分类列表数据接口
 // @Description 获取指标/图表分类列表数据接口
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
 // @Param   SubSource   query   int  false       "子来源 :ETA表格中的各种表格类型,以及图表的来源(这个是后续的扩展方向)"
 // @Success 200 {object} data_manage.ChartListResp
 // @router /edb_chart/classify [get]
@@ -63,7 +63,7 @@ func (c *DataMangePermissionController) EdbChartClassifyList() {
 // SecretEdbChartClassifyList
 // @Title 获取涉密的指标/图表分类列表数据接口
 // @Description 获取指标/图表分类列表数据接口
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
 // @Param   SubSource   query   int  false       "子来源 :ETA表格中的各种表格类型,以及图表的来源(这个是后续的扩展方向)"
 // @Success 200 {object} data_manage.ChartListResp
 // @router /edb_chart/classify/secret [get]
@@ -133,7 +133,7 @@ func removeNodesWithNoJoinPermissionAndEmptyChildRecursively(nodes []*data_manag
 // @Description 获取指标/图表创建人列表数据接口
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
 // @Param   SubSource   query   int  false       "子来源 :目前作用于ETA表格,2024-3-26 14:12:09"
 // @Param   Keyword   query   string  false       "关键字,code或者名称"
 // @Param   Classify   query   string  false       "分类id"

+ 1 - 1
controllers/data_manage/data_manage_permission/message.go

@@ -172,7 +172,7 @@ func (c *DataMangePermissionController) MessageRead() {
 // @Param   MessageId			query	int		true	"消息ID"
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格"
 // @Param   SubSource   query   int  false       "子来源 :目前作用于ETA表格,2024-3-26 14:12:09"
 // @Success 200 {object} data_manage_permission.MessageDetailListResp
 // @router /message/detail [get]

+ 1 - 1
controllers/data_manage/edb_classify.go

@@ -291,7 +291,7 @@ func (this *EdbClassifyController) EditEdbClassify() {
 		return
 	}
 
-	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ParentId, req.ClassifyName, this.Lang, this.SysUser)
+	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ParentId, req.ClassifyName, this.Lang, this.SysUser, false)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg

+ 93 - 10
controllers/data_manage/edb_info.go

@@ -21,6 +21,7 @@ import (
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -1251,7 +1252,7 @@ func (this *EdbInfoController) EdbInfoSearch() {
 				}
 				isAdd = true
 			}
-		} else if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL { //钢联化工
+		} else if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL { //上海钢联
 			dataItems, err := data_manage.GetEdbDataAllByEdbCode(edbCode, source, subSource, utils.EDB_DATA_LIMIT)
 			if err != nil && !utils.IsErrNoRow(err) {
 				br.Msg = "获取失败"
@@ -2249,7 +2250,15 @@ func (this *EdbInfoController) EdbInfoList() {
 	edbInfoItem.FrequencyEn = data.GetFrequencyEn(edbInfoItem.Frequency)
 	// 获取是否供应商停更
 	edbInfoItem.IsSupplierStop = data.GetIsSupplierStop(edbInfoItem.Source, edbInfoItem.EdbCode)
-
+	relation, err := data.GetEdbRelationListById(edbInfoItem.EdbInfoId, edbInfoItem.Source)
+	if err != nil && !utils.IsErrNoRow(err) {
+		br.Msg = "获取指标引用信息失败"
+		br.ErrMsg = "获取指标引用信息失败,Err:" + err.Error()
+		return
+	}
+	if relation != nil && relation.RelationNum > 0 {
+		edbInfoItem.IsRelation = true
+	}
 	//查询目录
 	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfoItem.ClassifyId)
 	if err != nil {
@@ -2402,9 +2411,9 @@ func (this *EdbInfoController) EdbInfoAdd() {
 	//	return
 	//}
 
-	// 兼容钢联与钢联化工数据
+	// 兼容钢联与上海钢联数据
 	if utils.InArrayByInt([]int{utils.DATA_SOURCE_GL, utils.DATA_SOURCE_MYSTEEL_CHEMICAL}, source) {
-		// 如果是钢联的话,那么就先判断是不是存在钢联化工
+		// 如果是钢联的话,那么就先判断是不是存在上海钢联
 		tmpInfo, err := data_manage.GetBaseFromMysteelChemicalIndexByCode(req.EdbCode)
 		if err != nil {
 			if !utils.IsErrNoRow(err) {
@@ -2997,7 +3006,7 @@ func (this *EdbInfoController) EdbInfoRefresh() {
 		br.ErrMsg = "参数错误"
 		return
 	}
-	_, err := data_manage.GetEdbInfoById(edbInfoId)
+	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
 	if err != nil {
 		if utils.IsErrNoRow(err) {
 			br.Msg = "数据已被删除,请刷新页面"
@@ -3008,6 +3017,11 @@ func (this *EdbInfoController) EdbInfoRefresh() {
 		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
 		return
 	}
+	if edbInfo.NoUpdate == 1 {
+		br.Msg = "该指标已停用"
+		br.ErrMsg = "该指标已停用,edbCode:" + edbInfo.EdbCode
+		return
+	}
 	err, isAsync := data.EdbInfoRefreshAllFromBaseV2(edbInfoId, false, false)
 	if err != nil {
 		br.Msg = "刷新失败"
@@ -3899,7 +3913,16 @@ func (this *ChartInfoController) EdbInfoData() {
 	fullEdb.EdbInfo = edbInfo
 	// 是否供应商停更
 	fullEdb.IsSupplierStop = data.GetIsSupplierStop(edbInfo.Source, edbInfo.EdbCode)
-
+	//是否被引用
+	relation, err := data.GetEdbRelationListById(edbInfo.EdbInfoId, edbInfo.Source)
+	if err != nil && !utils.IsErrNoRow(err) {
+		br.Msg = err.Error()
+		br.ErrMsg = "获取指标关联关系失败,err:" + err.Error()
+		return
+	}
+	if relation != nil && relation.RelationNum > 0 {
+		fullEdb.IsRelation = true
+	}
 	fullEdb.ClassifyList = classifyList
 
 	var currClassifyItem *data_manage.EdbClassifyIdItems
@@ -5271,7 +5294,7 @@ func (this *EdbInfoController) GetEdbBeforeAndAfterDateData() {
 // EdbChartAdminList
 // @Title 获取创建人员分组
 // @Description 获取创建人员分组
-// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库"
+// @Param   Source   query   int  false       "来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库"
 // @Success 200 {object} company.DepartmentGroupSellersResp
 // @router /edb_chart/adminList [get]
 func (this *EdbInfoController) EdbChartAdminList() {
@@ -5646,6 +5669,47 @@ func (this *EdbInfoController) EdbChartList() {
 					}
 				}
 			}
+			var wg sync.WaitGroup
+			wg.Add(2)
+			var relationMap =make(map[int]*data_manage.BaseRelationEdbInfo,len(list))
+			var relationList []*data_manage.BaseRelationEdbInfo
+			relationChan := make(chan RelationResult, 2)
+			var edbInfoIds []int
+			var glEdbInfoInfoIds []int
+			for _, v := range list {
+				if v.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+					glEdbInfoInfoIds = append(glEdbInfoInfoIds, v.EdbInfoId)
+				} else {
+					edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+				}
+
+			}
+			// 抽象为一个函数减少重复逻辑
+			fetchRelations := func(ids []int, source int) {
+				defer wg.Done()
+				_, relList, getErr := data.GetEdbRelationListByIds(ids, source)
+				relationChan <- RelationResult{
+					List: relList,
+					Err:  getErr,
+				}
+			}
+			go fetchRelations(glEdbInfoInfoIds, utils.DATA_SOURCE_MYSTEEL_CHEMICAL)
+			go fetchRelations(edbInfoIds, -1)
+			wg.Wait()
+			close(relationChan)
+
+			for i := 0; i < 2; i++ {
+				result := <-relationChan
+				if result.Err != nil && !utils.IsErrNoRow(result.Err) {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取数据引用信息失败,Err:" + e.Error()
+					return
+				}
+				relationList = append(relationList, result.List...)
+			}
+			for _, v := range relationList {
+				relationMap[v.EdbInfoId] = v
+			}
 
 			for _, v := range list {
 				if currClassify, ok := classifyMap[v.ClassifyId]; ok {
@@ -5660,9 +5724,11 @@ func (this *EdbInfoController) EdbChartList() {
 						v.IsSupplierStop = 1
 					}
 				}
+				if relationMap[v.EdbInfoId] != nil && relationMap[v.EdbInfoId].RelationNum > 0 {
+					v.IsRelation = true
+				}
 			}
 		}
-
 		page = paging.GetPaging(currentIndex, pageSize, int(dataCount))
 	}
 
@@ -5676,6 +5742,11 @@ func (this *EdbInfoController) EdbChartList() {
 	br.Data = resp
 }
 
+type RelationResult struct {
+	List []*data_manage.BaseRelationEdbInfo
+	Err  error
+}
+
 // Modify
 // @Title 修改指标信息接口
 // @Description 编辑指标接口
@@ -6714,13 +6785,25 @@ func (this *EdbInfoController) ChartImageSetBySvg() {
 		br.Ret = 408
 		return
 	}
-
+	NotBackendGenerate,_ := this.GetBool("NotBackendGenerate",false)
 	imgData := this.GetString("Img")
 	if imgData == "" {
 		br.Msg = "图片参数错误"
 		br.ErrMsg = "图片参数错误,Img Is Empty"
 		return
 	}
+	if NotBackendGenerate {
+		b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imgData)
+		if !b {
+			br.Msg = "图片格式不正确"
+			br.ErrMsg = "图片格式不正确"
+			return
+		}
+		re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
+		base64Str := re.ReplaceAllString(imgData, "")
+		base64Str = strings.Replace(base64Str, " ", "", -1)
+		imgData = base64Str
+	}
 	edbInfoId, _ := this.GetInt("EdbInfoId", 0)
 	if edbInfoId <= 0 {
 		br.Msg = "指标参数错误"
@@ -6729,7 +6812,7 @@ func (this *EdbInfoController) ChartImageSetBySvg() {
 	}
 
 	// 通过svg图片生成图片资源地址
-	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData)
+	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData,NotBackendGenerate)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = err.Error()

+ 30 - 12
controllers/data_manage/edb_info_refresh.go

@@ -679,7 +679,7 @@ func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
 	}
 	edbType := 1 //基础指标
 	switch req.Source {
-	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL, utils.DATA_SOURCE_WIND: // wind
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL, utils.DATA_SOURCE_WIND, utils.DATA_SOURCE_THS, utils.DATA_SOURCE_PB_FINANCE, utils.DATA_SOURCE_PB: // wind
 	case -1:
 		req.Source = 0
 		edbType = 2
@@ -690,6 +690,10 @@ func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
 	// todo 批量设置刷新状态修改
 	edbIdList := make([]int, 0)
 	edbCodeList := make([]string, 0)
+	isStop := 0
+	if req.ModifyStatus == `暂停` {
+		isStop = 1
+	}
 	// 指标id列表
 	if req.IsSelectAll {
 		// 如果是列表全选
@@ -712,12 +716,32 @@ func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
 			if ok {
 				continue
 			}
-
+			if v.RelationNum > 0 && v.IsSupplierStop == 0 && isStop == 1 {
+				br.Msg = "保存失败,停用指标已被引用,指标ID:" + v.EdbCode + "指标名称:" + v.EdbName
+				br.ErrMsg = "保存失败,停用指标已被引用,指标ID:" + v.EdbCode + "指标名称:" + v.EdbName
+				return
+			}
 			// 加入到待配置的指标列表id
 			edbIdList = append(edbIdList, v.EdbInfoId)
 		}
 	} else {
 		edbIdList = req.EdbSelectIdList
+		_, edbRelationList, err := data.GetEdbRelationListByIds(edbIdList, req.Source)
+		if err != nil && !utils.IsErrNoRow(err) {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range edbRelationList {
+			if v.RelationNum > 0 && v.IsSupplierStop == 0 && isStop == 1 {
+				br.Msg = "保存失败,停用指标已被引用,指标ID:" + v.EdbCode + "指标名称:" + v.EdbName
+				br.ErrMsg = "保存失败,停用指标已被引用,指标ID:" + v.EdbCode + "指标名称:" + v.EdbName
+				return
+			}
+			// 加入到待配置的指标列表id
+			// 加入到待配置的指标列表id
+			edbIdList = append(edbIdList, v.EdbInfoId)
+		}
 	}
 
 	if len(edbIdList) <= 0 {
@@ -729,11 +753,11 @@ func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
 	edbList, e := data_manage.GetEdbInfoByIdList(edbIdList)
 	if e != nil && !utils.IsErrNoRow(e) {
 		br.Msg = "获取失败"
-		br.ErrMsg = "获取数据失败,Err:" + e.Error()
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
 		return
 	}
-
-	// 如果是钢联化工,那么需要过滤供应商暂停的指标
+	//var edbList []*data_manage.EdbInfo
+	// 如果是上海钢联,那么需要过滤供应商暂停的指标
 	if req.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
 		// 获取未被供应商暂停的指标
 		tmpEdbCodeList := make([]string, 0)
@@ -760,7 +784,6 @@ func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
 			br.ErrMsg = "获取数据失败,Err:" + err.Error()
 			return
 		}
-
 		if len(edbList) <= 0 {
 			br.Ret = 200
 			br.Success = true
@@ -779,11 +802,6 @@ func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
 		}
 	}
 
-	isStop := 0
-	if req.ModifyStatus == `暂停` {
-		isStop = 1
-	}
-
 	// 查询计算指标ID
 	// 查询相关的计算指标
 	calculateEdbIdList := make([]int, 0)
@@ -798,7 +816,7 @@ func (c *EdbInfoController) SaveRelationEdbRefreshStatus() {
 	}
 
 	switch req.Source {
-	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL: // 钢联化工
+	case utils.DATA_SOURCE_MYSTEEL_CHEMICAL: // 上海钢联
 		err = data_manage.ModifyMysteelChemicalUpdateStatusByEdbInfoIds(edbIdList, isStop, edbCodeList, calculateEdbIdList)
 	default:
 		err = data_manage.EdbInfoUpdateStatusByEdbInfoId(edbIdList, isStop, calculateEdbIdList)

+ 1 - 1
controllers/data_manage/edb_info_relation.go

@@ -21,7 +21,7 @@ type EdbInfoRelationController struct {
 // RelationEdbList
 // @Title 获取被引用的指标列表接口
 // @Description 获取被引用的指标列表接口
-// @Param   Source   query   int  true       "来源:2:wind,34:钢联化工"
+// @Param   Source   query   int  true       "来源:2:wind,34:上海钢联"
 // @Param   ClassifyId   query   string  false             "分类ID,支持多选,用英文,隔开"
 // @Param   SysUserId   query   string  false       "创建人,支持多选,用英文,隔开"
 // @Param   Frequency   query   string  false       "频度,支持多选,用英文,隔开"

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

@@ -9,6 +9,7 @@ import (
 	response2 "eta/eta_api/models/data_manage/excel/response"
 	"eta/eta_api/services/data/data_manage_permission"
 	excel2 "eta/eta_api/services/data/excel"
+	excelService "eta/eta_api/services/excel"
 	"eta/eta_api/utils"
 	"fmt"
 	"sort"
@@ -792,6 +793,11 @@ func (this *ExcelClassifyController) DeleteExcelClassify() {
 			return
 		}
 
+		// 写入ES
+		go func() {
+			excelService.EsAddOrEditExcel(excelInfo.ExcelInfoId)
+		}()
+
 		// 返回下一个表格的信息
 		{
 			var nextItem *excel.ExcelInfo

+ 79 - 36
controllers/data_manage/excel/excel_info.go

@@ -466,14 +466,20 @@ func (c *ExcelInfoController) List() {
 		newKeyWord := strings.Split(keyword, " ")
 		keywordStr := strings.Replace(keyword, " ", "", -1)
 
-		condition += " AND ( "
-		condition += ` excel_name LIKE '%` + keywordStr + `%' OR`
+		likeKey := `%` + keywordStr + `%`
 
+		condition += " AND ( "
+		//condition += ` excel_name LIKE '%` + keywordStr + `%' OR`
+		condition += ` excel_name LIKE ? OR`
+		pars = append(pars, likeKey)
 		keyWordArr = append(keyWordArr, newKeyWord...)
 		if len(keyWordArr) > 0 {
 			for _, v := range keyWordArr {
 				if v != "" {
-					condition += ` excel_name LIKE '%` + v + `%' OR`
+					//condition += ` excel_name LIKE '%` + v + `%' OR`
+					likeKey := `%` + v + `%`
+					condition += ` excel_name LIKE ? OR`
+					pars = append(pars, likeKey)
 				}
 			}
 		}
@@ -3501,32 +3507,15 @@ func (c *ExcelInfoController) SearchByEs() {
 	page := paging.GetPaging(currentIndex, pageSize, total)
 
 	// 平衡表的查询条件
-	var condBalance string
-	var parsBalance []interface{}
-	if source == utils.BALANCE_TABLE {
-		condBalance += ` AND source = ? AND parent_id = 0 AND balance_type = 0` // 只显示动态表的一级表(不显示子表和静态表)
-		parsBalance = append(parsBalance, source)
-	}
+	//var condBalance string
+	//var parsBalance []interface{}
+	//if source == utils.BALANCE_TABLE {
+	//	condBalance += ` AND source = ? AND parent_id = 0 AND balance_type = 0` // 只显示动态表的一级表(不显示子表和静态表)
+	//	parsBalance = append(parsBalance, source)
+	//}
 
 	// 可见性过滤
 	var queryIds, exceptIds []int
-	{
-		unauthorized, e := excelPermissionModel.GetExcelInfoDataNoPermissionByUserId(sysUser.AdminId, source)
-		if e != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = fmt.Sprintf("获取无权限表格失败, %v", e)
-			return
-		}
-		if len(unauthorized) > 0 {
-			for _, v := range unauthorized {
-				id, _ := strconv.Atoi(v.DataId)
-				if id == 0 {
-					continue
-				}
-				exceptIds = append(exceptIds, id)
-			}
-		}
-	}
 
 	// 自定义分析表
 	var queryAdminId int
@@ -3580,17 +3569,71 @@ func (c *ExcelInfoController) SearchByEs() {
 	}
 	hasCheck := make(map[int]bool)
 	if isShowMe {
-		// 平衡表查询有权限的表格IDs
-		if source == utils.BALANCE_TABLE {
-			excelIds, e := services.GetBalanceExcelIdsByAdminId(sysUser.AdminId, condBalance, parsBalance, permissionEdbIdList, permissionClassifyIdList)
-			if e != nil {
-				br.Msg = "获取失败"
-				br.ErrMsg = fmt.Sprintf("获取平衡表有权限的表格IDs失败, %v", e)
+		if source == utils.BALANCE_TABLE { //平衡表的,显示同时需要显示协作人相关的图表
+			//找到当前协作人相关的表格ID
+			obj := new(excel3.ExcelWorker)
+			existList, err := obj.GetBySysUserId(sysUser.AdminId)
+			if err != nil {
+				br.Msg = "获取表格协作人失败!"
+				br.ErrMsg = "获取表格协作人失败,Err:" + err.Error()
 				return
 			}
-			if len(excelIds) == 0 {
-				list := make([]*excel3.SearchExcelInfo, 0)
-				resp := response.SearchExcelListResp{
+			var excelIds []int
+			var newCondition string
+			var newPars []interface{}
+			if len(existList) > 0 {
+				for _, v := range existList {
+					excelIds = append(excelIds, v.ExcelInfoId)
+				}
+				newCondition += fmt.Sprintf(` AND source = ? AND (excel_info_id IN (%s) or sys_user_id = ?)`, utils.GetOrmInReplace(len(excelIds)))
+				newPars = append(newPars, source, excelIds, sysUser.AdminId)
+			} else {
+				newCondition += ` AND sys_user_id = ? `
+				newPars = append(newPars, sysUser.AdminId)
+			}
+
+			//获取表格信息
+			tmpList, e := excel3.GetNoContentExcelListByConditionNoPage(newCondition, newPars)
+			if e != nil && !utils.IsErrNoRow(e) {
+				br.Success = true
+				br.Msg = "获取表格信息失败"
+				br.ErrMsg = "获取表格信息失败,Err:" + e.Error()
+				return
+			}
+			classifyIdListTmp := make([]int, 0)
+			for _, v := range tmpList {
+				classifyIdListTmp = append(classifyIdListTmp, v.ExcelClassifyId)
+			}
+			classifyMap := make(map[int]*excel3.ExcelClassify)
+
+			// 分类信息
+			if len(classifyIdListTmp) > 0 {
+				classifyListTmp, e := excel3.GetClassifyByIdList(classifyIdListTmp)
+				if e != nil {
+					br.Msg = "获取表格分类信息失败"
+					br.ErrMsg = "获取表格分类列表数据失败,Err:" + e.Error()
+					return
+				}
+				for _, v := range classifyListTmp {
+					classifyMap[v.ExcelClassifyId] = v
+				}
+			}
+			excelIds = make([]int, 0)
+			for _, v := range tmpList {
+				// 数据权限
+				if classifyInfo, ok := classifyMap[v.ExcelClassifyId]; ok {
+					v.HaveOperaAuth = data_manage_permission.CheckExcelPermissionByPermissionIdList(v.IsJoinPermission, classifyInfo.IsJoinPermission, v.ExcelInfoId, v.ExcelClassifyId, permissionEdbIdList, permissionClassifyIdList)
+					if v.HaveOperaAuth {
+						excelIds = append(excelIds, v.ExcelInfoId)
+					}
+					hasCheck[v.ExcelInfoId] = v.HaveOperaAuth
+				}
+			}
+			if len(excelIds) > 0 {
+				queryIds = excelIds
+			} else {
+				list := make([]*excel3.MyExcelInfoList, 0)
+				resp := response.ExcelListResp{
 					Paging: page,
 					List:   list,
 				}
@@ -3600,7 +3643,7 @@ func (c *ExcelInfoController) SearchByEs() {
 				br.Data = resp
 				return
 			}
-			queryIds = excelIds
+
 		} else {
 			queryAdminId = sysUser.AdminId
 		}

+ 75 - 41
controllers/data_manage/future_good/future_good_chart_info.go

@@ -18,6 +18,7 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -2614,57 +2615,77 @@ func (this *FutureGoodChartInfoController) ChartInfoBase64Upload() {
 		}
 	}
 
+	NotBackendGenerate, _ := this.GetBool("NotBackendGenerate", false)
+	if NotBackendGenerate {
+		b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imgData)
+		if !b {
+			br.Msg = "图片格式不正确"
+			br.ErrMsg = "图片格式不正确"
+			return
+		}
+		re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
+		base64Str := re.ReplaceAllString(imgData, "")
+		base64Str = strings.Replace(base64Str, " ", "", -1)
+		imgData = base64Str
+	}
 	//var saveToOssPath string
 	randStr := utils.GetRandStringNoSpecialChar(28)
 	var fileName, outFileName string
-	fileName = randStr + ".txt"
-	//saveSvgPath = uploadDir + fileName
-	err := utils.SaveToFile(imgData, fileName)
-	if err != nil {
-		br.Msg = "图片保存失败"
-		br.ErrMsg = "图片保存失败,Err:" + err.Error()
-		return
-	}
 	outFileName = randStr + ".png"
-
-	doneChannel := make(chan bool, 1)
-	errorChannel := make(chan error, 1)
-
-	cmd := exec.Command("highcharts-export-server", "--infile", fileName, "--constr", "Chart", "--scale", "2", "--workers", "10", "--workLimit", "3", "--outfile", outFileName)
-
-	go func() {
-		output, err := cmd.CombinedOutput()
+	if NotBackendGenerate {
+		err := utils.SaveBase64ToFile(imgData, outFileName)
 		if err != nil {
-			utils.FileLog.Info("execute command failed, output: , error: \n" + string(output) + err.Error())
-			errorChannel <- err
+			br.Msg = "图片保存失败"
+			br.ErrMsg = "图片保存失败,Err:" + err.Error()
 			return
 		}
-		doneChannel <- true
-	}()
+	} else {
+		fileName = randStr + ".txt"
+		//saveSvgPath = uploadDir + fileName
+		err := utils.SaveToFile(imgData, fileName)
+		if err != nil {
+			br.Msg = "图片保存失败"
+			br.ErrMsg = "图片保存失败,Err:" + err.Error()
+			return
+		}
+		doneChannel := make(chan bool, 1)
+		errorChannel := make(chan error, 1)
+		cmd := exec.Command("highcharts-export-server", "--infile", fileName, "--constr", "Chart", "--scale", "2", "--workers", "10", "--workLimit", "3", "--outfile", outFileName)
+		go func() {
+			output, err := cmd.CombinedOutput()
+			if err != nil {
+				utils.FileLog.Info("execute command failed, output: , error: \n" + string(output) + err.Error())
+				errorChannel <- err
+				return
+			}
+			doneChannel <- true
+		}()
 
-	select {
-	case <-time.After(30 * time.Second):
-		utils.FileLog.Info("执行超过30秒 杀死超时进程")
-		e := cmd.Process.Kill()
-		if e != nil {
-			fmt.Println("cmd kill err: ", e.Error())
-			utils.FileLog.Info(fmt.Sprintf("cmd kill err: %s", e.Error()))
-			br.Msg = "图片生成失败"
-			br.ErrMsg = "图片生成失败, 执行超时" + e.Error()
+		select {
+		case <-time.After(30 * time.Second):
+			utils.FileLog.Info("执行超过30秒 杀死超时进程")
+			e := cmd.Process.Kill()
+			if e != nil {
+				fmt.Println("cmd kill err: ", e.Error())
+				utils.FileLog.Info(fmt.Sprintf("cmd kill err: %s", e.Error()))
+				br.Msg = "图片生成失败"
+				br.ErrMsg = "图片生成失败, 执行超时" + e.Error()
+				return
+			}
+			fmt.Println("timeout kill process")
+		case <-doneChannel:
+			fmt.Println("done")
+		case err := <-errorChannel:
+			br.Msg = "文件上传失败"
+			br.ErrMsg = fmt.Sprintf("execute command failure err: %s", err.Error())
+			fmt.Println("execute command failure err:" + err.Error())
 			return
 		}
-		fmt.Println("timeout kill process")
-	case <-doneChannel:
-		fmt.Println("done")
-	case err := <-errorChannel:
-		br.Msg = "文件上传失败"
-		br.ErrMsg = fmt.Sprintf("execute command failure err: %s", err.Error())
-		fmt.Println("execute command failure err:" + err.Error())
-		return
+		defer func() {
+			os.Remove(fileName)
+		}()
 	}
-
 	defer func() {
-		os.Remove(fileName)
 		os.Remove(outFileName)
 	}()
 
@@ -2695,7 +2716,7 @@ func (this *FutureGoodChartInfoController) ChartInfoBase64Upload() {
 		br.ErrMsg = "初始化OSS服务失败"
 		return
 	}
-	resourceUrl, err = ossClient.UploadFile(outFileName, outFileName, "")
+	resourceUrl, err := ossClient.UploadFile(outFileName, outFileName, "")
 	if err != nil {
 		br.Msg = "文件上传失败"
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
@@ -3570,6 +3591,19 @@ func (this *FutureGoodChartInfoController) ChartInfoImgSetBySvg() {
 		br.ErrMsg = "图片参数错误,Img Is Empty"
 		return
 	}
+	NotBackendGenerate, _ := this.GetBool("NotBackendGenerate", false)
+	if NotBackendGenerate {
+		b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imgData)
+		if !b {
+			br.Msg = "图片格式不正确"
+			br.ErrMsg = "图片格式不正确"
+			return
+		}
+		re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
+		base64Str := re.ReplaceAllString(imgData, "")
+		base64Str = strings.Replace(base64Str, " ", "", -1)
+		imgData = base64Str
+	}
 	chartInfoId, _ := this.GetInt("ChartInfoId", 0)
 	if chartInfoId <= 0 {
 		br.Msg = "图片参数错误"
@@ -3579,7 +3613,7 @@ func (this *FutureGoodChartInfoController) ChartInfoImgSetBySvg() {
 	resp := new(models.ResourceResp)
 
 	// 通过svg图片生成图片资源地址
-	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData)
+	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData, NotBackendGenerate)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = err.Error()

+ 9 - 5
controllers/data_manage/gpr_risk_data.go

@@ -256,20 +256,24 @@ func (this *BaseFromGprRiskController) GprRiskSearchList() {
 		keyWordArr := strings.Split(keyword, " ")
 
 		if len(keyWordArr) > 0 {
-			condition := ""
+			var condition string
+			var pars []interface{}
 			for _, v := range keyWordArr {
-				condition += ` AND CONCAT(index_name,index_code) LIKE '%` + v + `%'`
+				likeKey := `%` + v + `%`
+				condition += ` AND CONCAT(index_name,index_code) LIKE ? `
+				pars = append(pars, likeKey)
 			}
-			list, err = data_manage.GetGprRiskItemList(condition)
+			list, err = data_manage.GetGprRiskItemList(condition, pars)
 			if err != nil {
 				br.ErrMsg = "获取失败,Err:" + err.Error()
 				br.Msg = "获取失败"
 				return
 			}
 		}
-
 	} else {
-		list, err = data_manage.GetGprRiskItemList("")
+		var condition string
+		var pars []interface{}
+		list, err = data_manage.GetGprRiskItemList(condition, pars)
 		if err != nil {
 			br.ErrMsg = "获取失败,Err:" + err.Error()
 			br.Msg = "获取失败"

+ 39 - 34
controllers/data_manage/mysteel_chemical_data.go

@@ -13,6 +13,7 @@ import (
 	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/shopspring/decimal"
 	"os"
 	"path/filepath"
 	"sort"
@@ -25,8 +26,8 @@ import (
 )
 
 // MysteelChemicalClassify
-// @Title 钢联化工数据分类
-// @Description 钢联化工分类接口
+// @Title 上海钢联数据分类
+// @Description 上海钢联分类接口
 // @Success 200 {object} data_manage.BaseFromMysteelChemicalClassifyItems
 // @router /mysteel_chemical/classify [get]
 func (this *EdbInfoController) MysteelChemicalClassify() {
@@ -90,8 +91,8 @@ func (this *EdbInfoController) MysteelChemicalClassify() {
 }
 
 // MysteelChemicalIndexList
-// @Title 钢联化工数据指标列表
-// @Description 钢联化工数据指标列表接口
+// @Title 上海钢联数据指标列表
+// @Description 上海钢联数据指标列表接口
 // @Success 200 {object} data_manage.BaseFromMysteelChemicalIndexResp
 // @router /mysteel_chemical/index/list [get]
 func (this *EdbInfoController) MysteelChemicalIndexList() {
@@ -181,7 +182,7 @@ func (this *EdbClassifyController) AddMysteelChemicalClassify() {
 		for _, v := range secondClassifyList {
 			_, _, tmpErrMsg := data.AddMysteelChemicalClassify(v, mysteelChemicalClassifyInfo.BaseFromMysteelChemicalClassifyId, mysteelChemicalClassifyInfo.Level, this.SysUser.AdminId, this.SysUser.RealName, this.Lang)
 			if tmpErrMsg != `` {
-				go alarm_msg.SendAlarmMsg("钢联化工-添加一级分类时,默认添加二级分类失败,一级分类名称:"+req.ClassifyName+",二级分类名称:"+v+", Err:"+tmpErrMsg, 3)
+				go alarm_msg.SendAlarmMsg("上海钢联-添加一级分类时,默认添加二级分类失败,一级分类名称:"+req.ClassifyName+",二级分类名称:"+v+", Err:"+tmpErrMsg, 3)
 				return
 			}
 		}
@@ -343,8 +344,8 @@ func (this *EdbClassifyController) DeleteMysteelChemicalClassify() {
 }
 
 // MysteelChemicalSearch
-// @Title 钢联化工指标查询
-// @Description 钢联化工指标查询
+// @Title 上海钢联指标查询
+// @Description 上海钢联指标查询
 // @Param   BaseFromMysteelChemicalClassifyId   query   int  true       "分类id"
 // @Param   Keyword   query   string  true       "名称关键词"
 // @Success 200 {object} data_manage.LzFrequency
@@ -416,10 +417,10 @@ func (this *EdbInfoController) MysteelChemicalSearch() {
 }
 
 // MysteelChemicalFrequency
-// @Title 钢联化工数据频度
-// @Description 钢联化工数据频度接口
+// @Title 上海钢联数据频度
+// @Description 上海钢联数据频度接口
 // @Param   BaseFromMysteelChemicalClassifyId   query   int  true       "分类id"
-// @Param   BaseFromMysteelChemicalIndexId   query   int  true       "钢联化工指标id"
+// @Param   BaseFromMysteelChemicalIndexId   query   int  true       "上海钢联指标id"
 // @Param   Keyword   query   string  true       "名称关键词"
 // @Success 200 {object} data_manage.LzFrequency
 // @router /mysteel_chemical/frequency [get]
@@ -495,10 +496,10 @@ func (this *EdbInfoController) MysteelChemicalFrequency() {
 }
 
 // MysteelChemicalData
-// @Title 获取钢联化工数据
-// @Description 获取钢联化工数据
+// @Title 获取上海钢联数据
+// @Description 获取上海钢联数据
 // @Param   BaseFromMysteelChemicalClassifyId   query   int  true       "分类id"
-// @Param   BaseFromMysteelChemicalIndexId   query   int  true       "钢联化工指标id"
+// @Param   BaseFromMysteelChemicalIndexId   query   int  true       "上海钢联指标id"
 // @Param   Frequency   query   string  true       "频度名称"
 // @Param   Keyword   query   string  true       "名称关键词"
 // @Param   PageSize   query   int  true       "每页数据条数"
@@ -584,14 +585,18 @@ func (this *EdbInfoController) MysteelChemicalData() {
 		total, err := data_manage.GetMysteelChemicalIndexDataCount(v.IndexCode)
 		page := paging.GetPaging(currentIndex, pageSize, total)
 
-		dataList, err := data_manage.GetMysteelChemicalIndexData(v.IndexCode, startSize, pageSize)
+		dataList := make([]*data_manage.MysteelChemicalData, 0)
+		tmpDataList, err := data_manage.GetMysteelChemicalIndexData(v.IndexCode, startSize, pageSize)
 		if err != nil {
 			br.Msg = "获取数据失败"
 			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
 			return
 		}
-		if dataList == nil {
-			dataList = make([]*data_manage.MysteelChemicalData, 0)
+		for _, tmpData := range tmpDataList {
+			dataList = append(dataList, &data_manage.MysteelChemicalData{
+				DataTime:   tmpData.DataTime,
+				InputValue: decimal.NewFromFloat(tmpData.InputValue).String(),
+			})
 		}
 		edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_MYSTEEL_CHEMICAL, v.IndexCode)
 		if err != nil && !utils.IsErrNoRow(err) {
@@ -615,10 +620,10 @@ func (this *EdbInfoController) MysteelChemicalData() {
 }
 
 // MysteelChemicalExport
-// @Title 导出钢联化工数据
-// @Description 导出钢联化工数据
+// @Title 导出上海钢联数据
+// @Description 导出上海钢联数据
 // @Param   BaseFromMysteelChemicalClassifyId   query   int  true       "分类"
-// @Param   BaseFromMysteelChemicalIndexId   query   string  true       "钢联化工指标id"
+// @Param   BaseFromMysteelChemicalIndexId   query   string  true       "上海钢联指标id"
 // @Param   Keyword   query   string  true       "名称关键词"
 // @Success 200  导出成功
 // @router /mysteel_chemical/export/dataList [get]
@@ -761,7 +766,7 @@ func (this *EdbClassifyController) MysteelChemicalExport() {
 				return
 			}
 			if k == 0 {
-				windRow.AddCell().SetValue("钢联")
+				windRow.AddCell().SetValue("上海钢联")
 				secNameRow.AddCell().SetValue("指标名称")
 				indexCodeRow.AddCell().SetValue("指标ID")
 				frequencyRow.AddCell().SetValue("频率")
@@ -827,8 +832,8 @@ func (this *EdbClassifyController) MysteelChemicalExport() {
 }
 
 // AddMysteelChemical
-// @Title 新增钢联化工指标
-// @Description 新增钢联化工指标接口
+// @Title 新增上海钢联指标
+// @Description 新增上海钢联指标接口
 // @Param	request	body data_manage.AddEdbClassifyReq true "type json string"
 // @Success 200 Ret=200 保存成功
 // @router /mysteel_chemical/add [post]
@@ -853,7 +858,7 @@ func (this *EdbClassifyController) AddMysteelChemical() {
 		br.ErrMsg = "获取业务配置失败,Err:" + err.Error()
 		return
 	}
-	// 判断钢联化工的数据刷新方式
+	// 判断上海钢联的数据刷新方式
 	if conf["MySteelDataMethod"] == "api" {
 		if len(req.List) > 150 {
 			br.Msg = "添加指标失败,指标数量不能超过150条"
@@ -862,7 +867,7 @@ func (this *EdbClassifyController) AddMysteelChemical() {
 		ok, errMsg, err := data.HealthCheckMysteelChemicalApi()
 		if err != nil {
 			br.Msg = "添加指标失败"
-			br.ErrMsg = "钢联化工数据接口异常,Err:" + err.Error()
+			br.ErrMsg = "上海钢联数据接口异常,Err:" + err.Error()
 			return
 		}
 		if !ok {
@@ -1010,8 +1015,8 @@ func sortEdbFrequency(frequencyList []string) (newFrequencyList []string) {
 }
 
 // EditMysteelChemical
-// @Title 编辑钢联化工指标
-// @Description 新增钢联化工指标接口
+// @Title 编辑上海钢联指标
+// @Description 新增上海钢联指标接口
 // @Param	request	body data_manage.AddEdbClassifyReq true "type json string"
 // @Success 200 Ret=200 保存成功
 // @router /mysteel_chemical/edit [post]
@@ -1168,9 +1173,9 @@ func (this *EdbClassifyController) MoveMysteelChemical() {
 }
 
 // MysteelChemicalDetail
-// @Title 钢联化工指标详情
-// @Description 钢联化工指标详情接口
-// @Param   BaseFromMysteelChemicalIndexId   query   string  true       "钢联化工指标id"
+// @Title 上海钢联指标详情
+// @Description 上海钢联指标详情接口
+// @Param   BaseFromMysteelChemicalIndexId   query   string  true       "上海钢联指标id"
 // @Success 200 {object} data_manage.BaseFromMysteelChemicalClassifyItems
 // @router /mysteel_chemical/detail [get]
 func (this *EdbInfoController) MysteelChemicalDetail() {
@@ -1211,9 +1216,9 @@ func (this *EdbInfoController) MysteelChemicalDetail() {
 }
 
 // MysteelChemicalDetail
-// @Title 钢联化工指标详情
-// @Description 钢联化工指标详情接口
-// @Param   BaseFromMysteelChemicalIndexId   query   string  true       "钢联化工指标id"
+// @Title 上海钢联指标详情
+// @Description 上海钢联指标详情接口
+// @Param   BaseFromMysteelChemicalIndexId   query   string  true       "上海钢联指标id"
 // @Success 200 {object} data_manage.BaseFromMysteelChemicalClassifyItems
 // @router /mysteel_chemical/refresh [get]
 func (this *EdbInfoController) MysteelChemicalRefresh() {
@@ -1746,8 +1751,8 @@ func (c *EdbInfoController) AddCheck() {
 }
 
 // MysteelChemicalBatchSearch
-// @Title 钢联化工指标查询
-// @Description 钢联化工指标查询
+// @Title 上海钢联指标查询
+// @Description 上海钢联指标查询
 // @Param   BaseFromMysteelChemicalClassifyIds   query   string  true       "分类id"
 // @Param   Keyword   query   string  true       "名称关键词"
 // @Success 200 {object} data_manage.LzFrequency

+ 1 - 1
controllers/data_manage/predict_edb_classify.go

@@ -259,7 +259,7 @@ func (this *PredictEdbClassifyController) Edit() {
 		return
 	}
 
-	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ParentId, req.ClassifyName, this.Lang, this.SysUser)
+	err, errMsg := data.EditEdbClassify(req.ClassifyId, req.ParentId, req.ClassifyName, this.Lang, this.SysUser, true)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg

+ 16 - 1
controllers/data_manage/predict_edb_info.go

@@ -13,6 +13,7 @@ import (
 	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
 	"fmt"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -2183,6 +2184,20 @@ func (this *PredictEdbInfoController) ChartImageSetBySvg() {
 		br.ErrMsg = "图片参数错误,Img Is Empty"
 		return
 	}
+	NotBackendGenerate, _ := this.GetBool("NotBackendGenerate", false)
+	if NotBackendGenerate {
+		b, _ := regexp.MatchString(`^data:\s*image\/(\w+);base64,`, imgData)
+		if !b {
+			br.Msg = "图片格式不正确"
+			br.ErrMsg = "图片格式不正确"
+			return
+		}
+		re, _ := regexp.Compile(`^data:\s*image\/(\w+);base64,`)
+		base64Str := re.ReplaceAllString(imgData, "")
+		base64Str = strings.Replace(base64Str, " ", "", -1)
+		imgData = base64Str
+	}
+
 	edbInfoId, _ := this.GetInt("EdbInfoId", 0)
 	if edbInfoId <= 0 {
 		br.Msg = "指标参数错误"
@@ -2191,7 +2206,7 @@ func (this *PredictEdbInfoController) ChartImageSetBySvg() {
 	}
 
 	// 通过svg图片生成图片资源地址
-	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData)
+	resourceUrl, err, errMsg := services.GetResourceUrlBySvgImg(imgData,NotBackendGenerate)
 	if err != nil {
 		br.Msg = errMsg
 		br.ErrMsg = err.Error()

+ 11 - 5
controllers/data_manage/purang_data.go

@@ -256,11 +256,15 @@ func (this *BaseFromPurangController) PurangSearchList() {
 		keyWordArr := strings.Split(keyword, " ")
 
 		if len(keyWordArr) > 0 {
-			condition := ""
+			var condition string
+			var pars []interface{}
 			for _, v := range keyWordArr {
-				condition += ` AND CONCAT(index_name,index_code) LIKE '%` + v + `%'`
+				//condition += ` AND CONCAT(index_name,index_code) LIKE '%` + v + `%'`
+				likeKey := `%` + v + `%`
+				condition += ` AND CONCAT(index_name,index_code) LIKE ? `
+				pars = append(pars, likeKey)
 			}
-			list, err = data_manage.GetPurangItemList(condition)
+			list, err = data_manage.GetPurangItemList(condition, pars)
 			if err != nil {
 				br.ErrMsg = "获取失败,Err:" + err.Error()
 				br.Msg = "获取失败"
@@ -269,7 +273,9 @@ func (this *BaseFromPurangController) PurangSearchList() {
 		}
 
 	} else {
-		list, err = data_manage.GetPurangItemList("")
+		var condition string
+		var pars []interface{}
+		list, err = data_manage.GetPurangItemList(condition, pars)
 		if err != nil {
 			br.ErrMsg = "获取失败,Err:" + err.Error()
 			br.Msg = "获取失败"
@@ -1042,4 +1048,4 @@ func (this *BaseFromPurangController) GetFrequency() {
 	br.Success = true
 	br.Msg = "获取成功"
 	br.Data = frequencyList
-} 
+}

+ 1 - 0
controllers/eta_forum/eta_forum.go

@@ -247,6 +247,7 @@ func (this *EtaForumController) CommonChartInfoDetailFromUniqueCode() {
 	resp.Status = status
 	resp.DataResp = forumResp.DataResp
 	resp.EdbInfoList = forumResp.EdbInfoList
+	resp.XEdbIdValue = forumResp.XEdbIdValue
 	resp.XDataList = forumResp.XDataList
 	resp.YDataList = forumResp.YDataList
 	br.Ret = 200

+ 24 - 3
controllers/fe_calendar/fe_calendar_matter.go

@@ -351,7 +351,8 @@ func (this *FeCalendarMatterController) PermissionList() {
 		br.ErrMsg = "获取品种列表失败, Err: " + e.Error()
 		return
 	}
-	resp := make([]*models.SimpleChartPermission, 0)
+	var resp models.FaCalendarPermissionResp
+	list := make([]*models.SimpleChartPermission, 0)
 	parentPermissions := make(map[int][]*models.SimpleChartPermission, 0)
 	for _, v := range permissions {
 		if v.ParentId > 0 {
@@ -361,12 +362,32 @@ func (this *FeCalendarMatterController) PermissionList() {
 			parentPermissions[v.ParentId] = append(parentPermissions[v.ParentId], models.FormatChartPermission2Simple(v))
 			continue
 		}
-		resp = append(resp, models.FormatChartPermission2Simple(v))
+		list = append(list, models.FormatChartPermission2Simple(v))
 	}
-	for _, v := range resp {
+	for _, v := range list {
 		v.Children = parentPermissions[v.ChartPermissionId]
 	}
+	lastEditPermissionId := 0
+	lastEditPermissionName := ""
+	// 查询最近被编辑过的品种ID
+	matterOb := new(fe_calendar.FeCalendarMatter)
+	cond := ""
+	pars := make([]interface{}, 0)
+	order := fmt.Sprintf(`%s Desc`, fe_calendar.FeCalendarMatterCols.ModifyTime)
+	matter, e := matterOb.GetItemByCondition(cond, pars, order)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取事项列表失败, Err: " + e.Error()
+		return
+	}
+	if e == nil {
+		lastEditPermissionId = matter.ChartPermissionId
+		lastEditPermissionName = matter.ChartPermissionName
+	}
 
+	resp.CheckedPermissionId = lastEditPermissionId
+	resp.CheckedPermissionName = lastEditPermissionName
+	resp.List = list
 	br.Data = resp
 	br.Ret = 200
 	br.Success = true

+ 206 - 212
controllers/llm/abstract.go

@@ -13,6 +13,8 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
+	"strconv"
+	"strings"
 )
 
 // AbstractController
@@ -45,7 +47,26 @@ func (c *AbstractController) List() {
 	pageSize, _ := c.GetInt("PageSize")
 	currentIndex, _ := c.GetInt("CurrentIndex")
 	keyWord := c.GetString("KeyWord")
-	tagId, _ := c.GetInt("TagId")
+	tagIdStr := c.GetString("TagId")
+	questionId, _ := c.GetInt("QuestionId")
+
+	tagIdList := make([]int, 0)
+	if tagIdStr != `` {
+		tagIdStrList := strings.Split(tagIdStr, `,`)
+		for _, v := range tagIdStrList {
+			if v == `0` {
+				continue
+			}
+
+			tagId, tmpErr := strconv.Atoi(v)
+			if tmpErr != nil {
+				br.Msg = "标签ID有误"
+				br.ErrMsg = fmt.Sprintf("标签ID有误, %s", v)
+				return
+			}
+			tagIdList = append(tagIdList, tagId)
+		}
+	}
 
 	var startSize int
 	if pageSize <= 0 {
@@ -57,7 +78,7 @@ func (c *AbstractController) List() {
 	startSize = utils.StartIndex(currentIndex, pageSize)
 
 	// 获取列表
-	total, viewList, err := getAbstractList(keyWord, tagId, startSize, pageSize)
+	total, viewList, err := getAbstractList(keyWord, tagIdList, questionId, startSize, pageSize)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -76,60 +97,91 @@ func (c *AbstractController) List() {
 	br.Data = resp
 }
 
-func getAbstractList(keyWord string, tagId int, startSize, pageSize int) (total int, viewList []rag.WechatArticleAbstractView, err error) {
+func getAbstractList(keyWord string, tagList []int, questionId int, startSize, pageSize int) (total int, viewList []rag.WechatArticleAbstractView, err error) {
+	//if keyWord == `` {
+	//	var condition string
+	//	var pars []interface{}
+	//	condition += fmt.Sprintf(` AND c.%s = ?`, rag.WechatPlatformColumns.Enabled)
+	//	pars = append(pars, 1)
+	//
+	//	if keyWord != "" {
+	//		condition += fmt.Sprintf(` AND a.%s like ?`, rag.WechatArticleAbstractColumns.Content)
+	//		pars = append(pars, `%`+keyWord+`%`)
+	//	}
+	//
+	//	if tagId > 0 {
+	//		condition += fmt.Sprintf(` AND d.%s = ?`, rag.WechatPlatformTagMappingColumns.TagID)
+	//		pars = append(pars, tagId)
+	//	}
+	//
+	//	obj := new(rag.WechatArticleAbstract)
+	//	tmpTotal, list, tmpErr := obj.GetPageListByTagAndPlatformCondition(condition, pars, startSize, pageSize)
+	//	if tmpErr != nil {
+	//		err = tmpErr
+	//		return
+	//	}
+	//	total = tmpTotal
+	//	viewList = obj.WechatArticleAbstractItem(list)
+	//} else {
+	//	sortMap := map[string]string{
+	//		//"ModifyTime":              "desc",
+	//		//"WechatArticleAbstractId": "desc",
+	//	}
+	//
+	//	obj := new(rag.WechatPlatform)
+	//	platformList, tmpErr := obj.GetListByCondition(` AND enabled = 1 `, []interface{}{}, 0, 100000)
+	//	if tmpErr != nil {
+	//		err = tmpErr
+	//		return
+	//	}
+	//	platformIdList := make([]int, 0)
+	//	for _, v := range platformList {
+	//		platformIdList = append(platformIdList, v.WechatPlatformId)
+	//	}
+	//	tagList := make([]int, 0)
+	//	if tagId > 0 {
+	//		tagList = append(tagList, tagId)
+	//	}
+	//	tmpTotal, list, tmpErr := elastic.WechatArticleAbstractEsSearch(keyWord, tagList, platformIdList, startSize, pageSize, sortMap)
+	//	if tmpErr != nil {
+	//		err = tmpErr
+	//		return
+	//	}
+	//	total = int(tmpTotal)
+	//	if list != nil && len(list) > 0 {
+	//		viewList = list[0].ToViewList(list)
+	//	}
+	//}
+
+	sortMap := map[string]string{
+		//"ModifyTime":              "desc",
+		//"WechatArticleAbstractId": "desc",
+	}
 	if keyWord == `` {
-		var condition string
-		var pars []interface{}
-		condition += fmt.Sprintf(` AND c.%s = ?`, rag.WechatPlatformColumns.Enabled)
-		pars = append(pars, 1)
-
-		if keyWord != "" {
-			condition += fmt.Sprintf(` AND a.%s like ?`, rag.WechatArticleAbstractColumns.Content)
-			pars = append(pars, `%`+keyWord+`%`)
-		}
-
-		if tagId > 0 {
-			condition += fmt.Sprintf(` AND d.%s = ?`, rag.WechatPlatformTagMappingColumns.TagID)
-			pars = append(pars, tagId)
-		}
-
-		obj := new(rag.WechatArticleAbstract)
-		tmpTotal, list, tmpErr := obj.GetPageListByTagAndPlatformCondition(condition, pars, startSize, pageSize)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		total = tmpTotal
-		viewList = obj.WechatArticleAbstractItem(list)
-	} else {
-		sortMap := map[string]string{
-			//"ModifyTime":              "desc",
+		sortMap = map[string]string{
+			"CreateTime": "desc",
 			//"WechatArticleAbstractId": "desc",
 		}
+	}
 
-		obj := new(rag.WechatPlatform)
-		platformList, tmpErr := obj.GetListByCondition(` AND enabled = 1 `, []interface{}{}, 0, 100000)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		platformIdList := make([]int, 0)
-		for _, v := range platformList {
-			platformIdList = append(platformIdList, v.WechatPlatformId)
-		}
-		tagList := make([]int, 0)
-		if tagId > 0 {
-			tagList = append(tagList, tagId)
-		}
-		tmpTotal, list, tmpErr := elastic.WechatArticleAbstractEsSearch(keyWord, tagList, platformIdList, startSize, pageSize, sortMap)
-		if tmpErr != nil {
-			err = tmpErr
-			return
-		}
-		total = int(tmpTotal)
-		if list != nil && len(list) > 0 {
-			viewList = list[0].ToViewList(list)
-		}
+	obj := new(rag.WechatPlatform)
+	platformList, tmpErr := obj.GetListByCondition(` AND enabled = 1 `, []interface{}{}, 0, 100000)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	platformIdList := make([]int, 0)
+	for _, v := range platformList {
+		platformIdList = append(platformIdList, v.WechatPlatformId)
+	}
+	tmpTotal, list, tmpErr := elastic.WechatArticleAbstractEsSearch(keyWord, tagList, platformIdList, questionId, startSize, pageSize, sortMap)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	total = int(tmpTotal)
+	if list != nil && len(list) > 0 {
+		viewList = list[0].ToViewList(list)
 	}
 
 	return
@@ -160,82 +212,14 @@ func (c *AbstractController) Del() {
 		return
 	}
 
-	vectorKeyList := make([]string, 0)
-	wechatArticleAbstractIdList := make([]int, 0)
-
-	obj := rag.WechatArticleAbstract{}
-
-	if !req.IsSelectAll {
-		list, err := obj.GetByIdList(req.WechatArticleAbstractIdList)
-		if err != nil {
-			br.Msg = "修改失败"
-			br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
-			if utils.IsErrNoRow(err) {
-				br.Msg = "问题不存在"
-				br.IsSendEmail = false
-			}
-			return
-		}
-		if len(list) > 0 {
-			for _, v := range list {
-				// 有加入到向量库,那么就加入到待删除的向量库list中
-				if v.VectorKey != `` {
-					vectorKeyList = append(vectorKeyList, v.VectorKey)
-				}
-				wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
-			}
-		}
-	} else {
-		notIdMap := make(map[int]bool)
-		for _, v := range req.NotWechatArticleAbstractIdList {
-			notIdMap[v] = true
-		}
-
-		_, list, err := getAbstractList(req.KeyWord, req.TagId, 0, 100000)
-		if err != nil {
-			br.Msg = "修改失败"
-			br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
-			if utils.IsErrNoRow(err) {
-				br.Msg = "问题不存在"
-				br.IsSendEmail = false
-			}
-			return
-		}
-		if len(list) > 0 {
-			for _, v := range list {
-				if notIdMap[v.WechatArticleAbstractId] {
-					continue
-				}
-				// 有加入到向量库,那么就加入到待删除的向量库list中
-				if v.VectorKey != `` {
-					vectorKeyList = append(vectorKeyList, v.VectorKey)
-				}
-				wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
-			}
-		}
-	}
-
-	// 删除向量库
-	err = services.DelLlmDoc(vectorKeyList, wechatArticleAbstractIdList)
-	if err != nil {
-		br.Msg = "删除失败"
-		br.ErrMsg = "删除向量库失败,Err:" + err.Error()
-		return
-	}
-
 	// 删除摘要
-	err = obj.DelByIdList(wechatArticleAbstractIdList)
+	err = services.DelWechatArticleAbstract(req.WechatArticleAbstractIdList)
 	if err != nil {
 		br.Msg = "删除失败"
 		br.ErrMsg = "删除失败,Err:" + err.Error()
 		return
 	}
 
-	// 删除es数据
-	for _, wechatArticleAbstractId := range wechatArticleAbstractIdList {
-		go services.DelEsWechatArticleAbstract(wechatArticleAbstractId)
-	}
-
 	br.Ret = 200
 	br.Success = true
 	br.Msg = `删除成功`
@@ -270,57 +254,76 @@ func (c *AbstractController) VectorDel() {
 	wechatArticleAbstractIdList := make([]int, 0)
 
 	obj := rag.WechatArticleAbstract{}
-
-	if !req.IsSelectAll {
-		list, err := obj.GetByIdList(req.WechatArticleAbstractIdList)
-		if err != nil {
-			br.Msg = "修改失败"
-			br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
-			if utils.IsErrNoRow(err) {
-				br.Msg = "问题不存在"
-				br.IsSendEmail = false
-			}
-			return
-		}
-		if len(list) > 0 {
-			for _, v := range list {
-				// 有加入到向量库,那么就加入到待删除的向量库list中
-				if v.VectorKey != `` {
-					vectorKeyList = append(vectorKeyList, v.VectorKey)
-				}
-				wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
-			}
-		}
-	} else {
-		notIdMap := make(map[int]bool)
-		for _, v := range req.NotWechatArticleAbstractIdList {
-			notIdMap[v] = true
-		}
-		_, list, err := getAbstractList(req.KeyWord, req.TagId, 0, 100000)
-		if err != nil {
-			br.Msg = "修改失败"
-			br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
-			if utils.IsErrNoRow(err) {
-				br.Msg = "问题不存在"
-				br.IsSendEmail = false
-			}
-			return
+	list, err := obj.GetByIdList(req.WechatArticleAbstractIdList)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "问题不存在"
+			br.IsSendEmail = false
 		}
-		if len(list) > 0 {
-			for _, v := range list {
-				if notIdMap[v.WechatArticleAbstractId] {
-					continue
-				}
-
-				// 有加入到向量库,那么就加入到待删除的向量库list中
-				if v.VectorKey != `` {
-					vectorKeyList = append(vectorKeyList, v.VectorKey)
-				}
-				wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
+		return
+	}
+	if len(list) > 0 {
+		for _, v := range list {
+			// 有加入到向量库,那么就加入到待删除的向量库list中
+			if v.VectorKey != `` {
+				vectorKeyList = append(vectorKeyList, v.VectorKey)
 			}
+			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
 		}
 	}
 
+	//if !req.IsSelectAll {
+	//	list, err := obj.GetByIdList(req.WechatArticleAbstractIdList)
+	//	if err != nil {
+	//		br.Msg = "修改失败"
+	//		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+	//		if utils.IsErrNoRow(err) {
+	//			br.Msg = "问题不存在"
+	//			br.IsSendEmail = false
+	//		}
+	//		return
+	//	}
+	//	if len(list) > 0 {
+	//		for _, v := range list {
+	//			// 有加入到向量库,那么就加入到待删除的向量库list中
+	//			if v.VectorKey != `` {
+	//				vectorKeyList = append(vectorKeyList, v.VectorKey)
+	//			}
+	//			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
+	//		}
+	//	}
+	//} else {
+	//	notIdMap := make(map[int]bool)
+	//	for _, v := range req.NotWechatArticleAbstractIdList {
+	//		notIdMap[v] = true
+	//	}
+	//	_, list, err := getAbstractList(req.KeyWord, req.TagId, 0, 100000)
+	//	if err != nil {
+	//		br.Msg = "修改失败"
+	//		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+	//		if utils.IsErrNoRow(err) {
+	//			br.Msg = "问题不存在"
+	//			br.IsSendEmail = false
+	//		}
+	//		return
+	//	}
+	//	if len(list) > 0 {
+	//		for _, v := range list {
+	//			if notIdMap[v.WechatArticleAbstractId] {
+	//				continue
+	//			}
+	//
+	//			// 有加入到向量库,那么就加入到待删除的向量库list中
+	//			if v.VectorKey != `` {
+	//				vectorKeyList = append(vectorKeyList, v.VectorKey)
+	//			}
+	//			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
+	//		}
+	//	}
+	//}
+
 	// 删除摘要库
 	err = services.DelLlmDoc(vectorKeyList, wechatArticleAbstractIdList)
 	if err != nil {
@@ -364,57 +367,48 @@ func (c *AbstractController) AddVector() {
 		return
 	}
 
-	wechatArticleAbstractIdList := make([]int, 0)
-
 	obj := rag.WechatArticleAbstract{}
-
-	if !req.IsSelectAll {
-		list, err := obj.GetByIdList(req.WechatArticleAbstractIdList)
-		if err != nil {
-			br.Msg = "修改失败"
-			br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
-			if utils.IsErrNoRow(err) {
-				br.Msg = "问题不存在"
-				br.IsSendEmail = false
-			}
-			return
-		}
-		if len(list) > 0 {
-			for _, v := range list {
-				wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
-			}
-		}
-	} else {
-		notIdMap := make(map[int]bool)
-		for _, v := range req.NotWechatArticleAbstractIdList {
-			notIdMap[v] = true
-		}
-
-		_, list, err := getAbstractList(req.KeyWord, req.TagId, 0, 100000)
-		if err != nil {
-			br.Msg = "修改失败"
-			br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
-			if utils.IsErrNoRow(err) {
-				br.Msg = "问题不存在"
-				br.IsSendEmail = false
-			}
-			return
-		}
-		if len(list) > 0 {
-			for _, v := range list {
-				if notIdMap[v.WechatArticleAbstractId] {
-					continue
-				}
-				wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.WechatArticleAbstractId)
-			}
+	list, err := obj.GetByIdList(req.WechatArticleAbstractIdList)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "问题不存在"
+			br.IsSendEmail = false
 		}
+		return
 	}
-
-	for _, wechatArticleAbstractId := range wechatArticleAbstractIdList {
-		cache.AddWechatArticleLlmOpToCache(wechatArticleAbstractId, ``)
+	for _, v := range list {
+		cache.AddWechatArticleLlmOpToCache(v.WechatArticleId, v.QuestionId, ``)
 	}
 
 	br.Ret = 200
 	br.Success = true
 	br.Msg = `添加向量库中,请稍后查看`
 }
+
+//func init() {
+//	//微信文章
+//	//{
+//	//	obj := rag.WechatArticle{}
+//	//	item, tmpErr := obj.GetById(1722)
+//	//	if tmpErr != nil {
+//	//		// 找不到就处理失败
+//	//		return
+//	//	}
+//	//	services.GenerateWechatArticleAbstract(item, false)
+//	//}
+//
+//	// ETA报告
+//	{
+//		obj := rag.RagEtaReport{}
+//		item, tmpErr := obj.GetById(1)
+//		if tmpErr != nil {
+//			// 找不到就处理失败
+//			return
+//		}
+//		services.GenerateRagEtaReportAbstract(item, false)
+//	}
+//
+//	fmt.Println("结束")
+//}

+ 47 - 0
controllers/llm/kb_controller.go

@@ -3,6 +3,7 @@ package llm
 import (
 	"encoding/json"
 	"eta/eta_api/controllers"
+	"eta/eta_api/controllers/llm/llm_http"
 	"eta/eta_api/models"
 	"eta/eta_api/services/llm/facade"
 )
@@ -49,3 +50,49 @@ func (kbctrl *KbController) SearchDocs() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// KnowledgeList
+// @Title 获取知识库列表
+// @Description  获取知识库列表
+// @Success 101 {object} response.ListResp
+// @router /knowledge/list [get]
+func (ucCtrl *UserChatController) KnowledgeList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		ucCtrl.Data["json"] = br
+		ucCtrl.ServeJSON()
+	}()
+	sysUser := ucCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 获取基础配置, 若未配置则直接返回
+	conf, e := models.GetBusinessConf()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取基础配置失败, Err: " + e.Error()
+		return
+	}
+
+	list := make([]llm_http.KnowledgeList, 0)
+	if conf[models.KnowledgeBaseName] != "" {
+		list = append(list, llm_http.KnowledgeList{
+			KnowledgeName: conf[models.KnowledgeBaseName],
+			Name:          "弘则公共知识库",
+		})
+	}
+	if conf[models.PrivateKnowledgeBaseName] != "" {
+		list = append(list, llm_http.KnowledgeList{
+			KnowledgeName: conf[models.PrivateKnowledgeBaseName],
+			Name:          "弘则私有知识库",
+		})
+	}
+
+	br.Data = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取知识库列表成功"
+}

+ 6 - 1
controllers/llm/llm_http/response.go

@@ -18,7 +18,7 @@ type UserChatAddResp struct {
 
 type AIGCResp struct {
 	Promote Content
-	Answer Content
+	Answer  Content
 }
 
 type Content struct {
@@ -26,3 +26,8 @@ type Content struct {
 	Content  string
 	SendTime string
 }
+
+type KnowledgeList struct {
+	Name          string
+	KnowledgeName string
+}

+ 367 - 27
controllers/llm/question.go

@@ -17,7 +17,7 @@ import (
 )
 
 // QuestionController
-// @Description: 问题库管理
+// @Description: 提示词库管理
 type QuestionController struct {
 	controllers.BaseAuthController
 }
@@ -28,6 +28,7 @@ type QuestionController struct {
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
 // @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   IsQueryDefault   query   bool  true       "是否默认:true,或者false"
 // @Success 200 {object} []*rag.QuestionListListResp
 // @router /question/list [get]
 func (c *QuestionController) List() {
@@ -46,6 +47,7 @@ func (c *QuestionController) List() {
 	pageSize, _ := c.GetInt("PageSize")
 	currentIndex, _ := c.GetInt("CurrentIndex")
 	keyWord := c.GetString("KeyWord")
+	isQueryDefault, _ := c.GetBool("IsQueryDefault")
 
 	var startSize int
 	if pageSize <= 0 {
@@ -64,9 +66,15 @@ func (c *QuestionController) List() {
 		var pars []interface{}
 
 		if keyWord != "" {
-			condition += fmt.Sprintf(` AND %s like ?`, rag.QuestionColumns.QuestionContent)
+			condition += fmt.Sprintf(` AND %s like ? `, rag.QuestionColumns.QuestionContent)
 			pars = append(pars, `%`+keyWord+`%`)
 		}
+
+		if isQueryDefault {
+			condition += fmt.Sprintf(` AND %s = ? `, rag.QuestionColumns.IsDefault)
+			pars = append(pars, 1)
+		}
+
 		obj := new(rag.Question)
 		tmpTotal, list, err := obj.GetPageListByCondition(condition, pars, startSize, pageSize)
 		if err != nil {
@@ -84,7 +92,7 @@ func (c *QuestionController) List() {
 			//"ArticleCreateTime": "desc",
 			//"WechatArticleId":   "desc",
 		}
-		tmpTotal, list, err := elastic.RagQuestionEsSearch(keyWord, startSize, pageSize, sortMap)
+		tmpTotal, list, err := elastic.RagQuestionEsSearch(keyWord, isQueryDefault, startSize, pageSize, sortMap)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -114,6 +122,7 @@ func (c *QuestionController) List() {
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
 // @Param   KeyWord   query   string  true       "搜索关键词"
+// @Param   IsQueryDefault   query   bool  true       "是否默认:true,或者false"
 // @Success 200 {object} []*rag.QuestionListListResp
 // @router /question/title/list [get]
 func (c *QuestionController) TitleList() {
@@ -132,6 +141,7 @@ func (c *QuestionController) TitleList() {
 	pageSize, _ := c.GetInt("PageSize")
 	currentIndex, _ := c.GetInt("CurrentIndex")
 	keyWord := c.GetString("KeyWord")
+	isQueryDefault, _ := c.GetBool("IsQueryDefault")
 
 	var startSize int
 	if pageSize <= 0 {
@@ -153,6 +163,12 @@ func (c *QuestionController) TitleList() {
 			condition += fmt.Sprintf(` AND %s like ?`, rag.QuestionColumns.QuestionContent)
 			pars = append(pars, `%`+keyWord+`%`)
 		}
+
+		if isQueryDefault {
+			condition += fmt.Sprintf(` AND %s = ? `, rag.QuestionColumns.IsDefault)
+			pars = append(pars, 1)
+		}
+
 		obj := new(rag.Question)
 		tmpTotal, list, err := obj.GetTitlePageListByCondition(condition, pars, startSize, pageSize)
 		if err != nil {
@@ -170,16 +186,38 @@ func (c *QuestionController) TitleList() {
 			//"ArticleCreateTime": "desc",
 			//"WechatArticleId":   "desc",
 		}
-		tmpTotal, list, err := elastic.RagQuestionEsSearch(keyWord, startSize, pageSize, sortMap)
+		tmpTotal, esList, err := elastic.RagQuestionEsSearch(keyWord, isQueryDefault, 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)
+
+		if total > 0 {
+			questionIdList := make([]int, 0)
+			for _, v := range esList {
+				questionIdList = append(questionIdList, v.QuestionId)
+			}
+			var condition string
+			var pars []interface{}
+
+			condition += fmt.Sprintf(` AND %s in (?)`, rag.QuestionColumns.QuestionId)
+			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
+			}
+			if list != nil && len(list) > 0 {
+				viewList = list[0].ListToViewList(list)
+			}
+			total = tmpTotal
 		}
+
 	}
 
 	page := paging.GetPaging(currentIndex, pageSize, total)
@@ -197,7 +235,7 @@ func (c *QuestionController) TitleList() {
 // Detail
 // @Title 列表
 // @Description 列表
-// @Param   QuestionId   query   int  true       "问题id"
+// @Param   QuestionId   query   int  true       "提示词id"
 // @Success 200 {object} []*rag.QuestionListListResp
 // @router /question/detail [get]
 func (c *QuestionController) Detail() {
@@ -215,8 +253,8 @@ func (c *QuestionController) Detail() {
 	}
 	questionId, _ := c.GetInt("QuestionId")
 	if questionId <= 0 {
-		br.Msg = "问题id不能为空"
-		br.ErrMsg = "问题id不能为空"
+		br.Msg = "提示词id不能为空"
+		br.ErrMsg = "提示词id不能为空"
 		return
 	}
 
@@ -236,8 +274,8 @@ func (c *QuestionController) Detail() {
 }
 
 // Add
-// @Title 新增问题
-// @Description 新增问题
+// @Title 新增提示词
+// @Description 新增提示词
 // @Param	request	body request.AddQuestionReq true "type json string"
 // @Success 200 Ret=200 新增成功
 // @router /question/add [post]
@@ -256,14 +294,14 @@ func (c *QuestionController) Add() {
 	}
 	req.Content = strings.TrimSpace(req.Content)
 	if req.Content == "" {
-		br.Msg = "请输入问题"
+		br.Msg = "请输入提示词"
 		br.IsSendEmail = false
 		return
 	}
 	//obj := rag.Question{}
 	//_, err = obj.GetByCondition(` AND question_content = ? `, []interface{}{req.Content})
 	//if err == nil {
-	//	br.Msg = "问题已入库,请不要重复添加"
+	//	br.Msg = "提示词已入库,请不要重复添加"
 	//	br.IsSendEmail = false
 	//	return
 	//}
@@ -293,8 +331,8 @@ func (c *QuestionController) Add() {
 }
 
 // Edit
-// @Title 编辑问题
-// @Description 编辑问题
+// @Title 编辑提示词
+// @Description 编辑提示词
 // @Param	request	body request.EditQuestionReq true "type json string"
 // @Success 200 Ret=200 新增成功
 // @router /question/edit [post]
@@ -304,6 +342,7 @@ func (c *QuestionController) Edit() {
 		c.Data["json"] = br
 		c.ServeJSON()
 	}()
+
 	var req request.EditQuestionReq
 	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
 	if err != nil {
@@ -312,13 +351,13 @@ func (c *QuestionController) Edit() {
 		return
 	}
 	if req.QuestionId <= 0 {
-		br.Msg = "问题id不能为空"
+		br.Msg = "提示词id不能为空"
 		br.IsSendEmail = false
 		return
 	}
 	req.Content = strings.TrimSpace(req.Content)
 	if req.Content == "" {
-		br.Msg = "请输入问题"
+		br.Msg = "请输入提示词"
 		br.IsSendEmail = false
 		return
 	}
@@ -327,17 +366,37 @@ func (c *QuestionController) Edit() {
 	item, err := obj.GetByID(req.QuestionId)
 	if err != nil {
 		br.Msg = "修改失败"
-		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+		br.ErrMsg = "修改失败,查找提示词失败,Err:" + err.Error()
 		if utils.IsErrNoRow(err) {
-			br.Msg = "问题不存在"
+			br.Msg = "提示词不存在"
 			br.IsSendEmail = false
 		}
 		return
 	}
+
+	// 编辑提示词:
+	if item.IsDefault == 1 {
+		total, err := services.GetNotFinishGenerateAbstractTaskNumByQuestionId(item.QuestionId)
+		if err != nil {
+			br.Msg = "修改失败"
+			br.ErrMsg = "权限校验失败,Err:" + err.Error()
+			return
+		}
+		if total > 0 {
+			br.Msg = "当前提示词正在生成摘要,请稍后再修改"
+			return
+		}
+	}
+
+	// 添加问题的历史记录
+	rag.AddQuestionHistoryByQuestion(item)
+
 	item.QuestionTitle = utils.GetFirstNChars(req.Content, 20)
 	item.QuestionContent = req.Content
+	item.Version++
+	item.GenerateStatus = `undo`
 	item.ModifyTime = time.Now()
-	err = item.Update([]string{"question_title", "question_content", "modify_time"})
+	err = item.Update([]string{"question_title", "question_content", `version`, `generate_status`, "modify_time"})
 	if err != nil {
 		br.Msg = "修改失败"
 		br.ErrMsg = "修改失败,Err:" + err.Error()
@@ -348,12 +407,12 @@ func (c *QuestionController) Edit() {
 
 	br.Ret = 200
 	br.Success = true
-	br.Msg = `添加成功`
+	br.Msg = `修改成功`
 }
 
 // Del
-// @Title 删除问题
-// @Description 删除问题
+// @Title 删除提示词
+// @Description 删除提示词
 // @Param	request	body request.EditQuestionReq true "type json string"
 // @Success 200 Ret=200 新增成功
 // @router /question/del [post]
@@ -371,7 +430,7 @@ func (c *QuestionController) Del() {
 		return
 	}
 	if req.QuestionId <= 0 {
-		br.Msg = "问题id不能为空"
+		br.Msg = "提示词id不能为空"
 		br.IsSendEmail = false
 		return
 	}
@@ -380,13 +439,23 @@ func (c *QuestionController) Del() {
 	item, err := obj.GetByID(req.QuestionId)
 	if err != nil {
 		br.Msg = "修改失败"
-		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+		br.ErrMsg = "修改失败,查找提示词失败,Err:" + err.Error()
 		if utils.IsErrNoRow(err) {
-			br.Msg = "问题不存在"
+			br.Msg = "提示词不存在"
 			br.IsSendEmail = false
 		}
 		return
 	}
+
+	// 删除提示词:若删除默认提示词,提示:当前提示词不允许删除;若删除非默认提示词:提示删除成功(项目eta4.0,时间:2025-4-16 17:39:38)
+	if item.IsDefault == 1 {
+		br.Msg = "当前提示词不允许删除!"
+		return
+	}
+
+	// 添加问题的历史记录
+	rag.AddQuestionHistoryByQuestion(item)
+
 	err = item.Del()
 	if err != nil {
 		br.Msg = "删除失败"
@@ -402,8 +471,279 @@ func (c *QuestionController) Del() {
 	br.Msg = `删除成功`
 }
 
+// SetDefault
+// @Title 设置默认提示词
+// @Description 设置默认提示词
+// @Param	request	body request.EditQuestionReq true "type json string"
+// @Success 200 Ret=200 设置成功
+// @router /question/default/set [post]
+func (c *QuestionController) SetDefault() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.EditQuestionReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.QuestionId <= 0 {
+		br.Msg = "提示词id不能为空"
+		br.IsSendEmail = false
+		return
+	}
+
+	obj := rag.Question{}
+	item, err := obj.GetByID(req.QuestionId)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,查找提示词失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "提示词不存在"
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	// 判断是否正在取消默认提示词(删除历史摘要)
+	{
+		cacheKey := services.GetDelAbstractByQuestionIdCacheKey(item.QuestionId)
+		if utils.Rc.IsExist(cacheKey) {
+			br.Msg = "取消设置默认提示词后,删除历史摘要中,请稍后再试!"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	if item.IsDefault == 1 {
+		br.Msg = "该提示词已经是默认提示词,无需设置"
+		br.IsSendEmail = false
+		return
+	}
+	item.IsDefault = 1
+	item.GenerateStatus = `undo`
+	item.ModifyTime = time.Now()
+	err = item.Update([]string{"is_default", "generate_status", "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
+	br.Msg = `设置成功`
+}
+
+// UnSetDefault
+// @Title 取消设置默认提示词
+// @Description 取消设置默认提示词
+// @Param	request	body request.EditQuestionReq true "type json string"
+// @Success 200 Ret=200 设置成功
+// @router /question/default/unset [post]
+func (c *QuestionController) UnSetDefault() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.EditQuestionReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.QuestionId <= 0 {
+		br.Msg = "提示词id不能为空"
+		br.IsSendEmail = false
+		return
+	}
+
+	obj := rag.Question{}
+	item, err := obj.GetByID(req.QuestionId)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,查找提示词失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "提示词不存在"
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	if item.IsDefault == 0 {
+		br.Msg = "该提示词不是默认提示词,无需取消"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 如果是取消已经设置成默认的提示词,那么需要判断是否有正在生成摘要的提示词任务,如果存在的话,那么就不允许取消
+	auth, err := services.CheckOpQuestionAuth()
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "权限校验失败,Err:" + err.Error()
+		return
+	}
+	if !auth {
+		br.Msg = "当前有提示词正在生成摘要,请稍后再修改"
+		return
+	}
+
+	item.IsDefault = 0
+	item.GenerateStatus = `undo`
+	item.ModifyTime = time.Now()
+	err = item.Update([]string{"is_default", "generate_status", "modify_time"})
+	if err != nil {
+		br.Msg = "取消设置失败"
+		br.ErrMsg = "取消设置失败,Err:" + err.Error()
+		return
+	}
+	// 新增/编辑ES数据
+	go services.AddOrEditEsRagQuestion(item.QuestionId)
+
+	// 对应的提示词生成的摘要库和向量库内容也取消,同时需要加锁,不允许重复操作
+	go services.DelAbstractByQuestionId(item.QuestionId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `取消设置成功`
+}
+
+// GenerateAbstract
+// @Title 生成摘要
+// @Description 生成摘要
+// @Param	request	body request.EditQuestionReq true "type json string"
+// @Success 200 Ret=200 设置成功
+// @router /question/abstract/generate [post]
+func (c *QuestionController) GenerateAbstract() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.EditQuestionReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.QuestionId <= 0 {
+		br.Msg = "提示词id不能为空"
+		br.IsSendEmail = false
+		return
+	}
+
+	obj := rag.Question{}
+	item, err := obj.GetByID(req.QuestionId)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,查找提示词失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "提示词不存在"
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	if item.IsDefault != 1 {
+		br.Msg = "该提示词不是默认提示词,不允许生成"
+		br.IsSendEmail = false
+		return
+	}
+	if item.GenerateStatus != `undo` {
+		br.Msg = "该提示词已经生成过摘要,不允许重复生成"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 如果是需要对提示词做摘要的生成,那么需要判断是否有正在生成摘要的提示词任务,如果存在的话,那么就不允许生成(暂定,后面可以改成加到任务中去,等上一个批次的任务完成后,继续该任务)
+	auth, err := services.CheckOpQuestionAuth()
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "权限校验失败,Err:" + err.Error()
+		return
+	}
+	if !auth {
+		br.Msg = "当前有提示词正在生成摘要,请稍后再重新生成"
+		return
+	}
+
+	// 标记摘要生成状态,避免重复生成
+	item.GenerateStatus = `done`
+	item.ModifyTime = time.Now()
+	err = item.Update([]string{"generate_status", "modify_time"})
+	if err != nil {
+		br.Msg = "取消设置失败"
+		br.ErrMsg = "取消设置失败,Err:" + err.Error()
+		return
+	}
+	// 新增/编辑ES数据
+	go services.AddOrEditEsRagQuestion(item.QuestionId)
+
+	// 添加任务
+	services.AddGenerateAbstractTask(item, c.SysUser)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `摘要生成中`
+}
+
+// CheckOpAuth
+// @Title 获取
+// @Description 列表
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /question/op_auth/check [get]
+func (c *QuestionController) CheckOpAuth() {
+	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
+	}
+
+	// 如果是需要对提示词做摘要的生成,那么需要判断是否有正在生成摘要的提示词任务,如果存在的话,那么就不允许生成(暂定,后面可以改成加到任务中去,等上一个批次的任务完成后,继续该任务)
+	auth, err := services.CheckOpQuestionAuth()
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "权限校验失败,Err:" + err.Error()
+		return
+	}
+
+	var status, tip string
+	if auth {
+		status = `done`
+		tip = `新摘要生成成功!`
+	} else {
+		status = `processing`
+		tip = `新摘要生成中...`
+	}
+
+	resp := response.QuestionOpAuthResp{
+		Status: status,
+		Tip:    tip,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
 //func init() {
-//	// 问题加到es
+//	// 提示词加到es
 //	{
 //		obj := rag.Question{}
 //		list, _ := obj.GetListByCondition(``, ` `, []interface{}{}, 0, 10000)

+ 422 - 0
controllers/llm/rag_eta_report_abstract.go

@@ -0,0 +1,422 @@
+package llm
+
+import (
+	"encoding/json"
+	"eta/eta_api/cache"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"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"
+	"strconv"
+	"strings"
+)
+
+// RagEtaReportAbstractController
+// @Description: ETA报告摘要管理
+type RagEtaReportAbstractController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title ETA报告摘要列表
+// @Description ETA报告摘要列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /abstract/eta_report/list [get]
+func (c *RagEtaReportAbstractController) List() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyWord := c.GetString("KeyWord")
+	tagIdStr := c.GetString("TagId")
+	questionId, _ := c.GetInt("QuestionId")
+
+	tagIdList := make([]int, 0)
+	if tagIdStr != `` {
+		tagIdStrList := strings.Split(tagIdStr, `,`)
+		for _, v := range tagIdStrList {
+			if v == `0` {
+				continue
+			}
+
+			tagId, tmpErr := strconv.Atoi(v)
+			if tmpErr != nil {
+				br.Msg = "标签ID有误"
+				br.ErrMsg = fmt.Sprintf("标签ID有误, %s", v)
+				return
+			}
+			tagIdList = append(tagIdList, tagId)
+		}
+	}
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 获取列表
+	total, viewList, err := getRagEtaReportAbstractList(keyWord, tagIdList, questionId, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.RagEtaReportAbstractListListResp{
+		List:   viewList,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+func getRagEtaReportAbstractList(keyWord string, tagList []int, questionId, startSize, pageSize int) (total int, viewList []rag.RagEtaReportAbstractView, err error) {
+	//if keyWord == `` {
+	//	var condition string
+	//	var pars []interface{}
+	//	condition += fmt.Sprintf(` AND c.%s = ?`, rag.WechatPlatformColumns.Enabled)
+	//	pars = append(pars, 1)
+	//
+	//	if keyWord != "" {
+	//		condition += fmt.Sprintf(` AND a.%s like ?`, rag.WechatArticleAbstractColumns.Content)
+	//		pars = append(pars, `%`+keyWord+`%`)
+	//	}
+	//
+	//	if tagId > 0 {
+	//		condition += fmt.Sprintf(` AND d.%s = ?`, rag.WechatPlatformTagMappingColumns.TagID)
+	//		pars = append(pars, tagId)
+	//	}
+	//
+	//	obj := new(rag.WechatArticleAbstract)
+	//	tmpTotal, list, tmpErr := obj.GetPageListByTagAndPlatformCondition(condition, pars, startSize, pageSize)
+	//	if tmpErr != nil {
+	//		err = tmpErr
+	//		return
+	//	}
+	//	total = tmpTotal
+	//	viewList = obj.WechatArticleAbstractItem(list)
+	//} else {
+	//	sortMap := map[string]string{
+	//		//"ModifyTime":              "desc",
+	//		//"WechatArticleAbstractId": "desc",
+	//	}
+	//
+	//	obj := new(rag.WechatPlatform)
+	//	platformList, tmpErr := obj.GetListByCondition(` AND enabled = 1 `, []interface{}{}, 0, 100000)
+	//	if tmpErr != nil {
+	//		err = tmpErr
+	//		return
+	//	}
+	//	platformIdList := make([]int, 0)
+	//	for _, v := range platformList {
+	//		platformIdList = append(platformIdList, v.WechatPlatformId)
+	//	}
+	//	tagList := make([]int, 0)
+	//	if tagId > 0 {
+	//		tagList = append(tagList, tagId)
+	//	}
+	//	tmpTotal, list, tmpErr := elastic.WechatArticleAbstractEsSearch(keyWord, tagList, platformIdList, startSize, pageSize, sortMap)
+	//	if tmpErr != nil {
+	//		err = tmpErr
+	//		return
+	//	}
+	//	total = int(tmpTotal)
+	//	if list != nil && len(list) > 0 {
+	//		viewList = list[0].ToViewList(list)
+	//	}
+	//}
+
+	sortMap := map[string]string{
+		//"ModifyTime":              "desc",
+		//"WechatArticleAbstractId": "desc",
+	}
+	if keyWord == `` {
+		sortMap = map[string]string{
+			"CreateTime": "desc",
+			//"WechatArticleAbstractId": "desc",
+		}
+	}
+
+	tmpTotal, list, tmpErr := elastic.RagEtaReportAbstractEsSearch(keyWord, tagList, questionId, startSize, pageSize, sortMap)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	total = int(tmpTotal)
+	if list != nil && len(list) > 0 {
+		viewList = list[0].ToViewList(list)
+	}
+
+	return
+}
+
+// Del
+// @Title 删除ETA报告摘要摘要
+// @Description 删除ETA报告摘要摘要
+// @Param	request	body request.BeachOpRagEtaReportAbstractReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /abstract/eta_report/del [post]
+func (c *RagEtaReportAbstractController) Del() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.BeachOpRagEtaReportAbstractReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if len(req.RagEtaReportAbstractIdList) <= 0 && !req.IsSelectAll {
+		br.Msg = "请选择摘要"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 删除摘要
+	err = services.DelRagEtaReportAbstract(req.RagEtaReportAbstractIdList)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `删除成功`
+}
+
+// VectorDel
+// @Title 删除ETA报告摘要向量库
+// @Description 删除ETA报告摘要向量库
+// @Param	request	body request.BeachOpRagEtaReportAbstractReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /abstract/eta_report/vector/del [post]
+func (c *RagEtaReportAbstractController) VectorDel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.BeachOpRagEtaReportAbstractReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if len(req.RagEtaReportAbstractIdList) <= 0 && !req.IsSelectAll {
+		br.Msg = "请选择摘要"
+		br.IsSendEmail = false
+		return
+	}
+
+	vectorKeyList := make([]string, 0)
+	wechatArticleAbstractIdList := make([]int, 0)
+
+	obj := rag.RagEtaReportAbstract{}
+	list, err := obj.GetByIdList(req.RagEtaReportAbstractIdList)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "问题不存在"
+			br.IsSendEmail = false
+		}
+		return
+	}
+	if len(list) > 0 {
+		for _, v := range list {
+			// 有加入到向量库,那么就加入到待删除的向量库list中
+			if v.VectorKey != `` {
+				vectorKeyList = append(vectorKeyList, v.VectorKey)
+			}
+			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.RagEtaReportAbstractId)
+		}
+	}
+
+	//if !req.IsSelectAll {
+	//	list, err := obj.GetByIdList(req.RagEtaReportAbstractIdList)
+	//	if err != nil {
+	//		br.Msg = "修改失败"
+	//		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+	//		if utils.IsErrNoRow(err) {
+	//			br.Msg = "问题不存在"
+	//			br.IsSendEmail = false
+	//		}
+	//		return
+	//	}
+	//	if len(list) > 0 {
+	//		for _, v := range list {
+	//			// 有加入到向量库,那么就加入到待删除的向量库list中
+	//			if v.VectorKey != `` {
+	//				vectorKeyList = append(vectorKeyList, v.VectorKey)
+	//			}
+	//			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.RagEtaReportAbstractId)
+	//		}
+	//	}
+	//} else {
+	//	notIdMap := make(map[int]bool)
+	//	for _, v := range req.NotRagEtaReportAbstractIdList {
+	//		notIdMap[v] = true
+	//	}
+	//	_, list, err := getRagEtaReportAbstractList(req.KeyWord, req.TagId, 0, 100000)
+	//	if err != nil {
+	//		br.Msg = "修改失败"
+	//		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+	//		if utils.IsErrNoRow(err) {
+	//			br.Msg = "问题不存在"
+	//			br.IsSendEmail = false
+	//		}
+	//		return
+	//	}
+	//	if len(list) > 0 {
+	//		for _, v := range list {
+	//			if notIdMap[v.RagEtaReportAbstractId] {
+	//				continue
+	//			}
+	//
+	//			// 有加入到向量库,那么就加入到待删除的向量库list中
+	//			if v.VectorKey != `` {
+	//				vectorKeyList = append(vectorKeyList, v.VectorKey)
+	//			}
+	//			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.RagEtaReportAbstractId)
+	//		}
+	//	}
+	//}
+
+	// 删除摘要库
+	err = services.DelRagReportLlmDoc(vectorKeyList, wechatArticleAbstractIdList)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// 修改ES数据
+	for _, wechatArticleAbstractId := range wechatArticleAbstractIdList {
+		go services.AddOrEditEsRagEtaReportAbstract(wechatArticleAbstractId)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `删除成功`
+}
+
+// AddVector
+// @Title 新增ETA报告摘要向量库
+// @Description 新增ETA报告摘要向量库
+// @Param	request	body request.BeachOpRagEtaReportAbstractReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /abstract/eta_report/vector/add [post]
+func (c *RagEtaReportAbstractController) AddVector() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.BeachOpRagEtaReportAbstractReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if len(req.RagEtaReportAbstractIdList) <= 0 && !req.IsSelectAll {
+		br.Msg = "请选择摘要"
+		br.IsSendEmail = false
+		return
+	}
+
+	obj := rag.RagEtaReportAbstract{}
+	list, err := obj.GetByIdList(req.RagEtaReportAbstractIdList)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "问题不存在"
+			br.IsSendEmail = false
+		}
+		return
+	}
+	if len(list) > 0 {
+		for _, v := range list {
+			cache.AddRagEtaReportLlmOpToCache(v.RagEtaReportId, v.QuestionId, false)
+		}
+	}
+
+	//if !req.IsSelectAll {
+	//	list, err := obj.GetByIdList(req.RagEtaReportAbstractIdList)
+	//	if err != nil {
+	//		br.Msg = "修改失败"
+	//		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+	//		if utils.IsErrNoRow(err) {
+	//			br.Msg = "问题不存在"
+	//			br.IsSendEmail = false
+	//		}
+	//		return
+	//	}
+	//	if len(list) > 0 {
+	//		for _, v := range list {
+	//			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.RagEtaReportAbstractId)
+	//		}
+	//	}
+	//} else {
+	//	notIdMap := make(map[int]bool)
+	//	for _, v := range req.NotRagEtaReportAbstractIdList {
+	//		notIdMap[v] = true
+	//	}
+	//
+	//	_, list, err := getRagEtaReportAbstractList(req.KeyWord, req.TagId, 0, 100000)
+	//	if err != nil {
+	//		br.Msg = "修改失败"
+	//		br.ErrMsg = "修改失败,查找问题失败,Err:" + err.Error()
+	//		if utils.IsErrNoRow(err) {
+	//			br.Msg = "问题不存在"
+	//			br.IsSendEmail = false
+	//		}
+	//		return
+	//	}
+	//	if len(list) > 0 {
+	//		for _, v := range list {
+	//			if notIdMap[v.RagEtaReportAbstractId] {
+	//				continue
+	//			}
+	//			wechatArticleAbstractIdList = append(wechatArticleAbstractIdList, v.RagEtaReportAbstractId)
+	//		}
+	//	}
+	//}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `添加向量库中,请稍后查看`
+}

+ 85 - 1
controllers/llm/report.go

@@ -68,7 +68,7 @@ func (c *RagEtaReportController) ArticleList() {
 	}
 
 	obj := new(rag.RagEtaReport)
-	tmpTotal, list, err := obj.GetPageListByCondition(condition, pars, startSize, pageSize)
+	tmpTotal, list, err := obj.GetPageListByCondition(``, condition, pars, startSize, pageSize)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -235,6 +235,90 @@ func (c *RagEtaReportController) ArticleDel() {
 	br.Msg = "删除成功"
 }
 
+// AbstractList
+// @Title 摘要列表
+// @Description 我关注的接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   RagEtaReportId   query   int  true       "知识库与eta报告关联的id"
+// @Success 200 {object} []*rag.WechatPlatform
+// @router /eta_report/article/abstract/list [get]
+func (c *RagEtaReportController) AbstractList() {
+	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")
+	ragEtaReportId, _ := c.GetInt("RagEtaReportId")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	//var condition string
+	//var pars []interface{}
+	//
+	//if keyWord != "" {
+	//	condition += fmt.Sprintf(` AND (b.%s like ? or a.%s like ? ) `, rag.WechatPlatformColumns.Nickname, rag.WechatArticleColumns.Title)
+	//	pars = append(pars, `%`+keyWord+`%`, `%`+keyWord+`%`)
+	//}
+	//
+	//if wechatPlatformId > 0 {
+	//	condition += fmt.Sprintf(` AND a.%s = ?`, rag.WechatArticleColumns.WechatPlatformID)
+	//	pars = append(pars, wechatPlatformId)
+	//}
+	//
+	//condition += fmt.Sprintf(` AND b.%s = ? `, rag.WechatPlatformColumns.Enabled)
+	//pars = append(pars, 1)
+
+	var total int
+
+	var condition string
+	var pars []interface{}
+
+	condition += fmt.Sprintf(` AND a.%s  = ?  `, rag.RagEtaReportAbstractColumns.RagEtaReportId)
+	pars = append(pars, ragEtaReportId)
+
+	obj := new(rag.RagEtaReportAbstract)
+	tmpTotal, tmpList, err := obj.GetPageListByPlatformCondition(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	total = tmpTotal
+
+	list := make([]rag.RagEtaReportAbstractView, 0)
+	for _, v := range tmpList {
+		list = append(list, v.ToView())
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.RagEtaReportItemAbstractListListResp{
+		List:   list,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
 //// 修复历史ETA报告到知识库
 //func init() {
 //	idList, err := models.GetAllPublishReportId()

+ 0 - 2
controllers/llm/user_chat_controller.go

@@ -330,5 +330,3 @@ func (ucCtrl *UserChatController) ChatRecordList() {
 	br.Success = true
 	br.Msg = "获取聊天记录成功"
 }
-
-

+ 84 - 0
controllers/llm/wechat_platform.go

@@ -725,6 +725,90 @@ func (c *WechatPlatformController) ArticleDel() {
 	br.Msg = "删除成功"
 }
 
+// AbstractList
+// @Title 摘要列表
+// @Description 我关注的接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   WechatArticleId   query   int  true       "文章id"
+// @Success 200 {object} []*rag.WechatPlatform
+// @router /wechat_platform/article/abstract/list [get]
+func (c *WechatPlatformController) AbstractList() {
+	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")
+	wechatArticleId, _ := c.GetInt("WechatArticleId")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	//var condition string
+	//var pars []interface{}
+	//
+	//if keyWord != "" {
+	//	condition += fmt.Sprintf(` AND (b.%s like ? or a.%s like ? ) `, rag.WechatPlatformColumns.Nickname, rag.WechatArticleColumns.Title)
+	//	pars = append(pars, `%`+keyWord+`%`, `%`+keyWord+`%`)
+	//}
+	//
+	//if wechatPlatformId > 0 {
+	//	condition += fmt.Sprintf(` AND a.%s = ?`, rag.WechatArticleColumns.WechatPlatformID)
+	//	pars = append(pars, wechatPlatformId)
+	//}
+	//
+	//condition += fmt.Sprintf(` AND b.%s = ? `, rag.WechatPlatformColumns.Enabled)
+	//pars = append(pars, 1)
+
+	var total int
+
+	var condition string
+	var pars []interface{}
+
+	condition += fmt.Sprintf(` AND a.%s  = ?  `, rag.WechatArticleAbstractColumns.WechatArticleID)
+	pars = append(pars, wechatArticleId)
+
+	obj := new(rag.WechatArticleAbstract)
+	tmpTotal, tmpList, err := obj.GetPageListByPlatformCondition(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	total = tmpTotal
+
+	list := make([]rag.WechatArticleAbstractView, 0)
+	for _, v := range tmpList {
+		list = append(list, v.ToView())
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.WechatArticleItemAbstractListListResp{
+		List:   list,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
 //func init() {
 //	//obj := rag.WechatPlatform{}
 //	//item, _ := obj.GetByID(2)

+ 13 - 4
controllers/material/material.go

@@ -786,22 +786,31 @@ func (this *MaterialController) List() {
 		switch this.Lang {
 		case utils.LANG_EN:
 			if len(keywordList) == 1 {
-				condition += ` AND  ( material_name_en LIKE '%` + keyword + `%' )`
+				likeKey := `%` + keyword + `%`
+
+				condition += ` AND  ( material_name_en LIKE ? )`
+				pars = append(pars, likeKey)
 			} else {
 				condition += ` AND  (`
 				for _, key := range keywordList {
-					condition += ` material_name_en LIKE '%` + key + `%' AND`
+					likeKey := `%` + key + `%`
+					condition += ` material_name_en LIKE ? AND`
+					pars = append(pars, likeKey)
 				}
 				condition = strings.TrimSuffix(condition, "AND")
 				condition += ` )`
 			}
 		default:
 			if len(keywordList) == 1 {
-				condition += ` AND  ( material_name LIKE '%` + keyword + `%' )`
+				likeKey := `%` + keyword + `%`
+				condition += ` AND  ( material_name LIKE ? )`
+				pars = append(pars, likeKey)
 			} else {
 				condition += ` AND  (`
 				for _, key := range keywordList {
-					condition += ` material_name LIKE '%` + key + `%' AND`
+					likeKey := `%` + key + `%`
+					condition += ` material_name LIKE ? AND `
+					pars = append(pars, likeKey)
 				}
 				condition = strings.TrimSuffix(condition, "AND")
 				condition += ` )`

+ 104 - 17
controllers/report_chapter.go

@@ -13,6 +13,7 @@ import (
 	"path"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -159,7 +160,6 @@ func (this *ReportController) AddChapter() {
 	//reportChapterInfo.CanvasColor = req.CanvasColor
 	//reportChapterInfo.HeadResourceId = req.HeadResourceId
 	//reportChapterInfo.EndResourceId = req.EndResourceId
-
 	err, errMsg := services.AddChapterBaseInfoAndPermission(reportInfo, reportChapterInfo, req.PermissionIdList, req.AdminIdList)
 	if err != nil {
 		br.Msg = "保存失败"
@@ -169,7 +169,9 @@ func (this *ReportController) AddChapter() {
 		br.ErrMsg = "保存失败,Err:" + err.Error()
 		return
 	}
-
+	if reportInfo.ReportLayout == 3 {
+		br.Data = reportInfo.FreeLayoutConfig
+	}
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -311,10 +313,6 @@ func (this *ReportController) EditDayWeekChapter() {
 		br.Msg = "报告章节ID有误"
 		return
 	}
-	if req.Content == "" {
-		br.Msg = "请输入内容"
-		return
-	}
 
 	// 获取章节详情
 	reportChapterInfo, err := models.GetReportChapterInfoById(reportChapterId)
@@ -331,7 +329,14 @@ func (this *ReportController) EditDayWeekChapter() {
 		br.ErrMsg = "报告信息有误, Err: " + err.Error()
 		return
 	}
-
+	if req.Content == "" && reportInfo.ReportLayout != 3 {
+		br.Msg = "请输入内容"
+		return
+	}
+	if reportInfo.ReportLayout == 3 && req.FreeLayoutConfig == "" {
+		br.Msg = "请输入自由布局配置"
+		return
+	}
 	// 操作权限校验
 	hasAuth, msg, errMsg, isSendEmail := checkOpPermission(sysUser, reportInfo, reportChapterInfo, true, this.Lang)
 	if !hasAuth {
@@ -431,11 +436,57 @@ func (this *ReportController) EditDayWeekChapter() {
 			})
 		}
 	}
-	err = models.UpdateChapterAndTicker(reportInfo, reportChapterInfo, updateCols, tickerList)
-	if err != nil {
-		br.Msg = "保存失败"
-		br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
-		return
+	if reportInfo.ReportLayout == 3 {
+		//对自由布局的数据做一个处理
+		//自由布局更新每页的数据
+		ormList := report.ToOrmViewList(req.FreeLayoutContentPages, true, reportInfo.Id, reportChapterId)
+		var wg sync.WaitGroup
+		wg.Add(len(ormList))
+		for _, v := range ormList {
+			go func(v *report.ReportFreeLayout) {
+				defer wg.Done()
+				content := v.Content
+				if content != "" {
+					// 处理关联excel的表格id
+					content = services.HandleReportContentTable(reportInfo.Id, content)
+					content = services.HandleReportContent(content, "del", nil)
+					e := utils.ContentXssCheck(content)
+					if e != nil {
+						br.Msg = "存在非法标签"
+						br.ErrMsg = "存在非法标签, Err: " + e.Error()
+						return
+					}
+					contentClean, e := services.FilterReportContentBr(content)
+					if e != nil {
+						br.Msg = "内容去除前后空格失败"
+						br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
+						return
+					}
+					content = contentClean
+					if v.ContentStruct != `` {
+						v.ContentStruct = services.HandleReportContentStructTable(reportChapterInfo.ReportId, v.ContentStruct)
+						v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "del", nil)
+					}
+					v.Content = html.EscapeString(content)
+					v.ContentStruct = html.EscapeString(v.ContentStruct)
+				}
+			}(v)
+		}
+		wg.Wait()
+		reportInfo.FreeLayoutConfig = req.FreeLayoutConfig
+		err = models.UpdateChapterFreeLayoutContentPage(reportInfo, reportChapterInfo, updateCols, tickerList, ormList)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		err = models.UpdateChapterAndTicker(reportInfo, reportChapterInfo, updateCols, tickerList)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "报告章节内容保存失败, Err: " + err.Error()
+			return
+		}
 	}
 
 	// 标记更新中
@@ -537,7 +588,9 @@ func (this *ReportController) DelChapter() {
 		br.ErrMsg = "删除失败,Err:" + err.Error()
 		return
 	}
-
+	if reportInfo.ReportLayout == 3 {
+		go report.DeleteChapters(reportInfo.Id, reportChapterInfo.ReportChapterId)
+	}
 	// 备份关键数据
 	chapters := make([]*models.ReportChapter, 0)
 	chapters = append(chapters, reportChapterInfo)
@@ -778,13 +831,38 @@ func (this *ReportController) GetDayWeekChapter() {
 		br.ErrMsg = "无操作权限"
 		return
 	}
+	var pageNum int
+	var pages []*report.ContentPage
+	if reportInfo.ReportLayout == 3 {
+		pages, err = report.GetSingleFreeLayoutChapterPagesByReportId(reportInfo.Id, reportChapterId)
+		if err != nil {
+			br.Msg = "获取自由布局页面列表"
+			br.ErrMsg = "获取自由布局页面列表,Err:" + err.Error()
+			return
+		}
+		if len(pages) == 0 {
+			//获取当前章节前置章节总页数
+			pageNum, err = report.GetPrevFreeLayoutChaptersPagesByChapterId(reportInfo.Id, reportChapterId)
+			if err != nil {
+				br.Msg = "获取自由布局前置章节总页数"
+				br.ErrMsg = "获取自由布局前置章节总页数,Err:" + err.Error()
+				return
+			}
 
+		} else {
+			for _, page := range pages {
+				page.Content = html.UnescapeString(page.Content)
+				page.ContentStruct = html.UnescapeString(page.ContentStruct)
+				page.Content = services.HandleReportContentTable(page.ReportId, page.Content)
+				page.ContentStruct = services.HandleReportContentStructTable(page.ReportId, page.ContentStruct)
+			}
+		}
+	}
 	chapterItem.Content = html.UnescapeString(chapterItem.Content)
 	chapterItem.ContentSub = html.UnescapeString(chapterItem.ContentSub)
 	chapterItem.ContentStruct = html.UnescapeString(chapterItem.ContentStruct)
 	chapterItem.Content = services.HandleReportContentTable(chapterItem.ReportId, chapterItem.Content)
 	chapterItem.ContentStruct = services.HandleReportContentStructTable(chapterItem.ReportId, chapterItem.ContentStruct)
-
 	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
 	if err != nil {
 		br.Msg = "获取失败"
@@ -796,6 +874,12 @@ func (this *ReportController) GetDayWeekChapter() {
 		tokenMap := make(map[string]string)
 		chapterItem.Content = services.HandleReportContent(chapterItem.Content, "add", tokenMap)
 		chapterItem.ContentStruct = services.HandleReportContentStruct(chapterItem.ContentStruct, "add", tokenMap)
+		if reportInfo.ReportLayout == 3 {
+			for _, page := range pages {
+				page.Content = services.HandleReportContent(page.Content, "add", tokenMap)
+				page.ContentStruct = services.HandleReportContentStruct(page.ContentStruct, "add", tokenMap)
+			}
+		}
 	}
 
 	// 授权用户列表map
@@ -833,9 +917,12 @@ func (this *ReportController) GetDayWeekChapter() {
 	}
 
 	resp := models.ReportChapterItemResp{
-		ReportChapterItem: *chapterItem,
-		GrandAdminIdList:  chapterGrantIdList,
-		PermissionIdList:  chapterPermissionIdList,
+		FreeLayoutContentPages: pages,
+		FreeLayoutConfig:       reportInfo.FreeLayoutConfig,
+		PreviousPagesNum:       pageNum,
+		ReportChapterItem:      *chapterItem,
+		GrandAdminIdList:       chapterGrantIdList,
+		PermissionIdList:       chapterPermissionIdList,
 	}
 
 	// 获取当前编辑状态

+ 199 - 48
controllers/report_v2.go

@@ -18,6 +18,7 @@ import (
 	"os"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -476,7 +477,6 @@ func (this *ReportController) Add() {
 	item.ReportVersion = req.ReportVersion
 	item.AdminId = sysUser.AdminId
 	item.AdminRealName = sysUser.RealName
-
 	item.ClassifyIdThird = req.ClassifyIdThird
 	item.ClassifyNameThird = classifyMap[req.ClassifyIdThird]
 
@@ -499,6 +499,7 @@ func (this *ReportController) Add() {
 	item.ReportLayout = req.ReportLayout
 	item.IsPublicPublish = req.IsPublicPublish
 	item.ReportCreateTime = time.Now()
+	item.MiniShow = req.MiniShow
 
 	reportDate := time.Now()
 	t, _ := time.ParseInLocation(utils.FormatDate, req.CreateTime, time.Local)
@@ -612,7 +613,6 @@ func (this *ReportController) Edit() {
 		br.ErrMsg = "保存失败,Err:" + err.Error()
 		return
 	}
-
 	reportCode := utils.MD5(strconv.Itoa(int(req.ReportId)))
 	resp := new(models.EditResp)
 	resp.ReportId = req.ReportId
@@ -667,6 +667,7 @@ func (this *ReportController) Detail() {
 		return
 	}
 	chapterList := make([]*models.ReportChapter, 0)
+	pageList := make([]*report.ContentPage, 0)
 	if item.HasChapter == 1 {
 		// 获取章节内容
 		tmpChapterList, err := models.GetPublishedChapterListByReportId(item.Id)
@@ -685,7 +686,40 @@ func (this *ReportController) Detail() {
 			}
 		}
 
+		if item.ReportLayout == 3 {
+			var chapterMap = make(map[int]bool)
+			for _, chapter := range tmpChapterList {
+				chapterMap[chapter.ReportChapterId] = true
+			}
+			pages, err := report.GetFreeLayoutChapterPagesByReportId(item.Id)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取自由布局内容页失败, Err: " + err.Error()
+				return
+			}
+			for _, page := range pages {
+				if chapterMap[page.ReportChapterId] {
+					page.Content = html.UnescapeString(page.Content)
+					page.ContentStruct = html.UnescapeString(page.ContentStruct)
+					pageList = append(pageList, page)
+				}
+			}
+		}
 		//item.Abstract = item.Title
+	} else {
+		if item.ReportLayout == 3 {
+			pages, err := report.GetFreeLayoutPagesByReportId(item.Id)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取自由布局内容页失败, Err: " + err.Error()
+				return
+			}
+			for _, page := range pages {
+				page.Content = html.UnescapeString(page.Content)
+				page.ContentStruct = html.UnescapeString(page.ContentStruct)
+				pageList = append(pageList, page)
+			}
+		}
 	}
 	item.Content = html.UnescapeString(item.Content)
 	item.ContentSub = html.UnescapeString(item.ContentSub)
@@ -739,12 +773,18 @@ func (this *ReportController) Detail() {
 			v.Content = services.HandleReportContent(v.Content, "add", tokenMap)
 			v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "add", tokenMap)
 		}
-
+		if item.ReportLayout == 3 {
+			for _, page := range pageList {
+				page.Content = services.HandleReportContent(page.Content, "add", tokenMap)
+				page.ContentStruct = services.HandleReportContentStruct(page.ContentStruct, "add", tokenMap)
+			}
+		}
 	}
 
 	resp := &models.ReportDetailView{
-		ReportDetail: item,
-		ChapterList:  chapterList,
+		ReportDetail:           item,
+		ChapterList:            chapterList,
+		FreeLayoutContentPages: pageList,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -800,7 +840,12 @@ func (this *ReportController) SaveReportContent() {
 		br.IsSendEmail = false
 		return
 	}
-
+	if reportInfo.ReportLayout == 3 && req.FreeLayoutConfig == "" {
+		br.Msg = "自由布局配置为空"
+		br.ErrMsg = "自由布局配置为空"
+		br.IsSendEmail = false
+		return
+	}
 	// 标记更新中
 	{
 		markStatus, err := services.UpdateReportEditMark(req.ReportId, 0, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
@@ -821,7 +866,7 @@ func (this *ReportController) SaveReportContent() {
 			content = this.GetString("Content")
 		}
 		content = services.HandleReportContent(content, "del", nil)
-		if content != "" {
+		if content != "" || reportInfo.ReportLayout == 3 {
 			e := utils.ContentXssCheck(content)
 			if e != nil {
 				br.Msg = "存在非法标签"
@@ -843,6 +888,7 @@ func (this *ReportController) SaveReportContent() {
 				go alarm_msg.SendAlarmMsg("解析 ContentSub 失败,Err:"+err.Error(), 3)
 				//utils.SendEmail(utils.APPNAME+"失败提醒", "解析 ContentSub 失败,Err:"+err.Error(), utils.EmailSendToUsers)
 			}
+
 			reportInfo.Content = html.EscapeString(content)
 			reportInfo.ContentSub = html.EscapeString(contentSub)
 			reportInfo.ContentStruct = html.EscapeString(req.ContentStruct)
@@ -853,15 +899,58 @@ func (this *ReportController) SaveReportContent() {
 			reportInfo.EndResourceId = req.EndResourceId
 			reportInfo.ModifyTime = time.Now()
 			reportInfo.ContentModifyTime = time.Now()
-			updateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime"}
-			err = reportInfo.UpdateReport(updateCols)
-			if err != nil {
-				br.Msg = "保存失败"
-				br.ErrMsg = "保存失败,Err:" + err.Error()
-				return
+			if reportInfo.ReportLayout == 3 {
+				reportInfo.FreeLayoutConfig = req.FreeLayoutConfig
+				//自由布局更新每页的数据
+				ormList := report.ToOrmViewList(req.FreeLayoutContentPages, false, reportInfo.Id, 0)
+				var wg sync.WaitGroup
+				wg.Add(len(ormList))
+				for _, v := range ormList {
+					go func(v *report.ReportFreeLayout) {
+						defer wg.Done()
+						pageContent := v.Content
+						pageContent = services.HandleReportContent(pageContent, "del", nil)
+						if pageContent != "" {
+							pageErr := utils.ContentXssCheck(pageContent)
+							if pageErr != nil {
+								br.Msg = "存在非法标签"
+								br.ErrMsg = "存在非法标签, Err: " + pageErr.Error()
+								return
+							}
+							var pageContentClean string
+							pageContentClean, pageErr = services.FilterReportContentBr(pageContent)
+							if pageErr != nil {
+								br.Msg = "内容去除前后空格失败"
+								br.ErrMsg = "内容去除前后空格失败, Err: " + pageErr.Error()
+								return
+							}
+							pageContent = pageContentClean
+							v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "del", nil)
+							v.Content = html.EscapeString(pageContent)
+							v.ContentStruct = html.EscapeString(v.ContentStruct)
+						}
+					}(v)
+				}
+				wg.Wait()
+				err = models.InsertOrUpdateReportFreeLayoutContentPage(reportInfo, ormList)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "保存失败,Err:" + err.Error()
+					return
+				}
+			} else {
+				updateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime"}
+				err = reportInfo.UpdateReport(updateCols)
+				if err != nil {
+					br.Msg = "保存失败"
+					br.ErrMsg = "保存失败,Err:" + err.Error()
+					return
+				}
 			}
+
 			go models.AddReportSaveLog(reportId, this.SysUser.AdminId, reportInfo.Content, reportInfo.ContentSub, reportInfo.ContentStruct, reportInfo.CanvasColor, this.SysUser.AdminName, reportInfo.HeadResourceId, reportInfo.EndResourceId)
 		}
+
 	}
 
 	resp := new(models.SaveReportContentResp)
@@ -1027,17 +1116,21 @@ func (this *ReportController) BaseDetail() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-	/*var req models.ReportDetailReq
-	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
-	if err != nil {
-		br.Msg = "参数解析异常!"
-		br.ErrMsg = "参数解析失败,Err:" + err.Error()
-		return
-	}
-	if req.ReportId <= 0 {
-		br.Msg = "参数错误"
-		return
-	}*/
+	/*
+	   var req models.ReportDetailReq
+	   err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+
+	   	if err != nil {
+	   		br.Msg = "参数解析异常!"
+	   		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+	   		return
+	   	}
+
+	   	if req.ReportId <= 0 {
+	   		br.Msg = "参数错误"
+	   		return
+	   	}
+	*/
 	reportId, err := this.GetInt("ReportId")
 	if err != nil {
 		br.Msg = "获取参数失败!"
@@ -1064,7 +1157,28 @@ func (this *ReportController) BaseDetail() {
 
 	reportInfo.Content = html.UnescapeString(reportInfo.Content)
 	reportInfo.ContentSub = html.UnescapeString(reportInfo.ContentSub)
-
+	if reportInfo.ReportLayout == 3 {
+		if reportInfo.HeadResourceId > 0 {
+			headResource, err := smart_report.GetResourceItemById(reportInfo.HeadResourceId)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
+				return
+			}
+			reportInfo.HeadImg = headResource.ImgUrl
+			reportInfo.HeadStyle = headResource.Style
+		}
+		if reportInfo.EndResourceId > 0 {
+			headResource, err := smart_report.GetResourceItemById(reportInfo.EndResourceId)
+			if err != nil {
+				br.Msg = "操作失败"
+				br.ErrMsg = "获取资源库版尾失败, Err: " + err.Error()
+				return
+			}
+			reportInfo.EndImg = headResource.ImgUrl
+			reportInfo.EndStyle = headResource.Style
+		}
+	}
 	grandAdminList := make([]models.ReportDetailViewAdmin, 0)
 	permissionList := make([]models.ReportDetailViewPermission, 0)
 
@@ -1182,11 +1296,12 @@ func (this *ReportController) EditLayoutImg() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
-	//if req.Content == "" {
-	//	br.Msg = "报告内容不能为空"
-	//	return
-	//}
-	//更新标记key
+	//	if req.Content == "" {
+	//		br.Msg = "报告内容不能为空"
+	//		return
+	//	}
+	//
+	// 更新标记key
 	markStatus, err := services.UpdateReportEditMark(int(req.ReportId), 0, sysUser.AdminId, 1, sysUser.RealName, this.Lang)
 	if err != nil {
 		br.Msg = err.Error()
@@ -1194,7 +1309,7 @@ func (this *ReportController) EditLayoutImg() {
 	}
 	if markStatus.Status == 1 {
 		br.Msg = markStatus.Msg
-		//br.Ret = 202 //202 服务器已接受请求,但尚未处理。
+		// br.Ret = 202 //202 服务器已接受请求,但尚未处理。
 		return
 	}
 
@@ -1466,10 +1581,28 @@ func (this *ReportController) PrePublishReport() {
 			}
 		}
 	} else {
-		if reportDetail.Content == "" {
-			br.Msg = "报告内容为空,不可设置定时发布"
-			br.ErrMsg = "报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportDetail.Id)
-			return
+		if reportDetail.ReportLayout != 3 {
+			if reportDetail.Content == "" {
+				br.Msg = "报告内容为空,不可设置定时发布"
+				br.ErrMsg = "报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportDetail.Id)
+				return
+			}
+		} else {
+			pages, err := report.GetFreeLayoutChapterPagesByReportId(reportDetail.Id)
+			if err != nil {
+				br.Msg = "获取自由布局报告失败,不可设置定时发布"
+				br.ErrMsg = "获取自由布局报告失败,不可设置定时发布,Err:" + err.Error()
+				return
+			}
+			var content string
+			for _, page := range pages {
+				content += page.Content
+			}
+			if content == "" {
+				br.Msg = "自由布局报告内容为空,不可设置定时发布"
+				br.ErrMsg = "自由布局报告内容为空,不可设置定时发布,report_id:" + strconv.Itoa(reportDetail.Id)
+				return
+			}
 		}
 	}
 
@@ -1568,10 +1701,28 @@ func (this *ReportController) SubmitApprove() {
 			}
 		}
 	} else {
-		if reportItem.Content == "" {
-			br.Msg = "报告内容为空,不可提交"
-			br.ErrMsg = "报告内容为空,不可提交,report_id:" + strconv.Itoa(reportItem.Id)
-			return
+		if reportItem.ReportLayout != 3 {
+			if reportItem.Content == "" {
+				br.Msg = "报告内容为空,不可提交"
+				br.ErrMsg = "报告内容为空,不可提交,report_id:" + strconv.Itoa(reportItem.Id)
+				return
+			}
+		} else {
+			pages, err := report.GetFreeLayoutChapterPagesByReportId(reportItem.Id)
+			if err != nil {
+				br.Msg = "获取自由布局报告失败,不可提交"
+				br.ErrMsg = "获取自由布局报告失败,不可提交,Err:" + err.Error()
+				return
+			}
+			var content string
+			for _, page := range pages {
+				content += page.Content
+			}
+			if content == "" {
+				br.Msg = "自由布局报告内容为空,不可提交"
+				br.ErrMsg = "自由布局报告内容为空,不可提交,report_id:" + strconv.Itoa(reportItem.Id)
+				return
+			}
 		}
 	}
 
@@ -1827,10 +1978,10 @@ func (this *ReportCommonController) ShareTransform() {
 // @author: Roc
 // @datetime 2024-06-21 09:19:05
 func init() {
-	//fixApproveRecord()
-	//fixChapterPermission()
-	//fixReportEs()
-	//fixSmartReport()
+	// fixApproveRecord()
+	// fixChapterPermission()
+	// fixReportEs()
+	// fixSmartReport()
 }
 
 // 修复研报审批数据
@@ -1846,7 +1997,7 @@ func fixApproveRecord() {
 		return
 	}
 	for _, recordItem := range list {
-		//fmt.Println(recordItem)
+		// fmt.Println(recordItem)
 		recordItem.NodeState = recordItem.State
 		recordItem.NodeApproveUserId = recordItem.ApproveUserId
 		recordItem.NodeApproveUserName = recordItem.ApproveUserName
@@ -1896,7 +2047,7 @@ func fixChapterPermission() {
 		}
 	}
 
-	//notIdList := []int{9675, 9675, 9740, 9749, 9768, 9773, 9791, 9792, 9793, 9850, 9851, 9852, 9852, 9852, 9853, 9854, 9856, 9857, 9857, 9858, 9859, 9860, 9861, 9862, 9862, 9863, 9866}
+	// notIdList := []int{9675, 9675, 9740, 9749, 9768, 9773, 9791, 9792, 9793, 9850, 9851, 9852, 9852, 9852, 9853, 9854, 9856, 9857, 9857, 9858, 9859, 9860, 9861, 9862, 9862, 9863, 9866}
 	notIdList := []int{}
 	allReportChapterList, err := models.GetAllReportChapter()
 	if err != nil {
@@ -2019,7 +2170,7 @@ func fixSmartReport() {
 	for _, v := range list {
 		fmt.Println(v)
 		addList = append(addList, &models.Report{
-			//Id:                  0,
+			// Id:                  0,
 			AddType:            1,
 			ClassifyIdFirst:    v.ClassifyIdFirst,
 			ClassifyNameFirst:  v.ClassifyNameFirst,
@@ -2035,7 +2186,7 @@ func fixSmartReport() {
 			PublishTime:        v.PublishTime,
 			Stage:              v.Stage,
 			MsgIsSend:          v.MsgIsSend,
-			//ThsMsgIsSend:        v.Tha,
+			// ThsMsgIsSend:        v.Tha,
 			Content:             v.Content,
 			VideoUrl:            v.VideoUrl,
 			VideoName:           v.VideoName,
@@ -2131,7 +2282,7 @@ func fixSmartReport() {
 func initPdf() {
 	inFile := "anNNgk3Bbi4LRULwcJgNOPrREYh5.pdf"
 	f2, err := services.GeneralWaterMarkPdf(inFile, "颜鹏 - 18170239278")
-	//f2, err := services.GeneralWaterMarkPdf(inFile, "上周美国馏分油库存累库95万桶,馏分油表需环比下降(-25.6万桶/日)。本期馏分油产量继续抬升,在供增需减的环比变动下库存持续累库。馏分油供应的增加我们认为可能和进口的油种有关,今年以来美国进口的中重质原油占比不断走高,尤其是5")
+	// f2, err := services.GeneralWaterMarkPdf(inFile, "上周美国馏分油库存累库95万桶,馏分油表需环比下降(-25.6万桶/日)。本期馏分油产量继续抬升,在供增需减的环比变动下库存持续累库。馏分油供应的增加我们认为可能和进口的油种有关,今年以来美国进口的中重质原油占比不断走高,尤其是5")
 	if err != nil {
 		fmt.Println("生成失败,ERR:", err)
 		return

+ 6 - 1
controllers/sandbox/sandbox.go

@@ -1727,7 +1727,12 @@ func (this *SandboxController) ListV2() {
 		//pars = append(pars, chartClassifyId)
 	}
 	if keyWord != "" {
-		condition += ` AND  ( name LIKE '%` + keyWord + `%' )`
+
+		//condition += ` AND  ( name LIKE '%` + keyWord + `%' )`
+		likeKey := `%` + keyWord + `%`
+
+		condition += ` AND  name LIKE ? `
+		pars = append(pars, likeKey)
 	}
 
 	//只看我的

+ 4 - 1
controllers/sys_admin.go

@@ -1298,9 +1298,12 @@ func (this *SysAdminController) ResetPass() {
 	_ = utils.Rc.Delete(utils.CACHE_KEY_ADMIN_ID)
 	abnormalKey := fmt.Sprint(utils.CACHE_ABNORMAL_LOGIN, adminInfo.AdminName)
 	errPassKey := fmt.Sprint(utils.CACHE_LOGIN_ERR_PASS, adminInfo.AdminName)
+	mobileAbnormalKey := fmt.Sprint(utils.MOBILE_CACHE_ABNORMAL_LOGIN, adminInfo.AdminName)
+	mobileErrPassKey := fmt.Sprint(utils.MOBILE_CACHE_LOGIN_ERR_PASS, adminInfo.AdminName)
 	_ = utils.Rc.Delete(abnormalKey)
 	_ = utils.Rc.Delete(errPassKey)
-
+	_ = utils.Rc.Delete(mobileAbnormalKey)
+	_ = utils.Rc.Delete(mobileErrPassKey)
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true

+ 8 - 1
controllers/sys_role.go

@@ -728,7 +728,14 @@ func (this *SysRoleController) SystemConfig() {
         system.BusinessConf{
         ConfKey: "KnowledgeBaseName",
     	ConfVal: conf["KnowledgeBaseName"],
-    })
+    },  system.BusinessConf{
+			ConfKey: "NotBackendGenerate",
+			ConfVal: conf["NotBackendGenerate"],
+
+	}, system.BusinessConf{
+		ConfKey: models.KnowledgeBaseName,
+		ConfVal: conf[models.KnowledgeBaseName],
+	})
 
 	osc := system.BusinessConf{
 		ConfKey: "ObjectStorageClient",

+ 18 - 0
controllers/sys_user.go

@@ -371,6 +371,21 @@ func (this *SysUserController) AuthCodeLogin() {
 		return
 	}
 
+	// 查询一下用户是否被禁用
+	sysAdmin, e := system.GetSysUserById(data.AdminId)
+	if e != nil && !utils.IsErrNoRow(e) {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取用户信息失败, %v", e)
+		return
+	}
+	if sysAdmin != nil && sysAdmin.Enabled != 1 {
+		br.Ret = 408
+		br.Msg = "您的账号已被禁用,如需登录,请联系管理员"
+		j, _ := json.Marshal(data)
+		br.ErrMsg = fmt.Sprintf("AuthCodeLogin, 账户信息异常:%s", j)
+		return
+	}
+
 	br.Data = data
 	br.Ret = 200
 	br.Success = true
@@ -407,6 +422,9 @@ func (this *SysUserController) SystemConfig() {
 	}, system.BusinessConf{
 		ConfKey: "LogoutUrl",
 		ConfVal: conf["LogoutUrl"],
+	}, system.BusinessConf{
+		ConfKey: "UserLogoutUrl",
+		ConfVal: conf["UserLogoutUrl"],
 	})
 
 	br.Data = list

+ 14 - 62
controllers/user_login.go

@@ -881,6 +881,8 @@ func (this *UserLoginController) ForgetResetPass() {
 	abnormalKey := fmt.Sprint(utils.CACHE_ABNORMAL_LOGIN, req.UserName)
 	_ = utils.Rc.Delete(abnormalKey)
 	_ = utils.Rc.Delete(successKey)
+	mobileAbnormalKey := fmt.Sprint(utils.MOBILE_CACHE_ABNORMAL_LOGIN, req.UserName)
+	_ = utils.Rc.Delete(mobileAbnormalKey)
 
 	// 同步ETA用户缓存
 	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
@@ -1010,73 +1012,23 @@ func (this *UserLoginController) BaseInfo() {
 		this.ServeJSON()
 	}()
 
-	icp, e := models.GetBusinessConfByKey("ICPLicense")
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
-		return
-	}
-
-	title, e := models.GetBusinessConfByKey("ETATitle")
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
-		return
+	configKeys := []string{
+		"ICPLicense", "ETATitle", "TabName", "LogoCN", "LogoEN", "LogoCNMini", "LogoENMini", "LoginLeftImg",
+		"ETASubTitleCN", "ETASubTitleEN",
 	}
-
-	tabName, e := models.GetBusinessConfByKey("TabName")
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
-		return
-	}
-
-	logoCn, e := models.GetBusinessConfByKey("LogoCN")
+	configOb := new(models.BusinessConf)
+	list, e := configOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
 	if e != nil {
 		br.Msg = "获取失败"
-		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
+		br.ErrMsg = fmt.Sprintf("获取配置失败, %v", e)
 		return
 	}
-
-	logoEn, e := models.GetBusinessConfByKey("LogoEN")
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
-		return
-	}
-
-	logoCnMini, e := models.GetBusinessConfByKey("LogoCNMini")
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
-		return
-	}
-
-	logoEnMini, e := models.GetBusinessConfByKey("LogoENMini")
-	if e != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取商家配置失败, Err: " + e.Error()
-		return
-	}
-
-	type BaseInfoResp struct {
-		Icp        *models.BusinessConf `description:"Icp信息"`
-		ETATitle   *models.BusinessConf `description:"eta系统名称"`
-		TabName    *models.BusinessConf `description:"tab页名称"`
-		LogoCn     *models.BusinessConf `description:"中文logo"`
-		LogoEn     *models.BusinessConf `description:"英文logo"`
-		LogoCnMini *models.BusinessConf `description:"中文logoMini"`
-		LogoEnMini *models.BusinessConf `description:"英文logoMini"`
-	}
-
-	resp := BaseInfoResp{
-		Icp:        icp,
-		ETATitle:   title,
-		TabName:    tabName,
-		LogoCn:     logoCn,
-		LogoEn:     logoEn,
-		LogoCnMini: logoCnMini,
-		LogoEnMini: logoEnMini,
+	resp := make(map[string]*models.BusinessConf)
+	for _, v := range list {
+		if !utils.InArrayByStr(configKeys, v.ConfKey) {
+			continue
+		}
+		resp[v.ConfKey] = v
 	}
 
 	br.Data = resp

+ 71 - 9
models/ai_predict_model/ai_predict_model_index.go

@@ -6,6 +6,7 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -229,7 +230,7 @@ type AiPredictModelIndexPageListResp struct {
 }
 
 // RemoveIndexAndData 删除标的及数据
-func (m *AiPredictModelIndex) RemoveIndexAndData(indexId int) (err error) {
+func (m *AiPredictModelIndex) RemoveIndexAndData(indexId int, chartIds []int) (err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	tx := o.Begin()
 	defer func() {
@@ -252,6 +253,21 @@ func (m *AiPredictModelIndex) RemoveIndexAndData(indexId int) (err error) {
 		err = fmt.Errorf("remove index data err: %v", e)
 		return
 	}
+
+	// 删除图表
+	if len(chartIds) == 0 {
+		return
+	}
+	sql = ` DELETE FROM chart_info WHERE chart_info_id IN ?`
+	if e = tx.Exec(sql, chartIds).Error; e != nil {
+		err = fmt.Errorf("remove charts err: %v", e)
+		return
+	}
+	sql = ` DELETE FROM chart_edb_mapping WHERE chart_info_id IN ?`
+	if e = tx.Exec(sql, chartIds).Error; e != nil {
+		err = fmt.Errorf("remove chart mappings err: %v", e)
+		return
+	}
 	return
 }
 
@@ -277,12 +293,18 @@ func GetFirstAiPredictModelIndexByClassifyId(classifyId int) (item *AiPredictMod
 }
 
 type AiPredictModelImportData struct {
-	Index *AiPredictModelIndex
-	Data  []*AiPredictModelData
+	Index  *AiPredictModelIndex
+	Data   []*AiPredictModelData
+	Charts []*AiPredictModelImportCharts
+}
+
+type AiPredictModelImportCharts struct {
+	ChartInfo   *data_manage.ChartInfo
+	EdbMappings []*data_manage.ChartEdbMapping
 }
 
 // ImportIndexAndData 导入数据
-func (m *AiPredictModelIndex) ImportIndexAndData(createIndexes, updateIndexes []*AiPredictModelImportData, updateCols []string) (err error) {
+func (m *AiPredictModelIndex) ImportIndexAndData(createIndexes, updateIndexes []*AiPredictModelImportData, updateCols []string) (chartIds []int, err error) {
 	if len(createIndexes) == 0 && len(updateIndexes) == 0 {
 		return
 	}
@@ -349,23 +371,43 @@ func (m *AiPredictModelIndex) ImportIndexAndData(createIndexes, updateIndexes []
 
 	if len(createIndexes) > 0 {
 		for _, v := range createIndexes {
-			e := tx.Create(v.Index).Error
-			if e != nil {
+			if e := tx.Create(v.Index).Error; e != nil {
 				err = fmt.Errorf("insert index err: %v", e)
 				return
 			}
 
 			indexId := v.Index.AiPredictModelIndexId
 			for _, d := range v.Data {
-				d.AiPredictModelIndexId = int(indexId)
+				d.AiPredictModelIndexId = indexId
 				d.IndexCode = v.Index.IndexCode
 				d.DataTimestamp = d.DataTime.UnixNano() / 1e6
 			}
-			e = tx.CreateInBatches(v.Data, utils.MultiAddNum).Error
-			if e != nil {
+			if e := tx.CreateInBatches(v.Data, utils.MultiAddNum).Error; e != nil {
 				err = fmt.Errorf("insert index data err: %v", e)
 				return
 			}
+
+			// 图表
+			if len(v.Charts) == 0 {
+				continue
+			}
+			for _, ct := range v.Charts {
+				if e := tx.Create(ct.ChartInfo).Error; e != nil {
+					err = fmt.Errorf("insert chart err: %v", e)
+					return
+				}
+				for _, cm := range ct.EdbMappings {
+					cm.ChartInfoId = ct.ChartInfo.ChartInfoId
+					cm.EdbInfoId = indexId
+					time.Sleep(time.Microsecond)
+					cm.UniqueCode = utils.MD5(fmt.Sprint(utils.CHART_PREFIX, "_", indexId, "_", strconv.FormatInt(time.Now().UnixNano(), 10)))
+				}
+				if e := tx.CreateInBatches(ct.EdbMappings, utils.MultiAddNum).Error; e != nil {
+					err = fmt.Errorf("insert chart mapping err: %v", e)
+					return
+				}
+				chartIds = append(chartIds, ct.ChartInfo.ChartInfoId)
+			}
 		}
 	}
 	return
@@ -402,3 +444,23 @@ type AiPredictModelIndexExtraConfig struct {
 		PredictLegendName string `description:"预测图例的名称(通常为Predicted)"`
 	}
 }
+
+func (m *AiPredictModelIndex) GetSortMax() (sort int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT COALESCE(MAX(sort), 0) AS sort FROM ai_predict_model_index`
+	err = o.Raw(sql).Scan(&sort).Error
+	if err != nil {
+		return
+	}
+	// 查询分类的最大排序
+	sql = `SELECT COALESCE(MAX(sort), 0) AS sort FROM ai_predict_model_classify`
+	var classifySort int
+	err = o.Raw(sql).Scan(&classifySort).Error
+	if err != nil {
+		return
+	}
+	if classifySort > sort {
+		sort = classifySort
+	}
+	return
+}

+ 1 - 0
models/bi_dashboard/bi_dashboard.go

@@ -108,6 +108,7 @@ type AddDashboardListReq struct {
 	Type       int
 	UniqueCode string
 	Sort       int
+	Conf       string
 }
 
 type EditDashboardReq struct {

+ 1 - 0
models/bi_dashboard/bi_dashboard_detail.go

@@ -10,6 +10,7 @@ type BiDashboardDetail struct {
 	BiDashboardDetailId int       `orm:"column(bi_dashboard_detail_id);pk" gorm:"primaryKey" ` // bi看板id
 	BiDashboardId       int       `gorm:"column:bi_dashboard_id" `                             // 看板id
 	Type                int       `gorm:"column:type" `                                        // 1图表 2表格
+	Conf                string    `gorm:"column:conf" `                                        // 配置信息
 	UniqueCode          string    `gorm:"column:unique_code;size:32;not null" `                // 报告唯一编码
 	Sort                int       `gorm:"column:sort" `                                        // 排序字段
 	CreateTime          time.Time `gorm:"column:create_time" `                                 // 创建时间

+ 5 - 0
models/business_conf.go

@@ -61,10 +61,12 @@ const (
 	BusinessConfEsIndexNameDataSource        = "EsIndexNameDataSource"        // ES索引名称-数据源
 	LLMInitConfig                            = "llmInitConfig"
 	KnowledgeBaseName                        = "KnowledgeBaseName"                // 摘要库
+	PrivateKnowledgeBaseName                 = "PrivateKnowledgeBaseName"         // 私有摘要库
 	KnowledgeArticleName                     = "KnowledgeArticleName"             // 原文库
 	BusinessConfEsWechatArticle              = "EsIndexNameWechatArticle"         // ES索引名称-微信文章
 	BusinessConfEsWechatArticleAbstract      = "EsIndexNameWechatArticleAbstract" // ES索引名称-微信文章摘要
 	BusinessConfEsRagQuestion                = "EsIndexNameRagQuestion"           // ES索引名称-知识库问题
+	BusinessConfEsRagEtaReportAbstract       = "EsIndexNameRagEtaReportAbstract"  // ES索引名称-eta报告摘要
 	BusinessConfIsOpenChartExpired           = "IsOpenChartExpired"               // 是否开启图表有效期鉴权/报告禁止复制
 	BusinessConfReportChartExpiredTime       = "ReportChartExpiredTime"           // 图表有效期鉴权时间,单位:分钟
 	BusinessConfOssUrlReplace                = "OssUrlReplace"                    // OSS地址替换-兼容内网客户用
@@ -282,6 +284,9 @@ func InitBusinessConf() {
 	if BusinessConfMap[BusinessConfEsRagQuestion] != "" {
 		utils.EsRagQuestionName = BusinessConfMap[BusinessConfEsRagQuestion]
 	}
+	if BusinessConfMap[BusinessConfEsRagEtaReportAbstract] != "" {
+		utils.EsRagEtaReportAbstractName = BusinessConfMap[BusinessConfEsRagEtaReportAbstract]
+	}
 	confStr := BusinessConfMap[LLMInitConfig]
 	if confStr != "" {
 		var config LLMConfig

+ 7 - 0
models/chart_permission.go

@@ -284,6 +284,7 @@ type SimpleChartPermission struct {
 	ChartPermissionName string                   `description:"品种名称"`
 	Sort                int                      `description:"排序"`
 	Children            []*SimpleChartPermission `description:"子分类"`
+	//IsLatestEdit        bool                     `description:"是否是最后一级"`
 }
 
 func FormatChartPermission2Simple(origin *ChartPermission) (item *SimpleChartPermission) {
@@ -321,3 +322,9 @@ func GetChartPermissionByIdList(permissionIdList []int) (items []*ChartPermissio
 	err = global.DbMap[utils.DbNameReport].Raw(sql, permissionIdList).Find(&items).Error
 	return
 }
+
+type FaCalendarPermissionResp struct {
+	List                  []*SimpleChartPermission
+	CheckedPermissionId   int
+	CheckedPermissionName string
+}

+ 12 - 12
models/data_manage/base_from_gpr_risk.go

@@ -48,10 +48,10 @@ type BaseFromGprRiskIndexList struct {
 }
 
 func (baseFromGprRiskIndexList *BaseFromGprRiskIndexList) AfterFind(tx *gorm.DB) (err error) {
-			baseFromGprRiskIndexList.CreateTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskIndexList.CreateTime)
-			baseFromGprRiskIndexList.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskIndexList.ModifyTime)
-			baseFromGprRiskIndexList.StartDate = utils.GormDateStrToDateStr(baseFromGprRiskIndexList.StartDate)
-			baseFromGprRiskIndexList.EndDate = utils.GormDateStrToDateStr(baseFromGprRiskIndexList.EndDate)
+	baseFromGprRiskIndexList.CreateTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskIndexList.CreateTime)
+	baseFromGprRiskIndexList.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskIndexList.ModifyTime)
+	baseFromGprRiskIndexList.StartDate = utils.GormDateStrToDateStr(baseFromGprRiskIndexList.StartDate)
+	baseFromGprRiskIndexList.EndDate = utils.GormDateStrToDateStr(baseFromGprRiskIndexList.EndDate)
 	return
 }
 
@@ -180,9 +180,9 @@ func GetGprRiskDataDataTimeByIndexId(indexIdList []int) (items []string, err err
 	}
 	sql := ` SELECT DISTINCT data_time FROM base_from_gpr_risk_data WHERE base_from_gpr_risk_index_id IN (` + utils.GetOrmInReplace(len(indexIdList)) + `) ORDER BY data_time DESC`
 	err = global.DbMap[utils.DbNameIndex].Raw(sql, indexIdList).Find(&items).Error
-		for i, item := range items {
-			items[i] = utils.GormDateStrToDateStr(item)
-		}
+	for i, item := range items {
+		items[i] = utils.GormDateStrToDateStr(item)
+	}
 	return
 }
 
@@ -198,9 +198,9 @@ type BaseFromGprRiskData struct {
 }
 
 func (baseFromGprRiskData *BaseFromGprRiskData) AfterFind(tx *gorm.DB) (err error) {
-			baseFromGprRiskData.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskData.ModifyTime)
-			baseFromGprRiskData.CreateTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskData.CreateTime)
-			baseFromGprRiskData.DataTime = utils.GormDateStrToDateStr(baseFromGprRiskData.DataTime)
+	baseFromGprRiskData.ModifyTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskData.ModifyTime)
+	baseFromGprRiskData.CreateTime = utils.GormDateStrToDateTimeStr(baseFromGprRiskData.CreateTime)
+	baseFromGprRiskData.DataTime = utils.GormDateStrToDateStr(baseFromGprRiskData.DataTime)
 	return
 }
 
@@ -223,12 +223,12 @@ type BatchCheckGprRiskEdbReq struct {
 }
 
 // GetGprRiskItemList 模糊查询GprRisk数据库指标列表
-func GetGprRiskItemList(condition string) (items []*BaseFromGprRiskIndexSearchItem, err error) {
+func GetGprRiskItemList(condition string, pars []interface{}) (items []*BaseFromGprRiskIndexSearchItem, err error) {
 	sql := "SELECT * FROM base_from_gpr_risk_index  WHERE 1=1"
 	if condition != "" {
 		sql += condition
 	}
-	err = global.DbMap[utils.DbNameIndex].Raw(sql).Find(&items).Error
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars).Find(&items).Error
 	return
 }
 

+ 55 - 55
models/data_manage/base_from_purang.go

@@ -14,39 +14,39 @@ import (
 
 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
+	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:"-"`
+	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) {
@@ -64,18 +64,18 @@ type BaseFromPurangIndexSearchList struct {
 
 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:"-"`
+	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 {
@@ -191,12 +191,12 @@ func GetPurangDataDataTimeByIndexId(indexIdList []int) (items []string, err erro
 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
+	IndexCode             string
+	DataTime              string
+	Value                 string
+	CreateTime            string
+	ModifyTime            string
+	DataTimestamp         int64
 }
 
 func (baseFromPurangData *BaseFromPurangData) AfterFind(tx *gorm.DB) (err error) {
@@ -208,10 +208,10 @@ func (baseFromPurangData *BaseFromPurangData) AfterFind(tx *gorm.DB) (err error)
 
 type BaseFromPurangIndexSearchItem struct {
 	BaseFromPurangIndexId int `orm:"column(base_from_purang_index_id);pk"`
-	ClassifyId             int
-	ParentClassifyId       int
-	IndexCode              string
-	IndexName              string
+	ClassifyId            int
+	ParentClassifyId      int
+	IndexCode             string
+	IndexName             string
 }
 
 // BatchCheckPurangEdbReq 指标数据结构体
@@ -224,12 +224,12 @@ type BatchCheckPurangEdbReq struct {
 }
 
 // GetPurangItemList 模糊查询Purang数据库指标列表
-func GetPurangItemList(condition string) (items []*BaseFromPurangIndexSearchItem, err error) {
+func GetPurangItemList(condition string, pars []interface{}) (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
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, pars).Find(&items).Error
 	return
 }
 
@@ -271,7 +271,7 @@ func (item *BaseFromPurangIndex) Update(cols []string) (err error) {
 // EditPurangIndexInfoResp 新增指标的返回
 type EditPurangIndexInfoResp struct {
 	BaseFromPurangIndexId int    `description:"指标ID"`
-	IndexCode              string `description:"指标code"`
+	IndexCode             string `description:"指标code"`
 }
 
 type PurangIndexSource2EdbReq struct {
@@ -289,4 +289,4 @@ func GetPurangFrequencyByClassifyId(classifyId int) (items []*GlFrequency, err e
 	sql += ` GROUP BY frequency ORDER BY frequency ASC `
 	err = global.DbMap[utils.DbNameIndex].Raw(sql, classifyId).Find(&items).Error
 	return
-} 
+}

+ 6 - 0
models/data_manage/chart_edb_mapping.go

@@ -388,3 +388,9 @@ func GetRelationEdbInfoListMappingByCondition(condition string, pars []interface
 	err = o.Raw(sql, pars...).Find(&item).Error
 	return
 }
+
+func GetChartEdbMappingsByChartInfoId(chartInfoId int) (list []*ChartEdbInfoMapping, err error) {
+	sql := ` SELECT * FROM chart_edb_mapping AS a WHERE chart_info_id = ? ORDER BY chart_edb_mapping_id ASC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, chartInfoId).Find(&list).Error
+	return
+}

+ 39 - 0
models/data_manage/chart_info.go

@@ -2976,3 +2976,42 @@ func GetNextChartByClassifyIdAndSource(classifyId, source int) (item *ChartInfo,
 	err = global.DbMap[utils.DbNameIndex].Raw(sql, classifyId, source).First(&item).Error
 	return
 }
+
+// GetAiPredictChartInfoByIndexId 获取AI预测模型图表
+func GetAiPredictChartInfoByIndexId(source, indexId int) (item *ChartInfo, err error) {
+	sql := `SELECT * FROM chart_info WHERE chart_info_id = (
+		  SELECT chart_info_id FROM chart_edb_mapping WHERE source = ? AND edb_info_id = ?
+		) LIMIT 1`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, source, indexId).First(&item).Error
+	return
+}
+
+func (m *ChartInfo) AddChartInfoAndEdbMappings(item *ChartInfo, edbMappings []*ChartEdbMapping) (err error) {
+	if item == nil {
+		return
+	}
+	tx := global.DbMap[utils.DbNameIndex].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	if e := tx.Create(item).Error; e != nil {
+		err = fmt.Errorf("insert chart err: %v", e)
+		return
+	}
+	if len(edbMappings) == 0 {
+		return
+	}
+	for _, v := range edbMappings {
+		v.ChartInfoId = item.ChartInfoId
+	}
+	if e := tx.CreateInBatches(edbMappings, utils.MultiAddNum).Error; e != nil {
+		err = fmt.Errorf("insert chart mapping err: %v", e)
+		return
+	}
+	return
+}

+ 1 - 1
models/data_manage/data_manage_permission/classify_no_auth_record.go

@@ -10,7 +10,7 @@ import (
 // @Description: 资产分类数据权限未授权记录表
 type DataPermissionClassifyNoAuthRecord struct {
 	DataPermissionClassifyNoAuthRecordId int64     `json:"data_permission_classify_no_auth_record_id" orm:"column(data_permission_classify_no_auth_record_id);pk" gorm:"primaryKey" ` // 资产分类数据操作记录id
-	Source                               int32     `json:"source"`                                                                                                                    // 数据来源,1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+	Source                               int32     `json:"source"`                                                                                                                    // 数据来源,1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 	SubSource                            int32     `json:"sub_source"`                                                                                                                // 子来源 :ETA表格中的各种表格类型,以及图表的来源(这个是后续的扩展方向)
 	OpUniqueCode                         string    `json:"op_unique_code"`                                                                                                            // 操作的唯一编码,主要是记录统一操作的日志
 	ClassifyId                           string    `json:"classify_id"`                                                                                                               // 资产分类id(指标、图表、表格)

+ 1 - 1
models/data_manage/data_manage_permission/move.go

@@ -49,7 +49,7 @@ func ModifyDataUserIdByOldUserId(oldUserIdList []int, userId int, userName strin
 		}
 	}()
 
-	// 钢联化工数据库
+	// 上海钢联数据库
 	if isMoveMysteelChemical {
 		sql := `UPDATE base_from_mysteel_chemical_index SET sys_user_id=?,sys_user_real_name=? WHERE sys_user_id in (` + utils.GetOrmInReplace(num) + `)  `
 		err = o.Exec(sql, userId, userName, oldUserIdList).Error

+ 1 - 1
models/data_manage/data_manage_permission/move_record.go

@@ -10,7 +10,7 @@ import (
 // @Description: 数据资产转移记录表
 type DataPermissionMoveRecord struct {
 	DataPermissionMoveRecordId int64     `json:"data_permission_move_record_id" orm:"column(data_permission_move_record_id);pk" gorm:"primaryKey" ` // 数据操作记录id
-	Source                     int32     `json:"source"`                                                                                            // 数据来源,1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+	Source                     int32     `json:"source"`                                                                                            // 数据来源,1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 	SubSource                  int32     `json:"sub_source"`                                                                                        // 子来源 :ETA表格中的各种表格类型,以及图表的来源(这个是后续的扩展方向)
 	OpUniqueCode               string    `json:"op_unique_code"`                                                                                    // 操作的唯一编码,主要是记录统一操作的日志
 	DataId                     string    `json:"data_id"`                                                                                           // 资产id(指标、图表、表格)

+ 1 - 1
models/data_manage/data_manage_permission/no_auth_record.go

@@ -10,7 +10,7 @@ import (
 // @Description: 资产数据权限设置记录表
 type DataPermissionNoAuthRecord struct {
 	DataPermissionNoAuthRecordId int64     `json:"data_permission_no_auth_record_id" orm:"column(data_permission_no_auth_record_id);pk" gorm:"primaryKey" ` // 资产数据操作记录id
-	Source                       int32     `json:"source"`                                                                                                  // 数据来源,1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+	Source                       int32     `json:"source"`                                                                                                  // 数据来源,1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 	SubSource                    int32     `json:"sub_source"`                                                                                              // 子来源 :ETA表格中的各种表格类型,以及图表的来源(这个是后续的扩展方向)
 	OpUniqueCode                 string    `json:"op_unique_code"`                                                                                          // 操作的唯一编码,主要是记录统一操作的日志
 	DataId                       string    `json:"data_id"`                                                                                                 // 资产id(指标、图表、表格)

+ 2 - 2
models/data_manage/edb_data_mysteel_chemical.go

@@ -5,7 +5,7 @@ import (
 	"eta/eta_api/utils"
 )
 
-// GetEdbDataMysteelChemicalMaxOrMinDate 根据钢联化工指标code获取最大、最小日期
+// GetEdbDataMysteelChemicalMaxOrMinDate 根据上海钢联指标code获取最大、最小日期
 func GetEdbDataMysteelChemicalMaxOrMinDate(edbCode string) (minDate, maxDate string, err error) {
 	//o := orm.NewOrmUsingDB("data")
 	sql := ` SELECT MIN(data_time) AS minDate,MAX(data_time) AS maxDate FROM edb_data_mysteel_chemical WHERE edb_code=? `
@@ -20,7 +20,7 @@ func GetEdbDataMysteelChemicalMaxOrMinDate(edbCode string) (minDate, maxDate str
 	return
 }
 
-// 更新钢联化工指标的刷新状态
+// 更新上海钢联指标的刷新状态
 func UpdateMysteelChemicalRefreshStatus(edbCode string, isStop int) (err error) {
 	//o := orm.NewOrmUsingDB("data")
 	sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = ? WHERE index_code =? and is_stop=1`

+ 7 - 5
models/data_manage/edb_info.go

@@ -104,6 +104,7 @@ type EdbInfoFullClassify struct {
 	ClassifyList   []*EdbClassifyIdItems
 	HaveOperaAuth  bool `description:"是否有数据权限,默认:false"`
 	IsSupplierStop int  `description:"是否供应商停更:1:停更,0:未停更"`
+	IsRelation     bool `description:"是否被引用,默认:0"`
 }
 
 func AddEdbInfo(item *EdbInfo) (lastId int64, err error) {
@@ -283,7 +284,7 @@ type BatchAddCheckReq struct {
 	IndexCodes []string `form:"IndexCodes" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
 }
 
-// MysteelChemicalDataBatchAddCheckReq 钢联化工指标批量添加校验
+// MysteelChemicalDataBatchAddCheckReq 上海钢联指标批量添加校验
 type MysteelChemicalDataBatchAddCheckReq struct {
 	// MysteelChemicalDataListReq
 	IndexCodes []string `form:"IndexCodes" description:"全选为false时, 该数组为选中; 全选为true时, 该数组为不选的指标"`
@@ -527,6 +528,7 @@ type EdbInfoList struct {
 	MinValue         float64                 `description:"最小值"`
 	MaxValue         float64                 `description:"最大值"`
 	SearchText       string                  `description:"搜索结果(含高亮)"`
+	IsRelation       bool                    `description:"是否被引用"`
 }
 
 // AfterFind
@@ -761,14 +763,14 @@ type EdbInfoMaxAndMinInfo struct {
 }
 
 func (edbInfoMaxAndMinInfo *EdbInfoMaxAndMinInfo) AfterFind(tx *gorm.DB) (err error) {
-			edbInfoMaxAndMinInfo.MinDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MinDate)
-			edbInfoMaxAndMinInfo.MaxDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MaxDate)
+	edbInfoMaxAndMinInfo.MinDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MinDate)
+	edbInfoMaxAndMinInfo.MaxDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MaxDate)
 	return
 }
 
 func (edbInfoMaxAndMinInfo *EdbInfoMaxAndMinInfo) ConvertTimeStr() {
-			edbInfoMaxAndMinInfo.MinDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MinDate)
-			edbInfoMaxAndMinInfo.MaxDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MaxDate)
+	edbInfoMaxAndMinInfo.MinDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MinDate)
+	edbInfoMaxAndMinInfo.MaxDate = utils.GormDateStrToDateStr(edbInfoMaxAndMinInfo.MaxDate)
 }
 
 // GetEdbInfoMaxAndMinInfo

+ 4 - 4
models/data_manage/edb_info_relation.go

@@ -112,7 +112,7 @@ func AddOrUpdateEdbInfoRelation(objectId, objectType int, relationList []*EdbInf
 		}
 	}
 
-	//更新数据源钢联化工指标
+	//更新数据源上海钢联指标
 	if len(indexCodeList) > 0 {
 		// 更改数据源的更新状态
 		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
@@ -180,7 +180,7 @@ func AddOrUpdateEdbInfoRelationMulti(relationList []*EdbInfoRelation, refreshEdb
 		}
 	}
 
-	//更新数据源钢联化工指标
+	//更新数据源上海钢联指标
 	if len(indexCodeList) > 0 {
 		// 更改数据源的更新状态
 		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
@@ -431,7 +431,7 @@ func ReplaceRelationEdbInfoId(oldEdbInfo, newEdbInfo *EdbInfo, edbRelationIds []
 		}
 	}
 
-	//更新数据源钢联化工指标
+	//更新数据源上海钢联指标
 	if len(indexCodeList) > 0 {
 		// 更改数据源的更新状态
 		sql := ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`
@@ -505,7 +505,7 @@ func UpdateSecondRelationEdbInfoId(edbRelationIds []int, relationList []*EdbInfo
 		}
 	}
 
-	//更新数据源钢联化工指标
+	//更新数据源上海钢联指标
 	if len(indexCodeList) > 0 {
 		// 更改数据源的更新状态
 		sql = ` UPDATE base_from_mysteel_chemical_index SET is_stop = 0 WHERE index_code IN (` + utils.GetOrmInReplace(len(indexCodeList)) + `) and is_stop=1`

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

@@ -816,3 +816,10 @@ type ExcelInfoFreeze struct {
 	FreezeStartCol int  `description:"冻结开始列"`
 	FreezeEndCol   int  `description:"冻结结束列"`
 }
+
+// GetItemById 主键获取表格
+func (m *ExcelInfo) GetItemById(excelInfoId int) (item *ExcelInfo, err error) {
+	sql := ` SELECT * FROM excel_info WHERE excel_info_id = ? LIMIT 1`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, excelInfoId).First(&item).Error
+	return
+}

+ 4 - 4
models/data_manage/mysteel_chemical_classify.go

@@ -7,7 +7,7 @@ import (
 	"time"
 )
 
-// BaseFromMysteelChemicalClassify 钢联化工分类表
+// BaseFromMysteelChemicalClassify 上海钢联分类表
 type BaseFromMysteelChemicalClassify struct {
 	BaseFromMysteelChemicalClassifyId int       `orm:"column(base_from_mysteel_chemical_classify_id);pk" gorm:"primaryKey"`
 	ClassifyName                      string    `description:"分类名称"`
@@ -21,7 +21,7 @@ type BaseFromMysteelChemicalClassify struct {
 	ClassifyNameEn                    string    `description:"英文分类名称"`
 }
 
-// AddBaseFromMysteelChemicalClassify 添加钢联化工分类
+// AddBaseFromMysteelChemicalClassify 添加上海钢联分类
 func AddBaseFromMysteelChemicalClassify(item *BaseFromMysteelChemicalClassify) (lastId int64, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	err = o.Create(item).Error
@@ -70,7 +70,7 @@ func GetBaseFromMysteelChemicalClassifyById(classifyId int) (item *BaseFromMyste
 	return
 }
 
-// EditBaseFromMysteelChemicalClassify 修改钢联化工分类
+// EditBaseFromMysteelChemicalClassify 修改上海钢联分类
 func EditBaseFromMysteelChemicalClassify(classifyId int, classifyName string) (err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := `UPDATE base_from_mysteel_chemical_classify SET classify_name=?,modify_time=NOW() WHERE base_from_mysteel_chemical_classify_id=? `
@@ -79,7 +79,7 @@ func EditBaseFromMysteelChemicalClassify(classifyId int, classifyName string) (e
 }
 
 // EditBaseFromMysteelChemicalClassifyEn
-// @Description: 修改钢联化工英文分类名称
+// @Description: 修改上海钢联英文分类名称
 // @author: Roc
 // @datetime 2024-04-16 16:34:53
 // @param classifyId int

+ 38 - 29
models/data_manage/mysteel_chemical_index.go

@@ -11,10 +11,10 @@ import (
 	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
-// BaseFromMysteelChemicalIndex 钢联化工指标表
+// BaseFromMysteelChemicalIndex 上海钢联指标表
 type BaseFromMysteelChemicalIndex struct {
 	BaseFromMysteelChemicalIndexId    int       `orm:"column(base_from_mysteel_chemical_index_id);pk" gorm:"primaryKey"`
-	BaseFromMysteelChemicalClassifyId int       `orm:"column(base_from_mysteel_chemical_classify_id)" description:"钢联化工指标分类id"`
+	BaseFromMysteelChemicalClassifyId int       `orm:"column(base_from_mysteel_chemical_classify_id)" description:"上海钢联指标分类id"`
 	IndexCode                         string    `description:"指标编码"`
 	IndexName                         string    `description:"指标名称"`
 	Unit                              string    `description:"单位"`
@@ -89,7 +89,7 @@ var BaseFromMysteelChemicalIndexCols = struct {
 	ModifyTime:                     "modify_time",
 }
 
-// Update 更新钢联化工指标基础信息
+// Update 更新上海钢联指标基础信息
 func (item *BaseFromMysteelChemicalIndex) Update(cols []string) (err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	err = o.Select(cols).Updates(item).Error
@@ -118,7 +118,7 @@ func (m *BaseFromMysteelChemicalIndex) GeItemsByCondition(condition string, pars
 	return
 }
 
-// AddBaseFromMysteelChemicalIndex 添加钢联化工指标
+// AddBaseFromMysteelChemicalIndex 添加上海钢联指标
 func AddBaseFromMysteelChemicalIndex(item *BaseFromMysteelChemicalIndex) (lastId int64, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	err = o.Create(item).Error
@@ -129,17 +129,17 @@ func AddBaseFromMysteelChemicalIndex(item *BaseFromMysteelChemicalIndex) (lastId
 	return
 }
 
-// AddBaseFromMysteelChemicalIndex 添加钢联化工指标
+// AddBaseFromMysteelChemicalIndex 添加上海钢联指标
 func BatchAddBaseFromMysteelChemicalIndex(items []*BaseFromMysteelChemicalIndex) (lastId int64, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	err = o.CreateInBatches(items, utils.MultiAddNum).Error
 	return
 }
 
-// BaseFromMysteelChemicalData 钢联化工指标数据表
+// BaseFromMysteelChemicalData 上海钢联指标数据表
 type BaseFromMysteelChemicalData struct {
 	BaseFromMysteelChemicalDataId  int       `orm:"column(base_from_mysteel_chemical_data_id);pk" gorm:"primaryKey"`
-	BaseFromMysteelChemicalIndexId int       `orm:"column(base_from_mysteel_chemical_index_id)" description:"钢联化工指标id"`
+	BaseFromMysteelChemicalIndexId int       `orm:"column(base_from_mysteel_chemical_index_id)" description:"上海钢联指标id"`
 	IndexCode                      string    `description:"指标编码"`
 	DataTime                       time.Time `description:"数据日期"`
 	Value                          float64   `description:"数据值"`
@@ -147,7 +147,7 @@ type BaseFromMysteelChemicalData struct {
 	CreateTime                     time.Time `description:"创建时间"`
 }
 
-// MysteelChemicalFrequency 钢联化工频度
+// MysteelChemicalFrequency 上海钢联频度
 type MysteelChemicalFrequency struct {
 	Frequency string `description:"频度:1-日度 2-周度 3-月度 4-季度 5-年度 99-无固定频率"`
 }
@@ -162,7 +162,7 @@ func GetMysteelChemicalIndexByClassifyId(classifyId int) (items []*BaseFromMyste
 	return
 }
 
-// MysteelChemicalFrequencyByClassifyId 根据分类id获取钢联化工频度数据列表
+// MysteelChemicalFrequencyByClassifyId 根据分类id获取上海钢联频度数据列表
 func MysteelChemicalFrequencyByClassifyId(classifyId int) (items []*MysteelChemicalFrequency, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	if classifyId == 0 {
@@ -176,7 +176,7 @@ func MysteelChemicalFrequencyByClassifyId(classifyId int) (items []*MysteelChemi
 	}
 }
 
-// GetMysteelChemicalFrequency 获取钢联化工频度数据列表
+// GetMysteelChemicalFrequency 获取上海钢联频度数据列表
 func GetMysteelChemicalFrequency(condition string, pars []interface{}) (items []*MysteelChemicalFrequency, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := ` SELECT frequency FROM base_from_mysteel_chemical_index WHERE 1=1 AND frequency != '' `
@@ -188,11 +188,11 @@ func GetMysteelChemicalFrequency(condition string, pars []interface{}) (items []
 	return
 }
 
-// MysteelChemicalList 钢联化工指标列表
+// MysteelChemicalList 上海钢联指标列表
 type MysteelChemicalList struct {
 	Id                                int                    `gorm:"column:base_from_mysteel_chemical_index_id"`
-	BaseFromMysteelChemicalClassifyId int                    `gorm:"column:base_from_mysteel_chemical_classify_id" description:"钢联化工指标分类id"`
-	ParentClassifyId                  int                    `description:"钢联化工指标父级分类id"`
+	BaseFromMysteelChemicalClassifyId int                    `gorm:"column:base_from_mysteel_chemical_classify_id" description:"上海钢联指标分类id"`
+	ParentClassifyId                  int                    `description:"上海钢联指标父级分类id"`
 	IndexCode                         string                 `description:"指标编码"`
 	IndexName                         string                 `description:"指标名称"`
 	UnitName                          string                 `gorm:"column:unit"`
@@ -214,7 +214,7 @@ func (m *MysteelChemicalList) AfterFind(tx *gorm.DB) (err error) {
 	return
 }
 
-// MysteelChemicalData 钢联化工数据列表
+// MysteelChemicalData 上海钢联数据列表
 type MysteelChemicalData struct {
 	InputValue string `gorm:"column:value" description:"值"`
 	DataTime   string `gorm:"column:data_time" description:"日期"`
@@ -225,7 +225,7 @@ func (m *MysteelChemicalData) AfterFind(tx *gorm.DB) (err error) {
 	return
 }
 
-// GetMysteelChemicalIndex 根据分类id获取钢联化工频度数据列表
+// GetMysteelChemicalIndex 根据分类id获取上海钢联频度数据列表
 func GetMysteelChemicalIndex(condition string, pars []interface{}) (items []*MysteelChemicalList, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := ` SELECT * FROM base_from_mysteel_chemical_index WHERE 1=1 `
@@ -238,8 +238,19 @@ func GetMysteelChemicalIndex(condition string, pars []interface{}) (items []*Mys
 	return
 }
 
-// GetMysteelChemicalIndexData 根据指标code获取钢联化工数据列表
-func GetMysteelChemicalIndexData(indexCode string, startSize, pageSize int) (items []*MysteelChemicalData, err error) {
+// MysteelChemicalData 上海钢联数据列表
+type MysteelChemicalDataItem struct {
+	InputValue float64 `gorm:"column:value" description:"值"`
+	DataTime   string  `gorm:"column:data_time" description:"日期"`
+}
+
+func (m *MysteelChemicalDataItem) AfterFind(tx *gorm.DB) (err error) {
+	m.DataTime = utils.GormDateStrToDateStr(m.DataTime)
+	return
+}
+
+// GetMysteelChemicalIndexData 根据指标code获取上海钢联数据列表
+func GetMysteelChemicalIndexData(indexCode string, startSize, pageSize int) (items []*MysteelChemicalDataItem, err error) {
 	sql := ` SELECT * FROM (
 	SELECT DISTINCT a.index_code,a.value,a.data_time FROM base_from_mysteel_chemical_data AS a WHERE index_code=? 
 	ORDER BY data_time DESC
@@ -252,7 +263,7 @@ func GetMysteelChemicalIndexData(indexCode string, startSize, pageSize int) (ite
 	return
 }
 
-// GetMysteelChemicalIndexDataCount 根据指标code获取钢联化工数据列表 获取钢联数据总数
+// GetMysteelChemicalIndexDataCount 根据指标code获取上海钢联数据列表 获取钢联数据总数
 func GetMysteelChemicalIndexDataCount(indexCode string) (count int, err error) {
 	sql := `SELECT COUNT(1) AS count FROM (
 			SELECT * FROM (
@@ -357,7 +368,7 @@ func GetBaseFromMysteelChemicalDataTimeByIndexId(indexIdList []int) (items []str
 	return
 }
 
-// GetMysteelChemicalIndexDataByCode 通过钢联化工指标code获取所有数据列表
+// GetMysteelChemicalIndexDataByCode 通过上海钢联指标code获取所有数据列表
 func GetMysteelChemicalIndexDataByCode(indexCode string) (items []*MysteelChemicalData, err error) {
 	sql := ` SELECT * FROM (
 	SELECT DISTINCT a.index_code,a.value,a.data_time FROM base_from_mysteel_chemical_data AS a WHERE index_code=? 
@@ -370,7 +381,7 @@ func GetMysteelChemicalIndexDataByCode(indexCode string) (items []*MysteelChemic
 	return
 }
 
-// MoveBaseFromMysteelChemicalIndex 移动钢联化工指标分类
+// MoveBaseFromMysteelChemicalIndex 移动上海钢联指标分类
 func MoveBaseFromMysteelChemicalIndex(chartInfoId, classifyId int) (err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := ` UPDATE base_from_mysteel_chemical_index
@@ -400,7 +411,7 @@ func GetFirstBaseFromMysteelChemicalIndexByClassifyId(classifyId int) (item *Bas
 	return
 }
 
-// GetMysteelChemicalIndexCount 根据条件获取钢联化工数据
+// GetMysteelChemicalIndexCount 根据条件获取上海钢联数据
 func GetMysteelChemicalIndexCount(condition string, pars []interface{}) (count int, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := ` SELECT COUNT(1) AS count FROM base_from_mysteel_chemical_index WHERE 1=1 `
@@ -413,7 +424,7 @@ func GetMysteelChemicalIndexCount(condition string, pars []interface{}) (count i
 	return
 }
 
-// GetMysteelChemicalIndexList 根据分类id获取钢联化工频度数据列表
+// GetMysteelChemicalIndexList 根据分类id获取上海钢联频度数据列表
 func GetMysteelChemicalIndexList(condition string, pars []interface{}, startSize, pageSize int, orderDesc string) (items []*BaseFromMysteelChemicalIndex, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := ` SELECT * FROM base_from_mysteel_chemical_index WHERE 1=1 `
@@ -527,7 +538,7 @@ type TerminalNum struct {
 	Num          int    `description:"num"`
 }
 
-// GetMysteelChemicalGroupTerminalNum 获取钢联化工指标的终端分布
+// GetMysteelChemicalGroupTerminalNum 获取上海钢联指标的终端分布
 func GetMysteelChemicalGroupTerminalNum() (items []*TerminalNum, err error) {
 	o := global.DbMap[utils.DbNameIndex]
 	sql := ` SELECT terminal_code,count(1) num FROM base_from_mysteel_chemical_index GROUP BY terminal_code ORDER BY num ASC `
@@ -539,7 +550,7 @@ func GetMysteelChemicalGroupTerminalNum() (items []*TerminalNum, err error) {
 // @Description: 刷新配置的基础指标信息结构体
 type BaseRefreshEdbInfo struct {
 	EdbInfoId       int
-	ClassifyId      int    `description:"钢联化工指标分类id"`
+	ClassifyId      int    `description:"上海钢联指标分类id"`
 	IndexCode       string `description:"指标编码"`
 	IndexName       string `description:"指标名称"`
 	EndDate         string `description:"最新日期"`
@@ -565,7 +576,7 @@ type RefreshBaseEdbInfoResp struct {
 }
 
 // GetMysteelChemicalBaseInfoList
-// @Description: 获取钢联化工数据列表
+// @Description: 获取上海钢联数据列表
 // @author: Roc
 // @datetime 2024-01-10 14:28:35
 // @param condition string
@@ -608,7 +619,7 @@ func GetMysteelChemicalBaseInfoList(condition string, pars []interface{}, orderB
 }
 
 // ModifyMysteelChemicalUpdateStatus
-// @Description:  修改钢联化工数据停更状态
+// @Description:  修改上海钢联数据停更状态
 // @author: Roc
 // @datetime 2024-01-08 16:23:31
 // @param edbIdList []int
@@ -663,7 +674,7 @@ func ModifyMysteelChemicalUpdateStatus(edbIdList []int, indexCodeList []string,
 }
 
 // ModifyMysteelChemicalUpdateStatusByEdbInfoId
-// @Description:  修改单个钢联化工指标停更状态,同时停更依赖于该指标的计算指标
+// @Description:  修改单个上海钢联指标停更状态,同时停更依赖于该指标的计算指标
 // @author: Roc
 // @datetime 2024-01-08 16:23:31
 // @param edbIdList []int
@@ -793,5 +804,3 @@ func GetNoEdbMysteelChemicalIndexPageList(condition string, pars []interface{},
 	err = o.Raw(sql, pars...).Find(&items).Error
 	return
 }
-
-

+ 7 - 6
models/data_manage/request/mysteel_chemical_data.go

@@ -1,13 +1,13 @@
 package request
 
-// AddBaseFromMysteelChemicalClassifyReq 添加钢联化工分类请求
+// AddBaseFromMysteelChemicalClassifyReq 添加上海钢联分类请求
 type AddBaseFromMysteelChemicalClassifyReq struct {
 	ParentId     int    `description:"上级id"`
 	ClassifyName string `description:"分类名称"`
 	Level        int    `description:"层级,第一级传0,其余传上一级的层级"`
 }
 
-// EditBaseFromMysteelChemicalClassifyReq 修改钢联化工分类请求
+// EditBaseFromMysteelChemicalClassifyReq 修改上海钢联分类请求
 type EditBaseFromMysteelChemicalClassifyReq struct {
 	ClassifyName                      string `description:"分类名称"`
 	BaseFromMysteelChemicalClassifyId int    `description:"分类id"`
@@ -21,12 +21,12 @@ type MoveBaseFromMysteelChemicalClassifyReq struct {
 	NextBaseFromMysteelChemicalClassifyId   int `description:"下一个兄弟节点分类id"`
 }
 
-// DelBaseFromMysteelChemicalClassifyReq 删除钢联化工分类请求
+// DelBaseFromMysteelChemicalClassifyReq 删除上海钢联分类请求
 type DelBaseFromMysteelChemicalClassifyReq struct {
 	BaseFromMysteelChemicalClassifyId int `description:"分类id"`
 }
 
-// AddBaseFromMysteelChemicalReq 添加钢联化工请求
+// AddBaseFromMysteelChemicalReq 添加上海钢联请求
 type AddBaseFromMysteelChemicalReqItem struct {
 	BaseFromMysteelChemicalIndexId    int    `description:"指标id"`
 	BaseFromMysteelChemicalClassifyId int    `description:"分类id"`
@@ -34,12 +34,13 @@ type AddBaseFromMysteelChemicalReqItem struct {
 	UpdateWeek                        string `description:"更新周期"`
 	UpdateTime                        string `description:"更新时间点,多个时间点用英文,隔开"`
 }
-// AddBaseFromMysteelChemicalReq 添加钢联化工请求
+
+// AddBaseFromMysteelChemicalReq 添加上海钢联请求
 type AddBaseFromMysteelChemicalReq struct {
 	List []AddBaseFromMysteelChemicalReqItem
 }
 
-// DelBaseFromMysteelChemicalReq 删除钢联化工请求
+// DelBaseFromMysteelChemicalReq 删除上海钢联请求
 type DelBaseFromMysteelChemicalReq struct {
 	BaseFromMysteelChemicalIndexId int `description:"指标id"`
 }

+ 1 - 1
models/db.go

@@ -353,7 +353,7 @@ func initEdbData() {
 		new(data_manage.EdbInfoCalculateMapping),
 		new(data_manage.PredictEdbConf),                  //预测指标配置
 		new(data_manage.BaseFromMysteelChemicalClassify), //预测指标配置
-		new(data_manage.BaseFromMysteelChemicalIndex),    //钢联化工
+		new(data_manage.BaseFromMysteelChemicalIndex),    //上海钢联
 		new(data_manage.BaseFromEiaSteoClassify),         // Eia steo 报告指标
 		new(data_manage.BaseFromEiaSteoIndex),            // Eia steo 报告指标分类
 		new(data_manage.PredictEdbRuleData),              //预测指标,动态规则的计算数据

+ 4 - 0
models/manual_edb.go

@@ -167,6 +167,10 @@ func GetEdbInfoSortList(condition string, pars []interface{}, startSize, pageSiz
 		orderType = "DESC"
 	}
 	sql += ` ORDER BY a.` + orderField + ` ` + orderType
+	//sql += ` ORDER BY ? ?`
+	//
+	//pars = append(pars, orderField)
+	//pars = append(pars, orderType)
 
 	if pageSize > 0 {
 		sql += ` LIMIT ?,? `

+ 5 - 2
models/material/material_classify.go

@@ -213,7 +213,10 @@ type SandboxLinkCheckResp struct {
 
 func GetMaterialClassifyByLevelPath(levelPath string) (items []*MaterialClassify, err error) {
 	o := global.DbMap[utils.DbNameReport]
-	sql := `SELECT * FROM material_classify where level_path like '` + levelPath + `%'`
-	err = o.Raw(sql).Find(&items).Error
+	//sql := `SELECT * FROM material_classify where level_path like '` + levelPath + `%'`
+	likeKey := `%` + levelPath + `%`
+	sql := `SELECT * FROM material_classify where level_path LIKE ? `
+
+	err = o.Raw(sql, likeKey).Find(&items).Error
 	return
 }

+ 164 - 0
models/rag/ai_task.go

@@ -0,0 +1,164 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// AiTask ai这边的任务表
+type AiTask struct {
+	AiTaskID                int       `gorm:"primaryKey;column:ai_task_id" description:"-"`
+	TaskName                string    `gorm:"column:task_name" description:"任务名称"`
+	TaskType                string    `gorm:"column:task_type" description:"任务类型"`
+	Status                  string    `gorm:"column:status" description:"任务状态"`
+	StartTime               time.Time `gorm:"column:start_time" description:"开始时间"`
+	EndTime                 time.Time `gorm:"column:end_time" description:"结束时间"`
+	CreateTime              time.Time `gorm:"column:create_time" description:"创建时间"`
+	UpdateTime              time.Time `gorm:"column:update_time" description:"更新时间"`
+	Parameters              string    `gorm:"column:parameters" description:"执行参数"`
+	Logs                    string    `gorm:"column:logs" description:"日志"`
+	Errormessage            string    `gorm:"column:ErrorMessage" description:"错误信息"`
+	Priority                int       `gorm:"column:priority" description:"优先级"`
+	RetryCount              int       `gorm:"column:retry_count" description:"重试次数"`
+	EstimatedCompletionTime time.Time `gorm:"column:estimated_completion_time" description:"预计完成时间"`
+	ActualCompletitonTime   time.Time `gorm:"column:actual_completiton_time" description:"实际完成时间"`
+	Remark                  string    `gorm:"column:remark" description:"备注"`
+	SysUserID               int       `gorm:"column:sys_user_id" description:"任务创建人id"`
+	SysUserRealName         string    `gorm:"column:sys_user_real_name" description:"任务创建人名称"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *AiTask) TableName() string {
+	return "ai_task"
+}
+
+// AiTaskColumns get sql column name.获取数据库列名
+var AiTaskColumns = struct {
+	AiTaskID                string
+	TaskName                string
+	TaskType                string
+	Status                  string
+	StartTime               string
+	EndTime                 string
+	CreateTime              string
+	UpdateTime              string
+	Parameters              string
+	Logs                    string
+	Errormessage            string
+	Priority                string
+	RetryCount              string
+	EstimatedCompletionTime string
+	ActualCompletitonTime   string
+	Remark                  string
+	SysUserID               string
+	SysUserRealName         string
+}{
+	AiTaskID:                "ai_task_id",
+	TaskName:                "task_name",
+	TaskType:                "task_type",
+	Status:                  "status",
+	StartTime:               "start_time",
+	EndTime:                 "end_time",
+	CreateTime:              "create_time",
+	UpdateTime:              "update_time",
+	Parameters:              "parameters",
+	Logs:                    "logs",
+	Errormessage:            "ErrorMessage",
+	Priority:                "priority",
+	RetryCount:              "retry_count",
+	EstimatedCompletionTime: "estimated_completion_time",
+	ActualCompletitonTime:   "actual_completiton_time",
+	Remark:                  "remark",
+	SysUserID:               "sys_user_id",
+	SysUserRealName:         "sys_user_real_name",
+}
+
+func (m *AiTask) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *AiTask) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *AiTask) Del() (err error) {
+	err = global.DbMap[utils.DbNameAI].Delete(&m).Error
+
+	return
+}
+
+func (m *AiTask) GetByID(id int) (item *AiTask, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", AiTaskColumns.AiTaskID), id).First(&item).Error
+
+	return
+}
+
+func (m *AiTask) GetByCondition(condition string, pars []interface{}) (item *AiTask, err error) {
+	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).First(&item).Error
+
+	return
+}
+
+func (m *AiTask) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*AiTask, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s order by ai_task_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 *AiTask) 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 %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
+}
+
+// AddAiTask
+// @Description: 添加Ai模块的任务
+// @author: Roc
+// @datetime 2025-04-16 16:55:36
+// @param aiTask *AiTask
+// @param aiRecordList []*AiTaskRecord
+// @return err error
+func AddAiTask(aiTask *AiTask, aiRecordList []*AiTaskRecord) (err error) {
+	to := global.DbMap[utils.DbNameAI].Begin()
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	err = to.Create(aiTask).Error
+	if err != nil {
+		return
+	}
+
+	for _, aiTaskRecord := range aiRecordList {
+		aiTaskRecord.AiTaskID = aiTask.AiTaskID
+	}
+
+	err = to.CreateInBatches(aiRecordList, utils.MultiAddNum).Error
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 115 - 0
models/rag/ai_task_record.go

@@ -0,0 +1,115 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// AiTaskRecord AI任务的子记录
+type AiTaskRecord struct {
+	AiTaskRecordID int       `gorm:"primaryKey;column:ai_task_record_id" json:"-"` // 任务记录id
+	AiTaskID       int       `gorm:"column:ai_task_id" json:"aiTaskId"`            // 任务id
+	Parameters     string    `gorm:"column:parameters" json:"parameters"`          // 子任务参数
+	Status         string    `gorm:"column:status" json:"status"`                  // 状态
+	Remark         string    `gorm:"column:remark" json:"remark"`                  // 备注
+	ModifyTime     time.Time `gorm:"column:modify_time" json:"modifyTime"`         // 最后一次修改时间
+	CreateTime     time.Time `gorm:"column:create_time" json:"createTime"`         // 任务创建时间
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *AiTaskRecord) TableName() string {
+	return "ai_task_record"
+}
+
+// AiTaskRecordColumns get sql column name.获取数据库列名
+var AiTaskRecordColumns = struct {
+	AiTaskRecordID string
+	AiTaskID       string
+	Parameters     string
+	Status         string
+	Remark         string
+	ModifyTime     string
+	CreateTime     string
+}{
+	AiTaskRecordID: "ai_task_record_id",
+	AiTaskID:       "ai_task_id",
+	Parameters:     "parameters",
+	Status:         "status",
+	Remark:         "remark",
+	ModifyTime:     "modify_time",
+	CreateTime:     "create_time",
+}
+
+func (m *AiTaskRecord) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *AiTaskRecord) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *AiTaskRecord) Del() (err error) {
+	err = global.DbMap[utils.DbNameAI].Delete(&m).Error
+
+	return
+}
+
+func (m *AiTaskRecord) GetByID(id int) (item *AiTaskRecord, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", AiTaskRecordColumns.AiTaskRecordID), id).First(&item).Error
+
+	return
+}
+
+func (m *AiTaskRecord) GetByCondition(condition string, pars []interface{}) (item *AiTaskRecord, err error) {
+	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).First(&item).Error
+
+	return
+}
+
+func (m *AiTaskRecord) GetAllListByCondition(field, condition string, pars []interface{}) (items []*AiTaskRecord, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s order by ai_task_record_id desc `, field, m.TableName(), condition)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
+func (m *AiTaskRecord) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*AiTaskRecord, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s order by ai_task_record_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 *AiTaskRecord) 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 %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
+}
+
+// QuestionGenerateAbstractParam
+// @Description:
+type QuestionGenerateAbstractParam struct {
+	QuestionId  int    `json:"questionId"`
+	ArticleType string `json:"articleType"`
+	ArticleId   int    `json:"articleId"`
+}

+ 114 - 0
models/rag/article_abstract_history.go

@@ -0,0 +1,114 @@
+package rag
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"time"
+)
+
+// ArticleAbstractHistory 文章/报告摘要历史记录表
+type ArticleAbstractHistory struct {
+	ArticleAbstractHistoryID int       `gorm:"primaryKey;column:article_abstract_history_id" description:"-"`
+	Source                   int8      `gorm:"column:source" description:"来源,0:公众号文章,1:eta报告"`
+	ArticleAbstractID        int       `gorm:"column:article_abstract_id" description:"文章/报告摘要id"`
+	ArticleID                int       `gorm:"column:article_id" description:"文章/报告Id"`
+	QuestionId               int       `gorm:"column:question_id" description:"提示词Id"`
+	Tags                     string    `gorm:"column:tags" description:"标签"`
+	TagsName                 string    `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	QuestionContent          string    `gorm:"column:question_content" description:"questionContent"`
+	Content                  string    `gorm:"column:content" description:"摘要内容"`
+	Version                  int       `gorm:"column:version" description:"版本号"`
+	VectorKey                string    `gorm:"column:vector_key" description:"向量key标识"`
+	ModifyTime               time.Time `gorm:"column:modify_time" description:"modifyTime"`
+	CreateTime               time.Time `gorm:"column:create_time" description:"createTime"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *ArticleAbstractHistory) TableName() string {
+	return "article_abstract_history"
+}
+
+// ArticleAbstractHistoryColumns get sql column name.获取数据库列名
+var ArticleAbstractHistoryColumns = struct {
+	ArticleAbstractHistoryID string
+	Source                   string
+	ArticleAbstractID        string
+	ArticleID                string
+	QuestionId               string
+	Tags                     string
+	TagsName                 string
+	QuestionContent          string
+	Content                  string
+	Version                  string
+	VectorKey                string
+	ModifyTime               string
+	CreateTime               string
+}{
+	ArticleAbstractHistoryID: "article_abstract_history_id",
+	Source:                   "source",
+	ArticleAbstractID:        "article_abstract_id",
+	ArticleID:                "article_id",
+	QuestionId:               "question_id",
+	Tags:                     "tags",
+	TagsName:                 "tags_name",
+	QuestionContent:          "question_content",
+	Content:                  "content",
+	Version:                  "version",
+	VectorKey:                "vector_key",
+	ModifyTime:               "modify_time",
+	CreateTime:               "create_time",
+}
+
+func (m *ArticleAbstractHistory) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+// AddArticleAbstractHistoryByWechatArticleAbstract
+// @Description: 根据eta报告摘要添加历史记录
+// @author: Roc
+// @datetime 2025-04-17 14:05:10
+// @param item *WechatArticleAbstract
+func AddArticleAbstractHistoryByWechatArticleAbstract(item *WechatArticleAbstract) {
+	history := &ArticleAbstractHistory{
+		ArticleAbstractHistoryID: 0,
+		Source:                   0,
+		ArticleAbstractID:        item.WechatArticleAbstractId,
+		ArticleID:                item.WechatArticleId,
+		QuestionId:               item.QuestionId,
+		Tags:                     item.Tags,
+		TagsName:                 item.TagsName,
+		QuestionContent:          item.QuestionContent,
+		Content:                  item.Content,
+		Version:                  item.Version,
+		VectorKey:                item.VectorKey,
+		ModifyTime:               time.Now(),
+		CreateTime:               time.Now(),
+	}
+	_ = history.Create()
+}
+
+// AddArticleAbstractHistoryByWechatArticleAbstract
+// @Description: 根据eta报告摘要添加历史记录
+// @author: Roc
+// @datetime 2025-04-17 14:05:10
+// @param item *WechatArticleAbstract
+func AddArticleAbstractHistoryByRagEtaReportAbstract(item *RagEtaReportAbstract) {
+	history := &ArticleAbstractHistory{
+		ArticleAbstractHistoryID: 0,
+		Source:                   0,
+		ArticleAbstractID:        item.RagEtaReportAbstractId,
+		ArticleID:                item.RagEtaReportId,
+		QuestionId:               item.QuestionId,
+		Tags:                     item.Tags,
+		TagsName:                 item.TagsName,
+		QuestionContent:          item.QuestionContent,
+		Content:                  item.Content,
+		Version:                  item.Version,
+		VectorKey:                item.VectorKey,
+		ModifyTime:               time.Now(),
+		CreateTime:               time.Now(),
+	}
+	_ = history.Create()
+}

+ 19 - 4
models/rag/question.go

@@ -13,6 +13,9 @@ type Question struct {
 	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:"排序"`
+	Version         int       `gorm:"column:version" description:"问题版本"`
+	GenerateStatus  string    `gorm:"column:generate_status;type:enum('undo', 'done');comment:生成摘要状态;default:NULL;" description:"生成摘要状态"`
+	IsDefault       int       `gorm:"column:is_default;type:int(1);comment:是否默认提示词;default:NULL;" 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"`
@@ -26,17 +29,23 @@ func (m *Question) TableName() string {
 
 // QuestionColumns get sql column name.获取数据库列名
 var QuestionColumns = struct {
-	QuestionID      string
+	QuestionId      string
 	QuestionTitle   string
 	QuestionContent string
 	Sort            string
+	Version         string
+	GenerateStatus  string
+	IsDefault       string
 	ModifyTime      string
 	CreateTime      string
 }{
-	QuestionID:      "question_id",
+	QuestionId:      "question_id",
 	QuestionTitle:   "question_title",
 	QuestionContent: "question_content",
 	Sort:            "sort",
+	Version:         "version",
+	GenerateStatus:  "generate_status",
+	IsDefault:       "is_default",
 	ModifyTime:      "modify_time",
 	CreateTime:      "create_time",
 }
@@ -64,6 +73,9 @@ type QuestionView struct {
 	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:"排序"`
+	Version         int    `gorm:"column:version" description:"问题版本"`
+	GenerateStatus  string `gorm:"column:generate_status;type:enum('undo', 'done');comment:生成摘要状态;default:NULL;" description:"生成摘要状态"`
+	IsDefault       int    `gorm:"column:is_default;type:int(1);comment:是否默认提示词;default:NULL;" 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"`
@@ -84,6 +96,9 @@ func (m *Question) ToView() QuestionView {
 		QuestionTitle:   m.QuestionTitle,
 		QuestionContent: m.QuestionContent,
 		Sort:            m.Sort,
+		Version:         m.Version,
+		GenerateStatus:  m.GenerateStatus,
+		IsDefault:       m.IsDefault,
 		SysUserId:       m.SysUserId,
 		SysUserRealName: m.SysUserRealName,
 		ModifyTime:      modifyTime,
@@ -101,7 +116,7 @@ func (m *Question) ListToViewList(list []*Question) (wechatArticleViewList []Que
 }
 
 func (m *Question) GetByID(id int) (item *Question, err error) {
-	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", QuestionColumns.QuestionID), id).First(&item).Error
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", QuestionColumns.QuestionId), id).First(&item).Error
 
 	return
 }
@@ -154,7 +169,7 @@ func (m *Question) GetTitlePageListByCondition(condition string, pars []interfac
 		return
 	}
 	if total > 0 {
-		items, err = m.GetListByCondition(`question_id,question_title,sort`, condition, pars, startSize, pageSize)
+		items, err = m.GetListByCondition(`question_id,question_title,sort,version,generate_status,is_default`, condition, pars, startSize, pageSize)
 	}
 
 	return

+ 86 - 0
models/rag/question_history.go

@@ -0,0 +1,86 @@
+package rag
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"time"
+)
+
+// QuestionHistory 问题历史列表
+type QuestionHistory struct {
+	QuestionHistoryID int       `gorm:"primaryKey;column:question_history_id" description:"-"`
+	QuestionId        int       `gorm:"column:question_id" description:"问题ID"`
+	QuestionTitle     string    `gorm:"column:question_title" description:"问题标题"`
+	QuestionContent   string    `gorm:"column:question_content" description:"问题内容"`
+	Sort              int       `gorm:"column:sort" description:"排序"`
+	Version           int       `gorm:"column:version" description:"问题版本"`
+	GenerateStatus    string    `gorm:"column:generate_status" description:"生成摘要状态"`
+	IsDefault         int       `gorm:"column:is_default" description:"是否默认提示词"`
+	SysUserID         int       `gorm:"column:sys_user_id" description:"添加人id"`
+	SysUserRealName   string    `gorm:"column:sys_user_real_name" description:"添加人名称"`
+	ModifyTime        time.Time `gorm:"column:modify_time" description:"modifyTime"`
+	CreateTime        time.Time `gorm:"column:create_time" description:"createTime"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *QuestionHistory) TableName() string {
+	return "question_history"
+}
+
+// QuestionHistoryColumns get sql column name.获取数据库列名
+var QuestionHistoryColumns = struct {
+	QuestionHistoryID string
+	QuestionId        string
+	QuestionTitle     string
+	QuestionContent   string
+	Sort              string
+	Version           string
+	GenerateStatus    string
+	IsDefault         string
+	SysUserID         string
+	SysUserRealName   string
+	ModifyTime        string
+	CreateTime        string
+}{
+	QuestionHistoryID: "question_history_id",
+	QuestionId:        "question_id",
+	QuestionTitle:     "question_title",
+	QuestionContent:   "question_content",
+	Sort:              "sort",
+	Version:           "version",
+	GenerateStatus:    "generate_status",
+	IsDefault:         "is_default",
+	SysUserID:         "sys_user_id",
+	SysUserRealName:   "sys_user_real_name",
+	ModifyTime:        "modify_time",
+	CreateTime:        "create_time",
+}
+
+func (m *QuestionHistory) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+// AddQuestionHistoryByQuestion
+// @Description: 根据提示词创建提示词历史记录
+// @author: Roc
+// @datetime 2025-04-17 10:44:15
+// @param item *Question
+func AddQuestionHistoryByQuestion(item *Question) {
+	history := &QuestionHistory{
+		QuestionHistoryID: 0,
+		QuestionId:        item.QuestionId,
+		QuestionTitle:     item.QuestionTitle,
+		QuestionContent:   item.QuestionContent,
+		Sort:              item.Sort,
+		Version:           item.Version,
+		GenerateStatus:    item.GenerateStatus,
+		IsDefault:         item.IsDefault,
+		SysUserID:         item.SysUserId,
+		SysUserRealName:   item.SysUserRealName,
+		ModifyTime:        time.Now(),
+		CreateTime:        time.Now(),
+	}
+	_ = history.Create()
+}

+ 5 - 5
models/rag/rag_eta_report.go

@@ -31,7 +31,7 @@ func (m *RagEtaReport) TableName() string {
 
 // RagEtaReportColumns get sql column name.获取数据库列名
 var RagEtaReportColumns = struct {
-	RagEtaReportID  string
+	RagEtaReportId  string
 	ReportID        string
 	ReportChapterID string
 	Title           string
@@ -44,7 +44,7 @@ var RagEtaReportColumns = struct {
 	ModifyTime      string
 	CreateTime      string
 }{
-	RagEtaReportID:  "rag_eta_report_id",
+	RagEtaReportId:  "rag_eta_report_id",
 	ReportID:        "report_id",
 	ReportChapterID: "report_chapter_id",
 	Title:           "title",
@@ -121,7 +121,7 @@ func (m *RagEtaReport) Update(updateCols []string) (err error) {
 }
 
 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
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", RagEtaReportColumns.RagEtaReportId), id).First(&item).Error
 
 	return
 }
@@ -154,14 +154,14 @@ func (m *RagEtaReport) GetCountByCondition(condition string, pars []interface{})
 	return
 }
 
-func (m *RagEtaReport) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*RagEtaReport, err error) {
+func (m *RagEtaReport) GetPageListByCondition(field, 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)
+		items, err = m.GetListByCondition(field, condition, pars, startSize, pageSize)
 	}
 
 	return

+ 292 - 0
models/rag/rag_eta_report_abstract.go

@@ -0,0 +1,292 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// RagEtaReportAbstract 报告摘要
+type RagEtaReportAbstract struct {
+	RagEtaReportAbstractId int       `gorm:"primaryKey;column:rag_eta_report_abstract_id" description:"-"`
+	RagEtaReportId         int       `gorm:"column:rag_eta_report_id" description:"ETA报告id"`
+	Content                string    `gorm:"column:content" description:"摘要内容"`
+	QuestionId             int       `gorm:"column:question_id" description:"提示词Id"`
+	QuestionContent        string    `gorm:"column:question_content" description:"questionContent"`
+	Version                int       `gorm:"column:version" description:"版本号"`
+	Tags                   string    `gorm:"column:tags" description:"标签"`
+	TagsName               string    `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	VectorKey              string    `gorm:"column:vector_key" description:"向量key标识"`
+	ModifyTime             time.Time `gorm:"column:modify_time" description:"modifyTime"`
+	CreateTime             time.Time `gorm:"column:create_time" description:"createTime"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *RagEtaReportAbstract) TableName() string {
+	return "rag_eta_report_abstract"
+}
+
+// RagEtaReportAbstractColumns get sql column name.获取数据库列名
+var RagEtaReportAbstractColumns = struct {
+	RagEtaReportAbstractId string
+	RagEtaReportId         string
+	Content                string
+	QuestionId             string
+	QuestionContent        string
+	Version                string
+	Tags                   string
+	TagsName               string
+	VectorKey              string
+	ModifyTime             string
+	CreateTime             string
+}{
+	RagEtaReportAbstractId: "rag_eta_report_abstract_id",
+	RagEtaReportId:         "rag_eta_report_id",
+	Content:                "content",
+	QuestionId:             "question_id",
+	QuestionContent:        "question_content",
+	Version:                "version",
+	Tags:                   "tags",
+	TagsName:               "tags_name",
+	VectorKey:              "vector_key",
+	ModifyTime:             "modify_time",
+	CreateTime:             "create_time",
+}
+
+func (m *RagEtaReportAbstract) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *RagEtaReportAbstract) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *RagEtaReportAbstract) Del() (err error) {
+	err = global.DbMap[utils.DbNameAI].Delete(&m).Error
+
+	return
+}
+
+func (m *RagEtaReportAbstract) GetById(id int) (item *RagEtaReportAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", RagEtaReportAbstractColumns.RagEtaReportAbstractId), id).First(&item).Error
+
+	return
+}
+
+func (m *RagEtaReportAbstract) GetByIdList(idList []int) (items []*RagEtaReportAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s in (?) ", RagEtaReportAbstractColumns.RagEtaReportAbstractId), idList).Find(&items).Error
+
+	return
+}
+
+func (m *RagEtaReportAbstract) GetListByQuestionId(questionId int) (items []*RagEtaReportAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ? ", RagEtaReportAbstractColumns.QuestionId), questionId).Find(&items).Error
+
+	return
+}
+
+func (m *RagEtaReportAbstract) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*RagEtaReportAbstract, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s  order by rag_eta_report_abstract_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 *RagEtaReportAbstract) DelByIdList(idList []int) (err error) {
+	if len(idList) <= 0 {
+		return
+	}
+	sqlStr := fmt.Sprintf(`delete from %s where %s in (?)`, m.TableName(), RagEtaReportAbstractColumns.RagEtaReportAbstractId)
+	err = global.DbMap[utils.DbNameAI].Exec(sqlStr, idList).Error
+
+	return
+}
+
+// GetByRagEtaReportId
+// @Description: 根据报告id获取摘要
+// @author: Roc
+// @receiver m
+// @datetime 2025-03-07 10:00:59
+// @param id int
+// @return item *RagEtaReportAbstract
+// @return err error
+func (m *RagEtaReportAbstract) GetByRagEtaReportId(id int) (item *RagEtaReportAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", RagEtaReportAbstractColumns.RagEtaReportId), id).Order(fmt.Sprintf(`%s DESC`, RagEtaReportAbstractColumns.RagEtaReportAbstractId)).First(&item).Error
+
+	return
+}
+
+// GetByRagEtaReportIdAndQuestionId
+// @Description: 根据报告id和提示词ID获取摘要
+// @author: Roc
+// @receiver m
+// @datetime 2025-04-17 17:39:27
+// @param articleId int
+// @param questionId int
+// @return item *RagEtaReportAbstract
+// @return err error
+func (m *RagEtaReportAbstract) GetByRagEtaReportIdAndQuestionId(articleId, questionId int) (item *RagEtaReportAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ? AND %s = ? ", RagEtaReportAbstractColumns.RagEtaReportId, RagEtaReportAbstractColumns.QuestionId), articleId, questionId).Order(fmt.Sprintf(`%s DESC`, RagEtaReportAbstractColumns.RagEtaReportAbstractId)).First(&item).Error
+
+	return
+}
+
+type RagEtaReportAbstractView struct {
+	RagEtaReportAbstractId int    `gorm:"primaryKey;column:rag_eta_report_abstract_id" description:"-"`
+	RagEtaReportId         int    `gorm:"column:rag_eta_report_id" description:"ETA报告id"`
+	Abstract               string `gorm:"column:abstract;type:longtext;comment:摘要内容;" description:"摘要内容"`
+	QuestionId             int    `gorm:"column:question_id" description:"提示词Id"`
+	QuestionContent        string `gorm:"column:question_content" description:"questionContent"`
+	Version                int    `gorm:"column:version" description:"版本号"`
+	Tags                   string `gorm:"column:tags" description:"标签"`
+	TagsName               string `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	VectorKey              string `gorm:"column:vector_key" description:"向量key标识"`
+	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"`
+	Title                  string `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
+}
+
+type RagEtaReportAbstractItem struct {
+	RagEtaReportAbstractId int       `gorm:"primaryKey;column:rag_eta_report_abstract_id" description:"-"`
+	RagEtaReportId         int       `gorm:"column:rag_eta_report_id" description:"ETA报告id"`
+	Content                string    `gorm:"column:content" description:"摘要内容"`
+	QuestionId             int       `gorm:"column:question_id" description:"提示词Id"`
+	QuestionContent        string    `gorm:"column:question_content" description:"questionContent"`
+	Version                int       `gorm:"column:version" description:"版本号"`
+	Tags                   string    `gorm:"column:tags" description:"标签"`
+	TagsName               string    `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	VectorKey              string    `gorm:"column:vector_key" description:"向量key标识"`
+	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"`
+	Title                  string    `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
+}
+
+func (m *RagEtaReportAbstractItem) ToView() RagEtaReportAbstractView {
+	return RagEtaReportAbstractView{
+		RagEtaReportAbstractId: m.RagEtaReportAbstractId,
+		RagEtaReportId:         m.RagEtaReportId,
+		Abstract:               m.Content,
+		Version:                m.Version,
+		VectorKey:              m.VectorKey,
+		ModifyTime:             utils.DateStrToDateTimeStr(m.ModifyTime),
+		CreateTime:             utils.DateStrToDateTimeStr(m.CreateTime),
+		Title:                  m.Title,
+		QuestionId:             m.QuestionId,
+		Tags:                   m.Tags,
+		TagsName:               m.TagsName,
+		QuestionContent:        m.QuestionContent,
+	}
+}
+
+func (m *RagEtaReportAbstract) EtaReportAbstractItem(list []*RagEtaReportAbstractItem) (etaReportAbstractViewList []RagEtaReportAbstractView) {
+	etaReportAbstractViewList = make([]RagEtaReportAbstractView, 0)
+
+	for _, v := range list {
+		etaReportAbstractViewList = append(etaReportAbstractViewList, v.ToView())
+	}
+	return
+}
+
+func (m *RagEtaReportAbstract) GetListByPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*RagEtaReportAbstractItem, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s AS a 
+          WHERE 1=1  %s order by a.modify_time DESC,a.rag_eta_report_abstract_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 *RagEtaReportAbstract) GetCountByPlatformCondition(condition string, pars []interface{}) (total int, err error) {
+	var intNull sql.NullInt64
+	sqlStr := fmt.Sprintf(`SELECT COUNT(1) total FROM %s AS a 
+          WHERE 1=1  %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 *RagEtaReportAbstract) GetPageListByPlatformCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*RagEtaReportAbstractItem, err error) {
+
+	total, err = m.GetCountByPlatformCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByPlatformCondition(``, condition, pars, startSize, pageSize)
+	}
+
+	return
+}
+
+func (m *RagEtaReportAbstract) GetListByTagAndPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*RagEtaReportAbstractItem, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s AS a 
+          JOIN wechat_article AS b ON a.rag_eta_report_id=b.rag_eta_report_id
+          JOIN wechat_platform AS c ON b.wechat_platform_id=c.wechat_platform_id
+          JOIN wechat_platform_tag_mapping AS d ON c.wechat_platform_id=d.wechat_platform_id
+          WHERE 1=1 AND b.is_deleted=0 %s  order by a.modify_time DESC,a.rag_eta_report_abstract_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 *RagEtaReportAbstract) GetCountByTagAndPlatformCondition(condition string, pars []interface{}) (total int, err error) {
+	var intNull sql.NullInt64
+	sqlStr := fmt.Sprintf(`SELECT COUNT(1) total FROM %s AS a 
+          JOIN wechat_article AS b ON a.rag_eta_report_id=b.rag_eta_report_id
+          JOIN wechat_platform AS c ON b.wechat_platform_id=c.wechat_platform_id
+          JOIN wechat_platform_tag_mapping AS d ON c.wechat_platform_id=d.wechat_platform_id
+          WHERE 1=1 AND b.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 *RagEtaReportAbstract) GetPageListByTagAndPlatformCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*RagEtaReportAbstractItem, err error) {
+
+	total, err = m.GetCountByTagAndPlatformCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByTagAndPlatformCondition(`a.rag_eta_report_abstract_id,a.rag_eta_report_id,a.content AS abstract,a.version,a.vector_key,a.modify_time,a.create_time,b.title,b.link,d.tag_id`, condition, pars, startSize, pageSize)
+	}
+
+	return
+}
+
+// DelVectorKey
+// @Description: 批量删除向量库
+// @author: Roc
+// @receiver m
+// @datetime 2025-03-12 16:47:52
+// @param ragEtaReportAbstractIdList []int
+// @return err error
+func (m *RagEtaReportAbstract) DelVectorKey(ragEtaReportAbstractIdList []int) (err error) {
+	sqlStr := fmt.Sprintf(`UPDATE %s set vector_key = '' WHERE rag_eta_report_abstract_id IN (?)`, m.TableName())
+	err = global.DbMap[utils.DbNameAI].Exec(sqlStr, ragEtaReportAbstractIdList).Error
+
+	return
+}

+ 9 - 0
models/rag/request/rag_eta_report.go

@@ -0,0 +1,9 @@
+package request
+
+type BeachOpRagEtaReportAbstractReq struct {
+	RagEtaReportAbstractIdList    []int  `description:"摘要id"`
+	NotRagEtaReportAbstractIdList []int  `description:"不需要的摘要id"`
+	KeyWord                       string `description:"关键字"`
+	TagId                         string `description:"标签id"`
+	IsSelectAll                   bool   `description:"是否选择所有摘要"`
+}

+ 1 - 1
models/rag/request/wechat_platform.go

@@ -28,6 +28,6 @@ type BeachOpAbstractReq struct {
 	WechatArticleAbstractIdList    []int  `description:"摘要id"`
 	NotWechatArticleAbstractIdList []int  `description:"不需要的摘要id"`
 	KeyWord                        string `description:"关键字"`
-	TagId                          int    `description:"标签id"`
+	TagId                          string `description:"标签id"`
 	IsSelectAll                    bool   `description:"是否选择所有摘要"`
 }

+ 15 - 0
models/rag/response/abstract.go

@@ -9,3 +9,18 @@ type AbstractListListResp struct {
 	List   []rag.WechatArticleAbstractView
 	Paging *paging.PagingItem `description:"分页数据"`
 }
+
+type RagEtaReportAbstractListListResp struct {
+	List   []rag.RagEtaReportAbstractView
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type RagEtaReportItemAbstractListListResp struct {
+	List   []rag.RagEtaReportAbstractView
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type WechatArticleItemAbstractListListResp struct {
+	List   []rag.WechatArticleAbstractView
+	Paging *paging.PagingItem `description:"分页数据"`
+}

+ 5 - 0
models/rag/response/question.go

@@ -9,3 +9,8 @@ type QuestionListListResp struct {
 	List   []rag.QuestionView
 	Paging *paging.PagingItem `description:"分页数据"`
 }
+
+type QuestionOpAuthResp struct {
+	Status string `description:"状态"`
+	Tip    string `description:"提示信息"`
+}

+ 23 - 1
models/rag/tag.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/global"
 	"eta/eta_api/utils"
 	"fmt"
+	"strings"
 	"time"
 )
 
@@ -37,6 +38,12 @@ var TagColumns = struct {
 	CreateTime: "create_time",
 }
 
+func (m *Tag) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
 func (m *Tag) GetByID(TagId int) (item *Tag, err error) {
 	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", TagColumns.TagID), TagId).First(&item).Error
 
@@ -70,7 +77,6 @@ func (m *Tag) GetCountByCondition(condition string, pars []interface{}) (total i
 }
 
 func (m *Tag) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*Tag, err error) {
-
 	total, err = m.GetCountByCondition(condition, pars)
 	if err != nil {
 		return
@@ -81,3 +87,19 @@ func (m *Tag) GetPageListByCondition(condition string, pars []interface{}, start
 
 	return
 }
+
+var aiAbstractTagMap map[string]int
+
+func (m *Tag) GetTagIdByName(tagName string) (tagId int, err error) {
+	tagName = strings.TrimSpace(tagName)
+	tagId, ok := aiAbstractTagMap[tagName]
+	if ok {
+		return
+	}
+
+	var item *Tag
+	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? `, m.TableName(), TagColumns.TagName)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, tagName).First(&item).Error
+
+	return
+}

+ 82 - 12
models/rag/wechat_article_abstract.go

@@ -11,11 +11,15 @@ import (
 type WechatArticleAbstract struct {
 	WechatArticleAbstractId int       `gorm:"column:wechat_article_abstract_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"wechat_article_abstract_id"`
 	WechatArticleId         int       `gorm:"column:wechat_article_id;type:int(9) UNSIGNED;comment:关联的微信报告id;default:0;" description:"关联的微信报告id"`
-	Content                 string    `gorm:"column:content;type:longtext;comment:摘要内容;" description:"content"` // 摘要内容
+	Content                 string    `gorm:"column:content;type:longtext;comment:摘要内容;" description:"摘要内容"`
 	Version                 int       `gorm:"column:version;type:int(10) UNSIGNED;comment:版本号;default:1;" description:"版本号"`
 	VectorKey               string    `gorm:"column:vector_key;type:varchar(255);comment:向量key标识;" description:"向量key标识"`
 	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"`
+	QuestionId              int       `gorm:"column:question_id" description:"提示词Id"`
+	Tags                    string    `gorm:"column:tags" description:"标签"`
+	TagsName                string    `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	QuestionContent         string    `gorm:"column:question_content" description:"提示词内容"`
 }
 
 // TableName get sql table name.获取数据库表名
@@ -27,6 +31,10 @@ func (m *WechatArticleAbstract) TableName() string {
 var WechatArticleAbstractColumns = struct {
 	WechatArticleAbstractID string
 	WechatArticleID         string
+	QuestionId              string
+	Tags                    string
+	TagsName                string
+	QuestionContent         string
 	Content                 string
 	Version                 string
 	ModifyTime              string
@@ -34,6 +42,9 @@ var WechatArticleAbstractColumns = struct {
 }{
 	WechatArticleAbstractID: "wechat_article_abstract_id",
 	WechatArticleID:         "wechat_article_id",
+	QuestionId:              "question_id",
+	TagsName:                "tags_name",
+	QuestionContent:         "question_content",
 	Content:                 "content",
 	Version:                 "version",
 	ModifyTime:              "modify_time",
@@ -70,6 +81,12 @@ func (m *WechatArticleAbstract) GetByIdList(idList []int) (items []*WechatArticl
 	return
 }
 
+func (m *WechatArticleAbstract) GetListByQuestionId(questionId int) (items []*WechatArticleAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ? ", WechatArticleAbstractColumns.QuestionId), questionId).Find(&items).Error
+
+	return
+}
+
 func (m *WechatArticleAbstract) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAbstract, err error) {
 	if field == "" {
 		field = "*"
@@ -105,6 +122,21 @@ func (m *WechatArticleAbstract) GetByWechatArticleId(id int) (item *WechatArticl
 	return
 }
 
+// GetByWechatArticleIdAndQuestionId
+// @Description: 根据报告id和提示词ID获取摘要
+// @author: Roc
+// @receiver m
+// @datetime 2025-04-17 17:39:27
+// @param articleId int
+// @param questionId int
+// @return item *WechatArticleAbstract
+// @return err error
+func (m *WechatArticleAbstract) GetByWechatArticleIdAndQuestionId(articleId, questionId int) (item *WechatArticleAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ? AND %s = ? ", WechatArticleAbstractColumns.WechatArticleID, WechatArticleAbstractColumns.QuestionId), articleId, questionId).Order(fmt.Sprintf(`%s DESC`, WechatArticleAbstractColumns.WechatArticleAbstractID)).First(&item).Error
+
+	return
+}
+
 type WechatArticleAbstractView struct {
 	WechatArticleAbstractId int    `gorm:"column:wechat_article_abstract_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"wechat_article_abstract_id"`
 	WechatArticleId         int    `gorm:"column:wechat_article_id;type:int(9) UNSIGNED;comment:关联的微信报告id;default:0;" description:"关联的微信报告id"`
@@ -117,12 +149,16 @@ type WechatArticleAbstractView struct {
 	Title                   string `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
 	Link                    string `gorm:"column:link;type:varchar(255);comment:链接;" description:"链接"`
 	TagId                   int    `gorm:"column:tag_id;type:int(9) UNSIGNED;comment:品种id;default:0;" description:"品种id"`
+	QuestionId              int    `gorm:"column:question_id" description:"提示词Id"`
+	Tags                    string `gorm:"column:tags" description:"标签"`
+	TagsName                string `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	QuestionContent         string `gorm:"column:question_content" description:"提示词内容"`
 }
 
 type WechatArticleAbstractItem struct {
 	WechatArticleAbstractId int       `gorm:"column:wechat_article_abstract_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"wechat_article_abstract_id"`
 	WechatArticleId         int       `gorm:"column:wechat_article_id;type:int(9) UNSIGNED;comment:关联的微信报告id;default:0;" description:"关联的微信报告id"`
-	Abstract                string    `gorm:"column:abstract;type:longtext;comment:摘要内容;" description:"摘要内容"` //
+	Content                 string    `gorm:"column:content;type:longtext;comment:摘要内容;" description:"摘要内容"`
 	Version                 int       `gorm:"column:version;type:int(10) UNSIGNED;comment:版本号;default:1;" description:"版本号"`
 	VectorKey               string    `gorm:"column:vector_key;type:varchar(255);comment:向量key标识;" description:"向量key标识"`
 	ModifyTime              time.Time `gorm:"column:modify_time;type:datetime;default:NULL;" description:"modify_time"`
@@ -130,13 +166,17 @@ type WechatArticleAbstractItem struct {
 	Title                   string    `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
 	Link                    string    `gorm:"column:link;type:varchar(255);comment:链接;" description:"链接"`
 	TagId                   int       `gorm:"column:tag_id;type:int(9) UNSIGNED;comment:品种id;default:0;" description:"品种id"`
+	QuestionId              int       `gorm:"column:question_id" description:"提示词Id"`
+	Tags                    string    `gorm:"column:tags" description:"标签"`
+	TagsName                string    `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	QuestionContent         string    `gorm:"column:question_content" description:"提示词内容"`
 }
 
 func (m *WechatArticleAbstractItem) ToView() WechatArticleAbstractView {
 	return WechatArticleAbstractView{
 		WechatArticleAbstractId: m.WechatArticleAbstractId,
 		WechatArticleId:         m.WechatArticleId,
-		Abstract:                m.Abstract,
+		Abstract:                m.Content,
 		Version:                 m.Version,
 		VectorKey:               m.VectorKey,
 		ModifyTime:              utils.DateStrToDateTimeStr(m.ModifyTime),
@@ -144,16 +184,47 @@ func (m *WechatArticleAbstractItem) ToView() WechatArticleAbstractView {
 		Title:                   m.Title,
 		Link:                    m.Link,
 		TagId:                   m.TagId,
+		QuestionId:              m.QuestionId,
+		Tags:                    m.Tags,
+		TagsName:                m.TagsName,
+		QuestionContent:         m.QuestionContent,
 	}
 }
 
-func (m *WechatArticleAbstract) WechatArticleAbstractItem(list []*WechatArticleAbstractItem) (wechatArticleViewList []WechatArticleAbstractView) {
-	wechatArticleViewList = make([]WechatArticleAbstractView, 0)
+type WechatPlatArticleAbstractItem struct {
+	WechatArticleAbstractId int       `gorm:"column:wechat_article_abstract_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"wechat_article_abstract_id"`
+	WechatArticleId         int       `gorm:"column:wechat_article_id;type:int(9) UNSIGNED;comment:关联的微信报告id;default:0;" description:"关联的微信报告id"`
+	Abstract                string    `gorm:"column:abstract;type:longtext;comment:摘要内容;" description:"摘要内容"` //
+	Version                 int       `gorm:"column:version;type:int(10) UNSIGNED;comment:版本号;default:1;" description:"版本号"`
+	VectorKey               string    `gorm:"column:vector_key;type:varchar(255);comment:向量key标识;" description:"向量key标识"`
+	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"`
+	Title                   string    `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
+	Link                    string    `gorm:"column:link;type:varchar(255);comment:链接;" description:"链接"`
+	TagId                   int       `gorm:"column:tag_id;type:int(9) UNSIGNED;comment:品种id;default:0;" description:"品种id"`
+	QuestionId              int       `gorm:"column:question_id" description:"提示词Id"`
+	Tags                    string    `gorm:"column:tags" description:"标签"`
+	TagsName                string    `gorm:"column:tags_name" description:"标签名,多个用英文逗号隔开"`
+	QuestionContent         string    `gorm:"column:question_content" description:"提示词内容"`
+}
 
-	for _, v := range list {
-		wechatArticleViewList = append(wechatArticleViewList, v.ToView())
+func (m *WechatPlatArticleAbstractItem) ToView() WechatArticleAbstractView {
+	return WechatArticleAbstractView{
+		WechatArticleAbstractId: m.WechatArticleAbstractId,
+		WechatArticleId:         m.WechatArticleId,
+		Abstract:                m.Abstract,
+		Version:                 m.Version,
+		VectorKey:               m.VectorKey,
+		ModifyTime:              utils.DateStrToDateTimeStr(m.ModifyTime),
+		CreateTime:              utils.DateStrToDateTimeStr(m.CreateTime),
+		Title:                   m.Title,
+		Link:                    m.Link,
+		TagId:                   m.TagId,
+		QuestionId:              m.QuestionId,
+		Tags:                    m.Tags,
+		TagsName:                m.TagsName,
+		QuestionContent:         m.QuestionContent,
 	}
-	return
 }
 
 func (m *WechatArticleAbstract) GetListByPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAbstractItem, err error) {
@@ -185,19 +256,18 @@ func (m *WechatArticleAbstract) GetCountByPlatformCondition(condition string, pa
 }
 
 func (m *WechatArticleAbstract) GetPageListByPlatformCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatArticleAbstractItem, err error) {
-
 	total, err = m.GetCountByPlatformCondition(condition, pars)
 	if err != nil {
 		return
 	}
 	if total > 0 {
-		items, err = m.GetListByPlatformCondition(`a.wechat_article_abstract_id,a.wechat_article_id,a.content AS abstract,a.version,a.vector_key,b.title,b.link,a.modify_time,a.create_time`, condition, pars, startSize, pageSize)
+		items, err = m.GetListByPlatformCondition(`a.*`, condition, pars, startSize, pageSize)
 	}
 
 	return
 }
 
-func (m *WechatArticleAbstract) GetListByTagAndPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAbstractItem, err error) {
+func (m *WechatArticleAbstract) GetListByTagAndPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatPlatArticleAbstractItem, err error) {
 	if field == "" {
 		field = "*"
 	}
@@ -227,7 +297,7 @@ func (m *WechatArticleAbstract) GetCountByTagAndPlatformCondition(condition stri
 	return
 }
 
-func (m *WechatArticleAbstract) GetPageListByTagAndPlatformCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatArticleAbstractItem, err error) {
+func (m *WechatArticleAbstract) GetPageListByTagAndPlatformCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatPlatArticleAbstractItem, err error) {
 
 	total, err = m.GetCountByTagAndPlatformCondition(condition, pars)
 	if err != nil {

+ 63 - 6
models/report.go

@@ -4,6 +4,7 @@ import (
 	sql2 "database/sql"
 	"errors"
 	"eta/eta_api/global"
+	"eta/eta_api/models/report"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -91,6 +92,8 @@ type Report struct {
 	InheritReportId     int       `description:"待继承的报告ID"`
 	VoiceGenerateType   int       `description:"音频生成方式,0:系统生成,1:人工上传"`
 	RaiReportId         int       `description:"RAI报告ID"`
+	FreeLayoutConfig    string    `description:"'自由布局配置"`
+	MiniShow            int       `description:"是否在C端展示:0-否;1-是"`
 }
 
 func (m *Report) AfterFind(db *gorm.DB) (err error) {
@@ -169,6 +172,7 @@ type ReportList struct {
 	ClassifyNameThird   string    `description:"三级分类名称"`
 	InheritReportId     int       `description:"待继承的报告ID"`
 	RaiReportId         int       `description:"RAI报告ID"`
+	MiniShow            int       `description:"是否在C端展示:0-否;1-是"`
 }
 
 func (m *ReportList) AfterFind(db *gorm.DB) (err error) {
@@ -447,6 +451,8 @@ type ReportDetail struct {
 	IsPublicPublish     int8      `description:"是否公开发布,1:是,2:否"`
 	ReportCreateTime    time.Time `description:"报告时间创建时间"`
 	RaiReportId         int       `description:"RAI报告ID"`
+	FreeLayoutConfig    string    `description:"'自由布局配置"`
+	MiniShow            int       `description:"是否在C端展示:0-否;1-是"`
 }
 
 func (m *ReportDetail) AfterFind(db *gorm.DB) (err error) {
@@ -615,10 +621,11 @@ type AddReq struct {
 	HeadResourceId     int    `description:"版头资源ID"`
 	EndResourceId      int    `description:"版尾资源ID"`
 	CollaborateType    int8   `description:"协作方式,1:个人,2:多人协作。默认:1"`
-	ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局,3:自由布局。默认:1"`
 	IsPublicPublish    int8   `description:"是否公开发布,1:是,2:否"`
 	InheritReportId    int    `description:"待继承的报告ID"`
 	GrantAdminIdList   []int  `description:"授权用户id列表"`
+	MiniShow           int    `description:"是否在C端展示:0-否;1-是"`
 }
 
 type PrePublishReq struct {
@@ -666,9 +673,10 @@ type EditReq struct {
 	HeadResourceId     int    `description:"版头资源ID"`
 	EndResourceId      int    `description:"版尾资源ID"`
 	//CollaborateType    int8   `description:"协作方式,1:个人,2:多人协作。默认:1"`
-	//ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
+	//ReportLayout       int8   `description:"报告布局,1:常规布局,2:智能布局,3:自由布局。默认:1"`
 	IsPublicPublish  int8  `description:"是否公开发布,1:是,2:否"`
 	GrantAdminIdList []int `description:"授权用户id列表"`
+	MiniShow         int   `description:"是否在C端展示:0-否;1-是"`
 }
 
 type EditResp struct {
@@ -815,7 +823,6 @@ type SaveReportContent struct {
 	Content  string `description:"内容"`
 	ReportId int    `description:"报告id"`
 	NoChange int    `description:"内容是否未改变:1:内容未改变"`
-
 	// 以下是智能研报相关
 	ContentStruct  string `description:"内容组件"`
 	HeadImg        string `description:"报告头图地址"`
@@ -824,6 +831,9 @@ type SaveReportContent struct {
 	NeedSplice     int    `description:"是否拼接版头版位的标记,主要是为了兼容历史报告。0-不需要 1-需要"`
 	HeadResourceId int    `description:"版头资源ID"`
 	EndResourceId  int    `description:"版尾资源ID"`
+	//自由布局相关
+	FreeLayoutContentPages []report.ContentPage `description:"自由布局页面数据"`
+	FreeLayoutConfig       string               `description:"自由布局配置"`
 }
 
 //func EditReportContent(reportId int, content, contentSub string) (err error) {
@@ -953,9 +963,10 @@ func (reportInfo *Report) UpdateReport(cols []string) (err error) {
 // @Description: 晨周报详情
 type ReportDetailView struct {
 	*ReportDetail
-	ChapterList    []*ReportChapter
-	GrandAdminList []ReportDetailViewAdmin
-	PermissionList []ReportDetailViewPermission
+	ChapterList            []*ReportChapter
+	GrandAdminList         []ReportDetailViewAdmin
+	PermissionList         []ReportDetailViewPermission
+	FreeLayoutContentPages []*report.ContentPage
 }
 
 // ReportDetailViewAdmin
@@ -1700,3 +1711,49 @@ func GetAllPublishReportId() (items []int, err error) {
 	err = o.Raw(sql).Find(&items).Error
 	return
 }
+
+func InsertOrUpdateReportFreeLayoutContentPage(reportInfo *Report, ormList []*report.ReportFreeLayout) (err error) {
+	tx := global.DbMap[utils.DbNameReport].Begin()
+
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+	reportUpdateCols := []string{"Content", "ContentSub", "ContentStruct", "HeadImg", "EndImg", "CanvasColor", "HeadResourceId", "EndResourceId", "ModifyTime", "ContentModifyTime", "FreeLayoutConfig"}
+	err = tx.Model(&reportInfo).Select(reportUpdateCols).Updates(reportInfo).Error
+	return report.BatchInsertOrUpdatePages(tx, ormList, false, reportInfo.Id, 0)
+}
+func UpdateChapterFreeLayoutContentPage(reportInfo *Report, chapterInfo *ReportChapter, updateCols []string, tickerList []*ReportChapterTicker, ormList []*report.ReportFreeLayout) (err error) {
+	tx := global.DbMap[utils.DbNameReport].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	if err = tx.Model(&reportInfo).Select([]string{"LastModifyAdminId", "LastModifyAdminName", "ModifyTime", "FreeLayoutConfig"}).Updates(reportInfo).Error; err != nil {
+		return
+	}
+	// 更新章节
+	if err = tx.Model(&chapterInfo).Select(updateCols).Updates(chapterInfo).Error; err != nil {
+		return
+	}
+	sql := ` DELETE FROM report_chapter_ticker WHERE report_chapter_id = ? `
+	// 清空并新增章节ticker
+	if err = tx.Exec(sql, chapterInfo.ReportChapterId).Error; err != nil {
+		return
+	}
+	tickerLen := len(tickerList)
+	if tickerLen > 0 {
+		err = tx.CreateInBatches(tickerList, len(tickerList)).Error
+		if err != nil {
+			return
+		}
+	}
+	return report.BatchInsertOrUpdatePages(tx, ormList, true, reportInfo.Id, chapterInfo.ReportChapterId)
+}

+ 266 - 0
models/report/report_free_layout.go

@@ -0,0 +1,266 @@
+package report
+
+import (
+	sql2 "database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"time"
+)
+
+type ReportFreeLayout struct {
+	Id              int       `gorm:"primaryKey;autoIncrement;column:id"` // 主键
+	ReportId        int       `gorm:"column:report_id"`                   // 研报Id
+	ReportChapterId int       `gorm:"column:report_chapter_id"`           // 章节Id
+	Page            int       `gorm:"column:page"`                        // 页码
+	IsChapter       int       `gorm:"column:is_chapter"`                  // 是否多章节
+	Content         string    `gorm:"column:content;size:255"`            // 内容
+	ContentStruct   string    `gorm:"column:content_struct;size:255"`     // 内容
+	CreateTime      time.Time `gorm:"column:create_time"`                 // 创建时间
+	ModifyTime      time.Time `gorm:"column:modify_time"`                 // 修改时间
+}
+type PagePositionEnum string
+
+const (
+	Left   PagePositionEnum = "left"
+	Right  PagePositionEnum = "right"
+	Center PagePositionEnum = "center"
+)
+
+type ContentPage struct {
+	Id              int    `json:"Id"`
+	Page            int    `json:"Page"`
+	Content         string `json:"Content"`
+	ContentStruct   string `json:"ContentStruct"`
+	ReportId        int    `json:"ChapterId"`
+	ReportChapterId int    `json:"ReportChapterId"`
+}
+
+func (cp *ContentPage) ToView(isChapter bool, ReportId int, ReportChapterId int) *ReportFreeLayout {
+	if isChapter {
+		return &ReportFreeLayout{
+			ReportId:        ReportId,
+			ReportChapterId: ReportChapterId,
+			Page:            cp.Page,
+			IsChapter:       1,
+			Content:         cp.Content,
+			ContentStruct:   cp.ContentStruct,
+			CreateTime:      time.Now(),
+		}
+	} else {
+		return &ReportFreeLayout{
+			ReportId:        ReportId,
+			ReportChapterId: ReportChapterId,
+			Page:            cp.Page,
+			IsChapter:       0,
+			Content:         cp.Content,
+			ContentStruct:   cp.ContentStruct,
+			CreateTime:      time.Now(),
+		}
+	}
+}
+func (cp *ReportFreeLayout) ToPageView() *ContentPage {
+	return &ContentPage{
+		Page:            cp.Page,
+		Content:         cp.Content,
+		ContentStruct:   cp.ContentStruct,
+		ReportId:        cp.ReportId,
+		ReportChapterId: cp.ReportChapterId,
+	}
+}
+func ToOrmViewList(srcList []ContentPage, isChapter bool, ReportId int, ReportChapterId int) (list []*ReportFreeLayout) {
+	for _, v := range srcList {
+		list = append(list, v.ToView(isChapter, ReportId, ReportChapterId))
+	}
+	return
+}
+
+func ToPageViewList(srcList []*ReportFreeLayout) (list []*ContentPage) {
+	for _, v := range srcList {
+		list = append(list, v.ToPageView())
+	}
+	return
+}
+
+// TableName 设置表名
+func (*ReportFreeLayout) TableName() string {
+	return "report_free_layout"
+}
+
+func SortPage(reportId int, tx *gorm.DB) (err error) {
+	if tx == nil {
+		tx = global.DbMap[utils.DbNameReport].Begin()
+		defer func() {
+			if err != nil {
+				_ = tx.Rollback()
+				return
+			}
+			_ = tx.Commit()
+		}()
+	}
+	sql := `select * from report_free_layout where report_id = ?  and is_chapter=1   order by page asc`
+	var ormList []*ReportFreeLayout
+	err = tx.Raw(sql, reportId).Find(&ormList).Error
+	if err != nil {
+		return
+	}
+	if len(ormList) == 0 {
+		return
+	}
+	chapterPages := make(map[int][]*ReportFreeLayout)
+	for _, v := range ormList {
+		chapterPages[v.ReportChapterId] = append(chapterPages[v.ReportChapterId], v)
+	}
+
+	chapterSql := `select report_chapter_id from report_chapter where report_id =? order by sort asc`
+	var chapterIds []int
+	err = tx.Raw(chapterSql, reportId).Scan(&chapterIds).Error
+	if err != nil {
+		return
+	}
+	initPage := 1
+	for _, chapter := range chapterIds {
+		chapterList := chapterPages[chapter]
+		for _, v := range chapterList {
+			v.Page = initPage
+			initPage++
+		}
+
+	}
+	var updateList []*ReportFreeLayout
+	for _, chapterList := range chapterPages {
+		updateList = append(updateList, chapterList...)
+	}
+	err = tx.Model(&ReportFreeLayout{}).Clauses(clause.OnConflict{
+		Columns:   []clause.Column{{Name: "Id"}},
+		DoUpdates: clause.AssignmentColumns([]string{"Page"}),
+	}).CreateInBatches(updateList, len(updateList)).Error
+	return
+}
+
+func DeleteChapters(reportId int, chapterId int) (err error) {
+	tx := global.DbMap[utils.DbNameReport].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+	err = tx.Exec("delete from report_free_layout where   report_id = ?  and report_chapter_id=? and is_chapter=1", reportId, chapterId).Error
+	if err != nil {
+		return
+	}
+	err = SortPage(reportId, tx)
+	return
+}
+func BatchInsertOrUpdatePages(tx *gorm.DB, list []*ReportFreeLayout, isChapter bool, reportId, chapterId int) (err error) {
+	if isChapter {
+		err = tx.Exec("delete from report_free_layout where   report_id = ?  and report_chapter_id=? and is_chapter=1", reportId, chapterId).Error
+		if err != nil {
+			return
+		}
+		//err = tx.Model(&ReportFreeLayout{}).Clauses(clause.OnConflict{
+		//	Columns:   []clause.Column{{Name: "id"}},
+		//	DoUpdates: clause.AssignmentColumns([]string{"content", "content_struct", "modify_time"}),
+		//}).CreateInBatches(list, len(list)).Error
+		err = tx.Model(&ReportFreeLayout{}).CreateInBatches(list, len(list)).Error
+		if err != nil {
+			return
+		}
+		err = SortPage(reportId, tx)
+		return
+	} else {
+		err = tx.Exec("delete from  report_free_layout where report_id = ? and  is_chapter=0", reportId).Error
+		if err != nil {
+			return
+		}
+		err = tx.Model(&ReportFreeLayout{}).CreateInBatches(list, len(list)).Error
+		//.Clauses(clause.OnConflict{
+		//	Columns:   []clause.Column{{Name: "id"}},
+		//	DoUpdates: clause.AssignmentColumns([]string{"content", "content_struct", "page", "modify_time"}),
+		//})
+
+	}
+	return
+}
+func GetPrevFreeLayoutChaptersPagesByChapterId(reportId int, chapterId int) (pageNum int, err error) {
+	var pageNumNullable sql2.NullInt64
+	sql := `SELECT count(*) 
+FROM report_free_layout rfl
+JOIN report_chapter rc ON rc.report_id = rfl.report_id and rc.report_chapter_id=rfl.report_chapter_id
+WHERE rfl.report_id = ?
+  AND rc.sort < (
+    SELECT sort
+    FROM report_chapter
+    WHERE report_id = ? and report_chapter_id=?
+  )`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId, reportId, chapterId).Scan(&pageNumNullable).Error
+	if err != nil {
+		return
+	}
+	if pageNumNullable.Valid {
+		pageNum = int(pageNumNullable.Int64)
+	}
+	return
+}
+func GetFreeLayoutChapterPagesByReportId(reportId int) (list []*ContentPage, err error) {
+	var ormList []*ReportFreeLayout
+	sql := `select rfl.*,rc.sort from report_free_layout rfl LEFT JOIN report_chapter rc on rc.report_id=rfl.report_id and rc.report_chapter_id=rfl.report_chapter_id where rfl.report_id =? order by rc.sort,rfl.page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, reportId).Find(&ormList).Error
+	if err != nil {
+		return nil, err
+	}
+	list = ToPageViewList(ormList)
+	return
+}
+func GetSingleFreeLayoutChapterPagesByReportId(reportId, chapterId int) (list []*ContentPage, err error) {
+	var ormList []*ReportFreeLayout
+	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(&ormList).Error
+	if err != nil {
+		return nil, err
+	}
+	list = ToPageViewList(ormList)
+	return
+}
+func GetFreeLayoutPagesByReportId(id int) (list []*ContentPage, err error) {
+	var ormList []*ReportFreeLayout
+	sql := `select * from report_free_layout  where report_id =? and is_chapter=0 order by page asc`
+	err = global.DbMap[utils.DbNameReport].Raw(sql, id).Find(&ormList).Error
+	if err != nil {
+		return nil, err
+	}
+	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
+}

+ 19 - 11
models/report_chapter.go

@@ -134,14 +134,17 @@ func (m *ReportChapterItem) ConvDateTimeStr() {
 // @Description: 章节详情(带有一些额外的数据)
 type ReportChapterItemResp struct {
 	ReportChapterItem
-	GrandAdminIdList []int  `description:"授权的用户id列表"`
-	PermissionIdList []int  `description:"关联的品种id列表"`
-	CanEdit          bool   `description:"是否可编辑"`
-	Editor           string `description:"编辑人"`
-	HeadImg          string `description:"报告头图地址"`
-	EndImg           string `description:"报告尾图地址"`
-	HeadStyle        string `description:"版头样式"`
-	EndStyle         string `description:"版尾样式"`
+	FreeLayoutContentPages []*report.ContentPage
+	FreeLayoutConfig       string
+	PreviousPagesNum       int
+	GrandAdminIdList       []int  `description:"授权的用户id列表"`
+	PermissionIdList       []int  `description:"关联的品种id列表"`
+	CanEdit                bool   `description:"是否可编辑"`
+	Editor                 string `description:"编辑人"`
+	HeadImg                string `description:"报告头图地址"`
+	EndImg                 string `description:"报告尾图地址"`
+	HeadStyle              string `description:"版头样式"`
+	EndStyle               string `description:"版尾样式"`
 }
 
 type ReportChapterResp struct {
@@ -222,6 +225,9 @@ type EditReportChapterReq struct {
 	CanvasColor    string `description:"画布颜色"`
 	HeadResourceId int    `description:"版头资源ID"`
 	EndResourceId  int    `description:"版尾资源ID"`
+	//自由布局研报相关
+	FreeLayoutContentPages []report.ContentPage `description:"自由布局内容"`
+	FreeLayoutConfig       string               `description:"'自由布局配置"`
 }
 
 type EditTickList struct {
@@ -504,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

+ 26 - 2
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
+				}
+			}
+
 		}
 	}
 
@@ -353,7 +377,7 @@ func GetReportListCountByAuthorized(condition string, pars []interface{}) (count
 func GetReportListByAuthorized(condition string, pars []interface{}, startSize, pageSize int) (items []*ReportList, err error) {
 	o := global.DbMap[utils.DbNameReport]
 
-	sql := `SELECT id,classify_id_first,classify_name_first,classify_id_second,classify_name_second,classify_id_third,classify_name_third,title,stage,create_time,author,report_layout,collaborate_type,is_public_publish,abstract,has_chapter,publish_time FROM report as a WHERE 1=1  `
+	sql := `SELECT id,classify_id_first,classify_name_first,classify_id_second,classify_name_second,classify_id_third,classify_name_third,title,stage,create_time,author,report_layout,collaborate_type,is_public_publish,abstract,has_chapter,publish_time,mini_show FROM report as a WHERE 1=1  `
 	if condition != "" {
 		sql += condition
 	}

+ 1 - 1
models/sandbox/sandbox_classify.go

@@ -129,7 +129,7 @@ func GetSandboxInfoCountByClassifyId(classifyId int) (count int, err error) {
 	var sql string
 	var pars []interface{}
 	if utils.DbDriverName == utils.DbDriverByDm {
-		sql = `WITH RECURSIVE classify_tree AS (
+		sql = `WITH RECURSIVE classify_tree(sandbox_classify_id, parent_id, level) AS (
     -- 基础查询:获取起始节点
     SELECT sandbox_classify_id, parent_id, 1 as level
     FROM sandbox_classify

+ 2 - 2
models/system/sys_role.go

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

+ 1 - 1
models/system/sys_user.go

@@ -56,7 +56,7 @@ type Admin struct {
 	OpenId                    string    `description:"弘则部门公众号的openid"`
 	UnionId                   string    `description:"微信公众平台唯一标识"`
 	EdbPermission             int8      `description:"指标库操作权限,0:只能操作 自己的,1:所有指标可操作"`
-	MysteelChemicalPermission int8      `description:"钢联化工指标操作权限,0:只能操作 自己的,1:所有指标可操作"`
+	MysteelChemicalPermission int8      `description:"上海钢联指标操作权限,0:只能操作 自己的,1:所有指标可操作"`
 	PredictEdbPermission      int8      `description:"预测指标库操作权限,0:只能操作 自己的,1:所有预测指标可操作"`
 	Province                  string    `description:"省"`
 	ProvinceCode              string    `description:"省编码"`

+ 1 - 1
models/target.go

@@ -306,7 +306,7 @@ func GetEdbinfoItemList(condition string, pars []interface{}, startSize, pageSiz
 
 // EdbParamsInfo 指标数据结构体
 type EdbParamsInfo struct {
-	Unit      string `gorm:"column(UNIT);" description:"单位" gorm:"column:UNIT"`
+	Unit      string `description:"单位" gorm:"column:UNIT"`
 	Frequency string `gorm:"column:frequency;" description:"单位"`
 }
 

+ 108 - 0
routers/commentsRouter.go

@@ -358,6 +358,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/ai_predict_model:AiPredictModelIndexController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/ai_predict_model:AiPredictModelIndexController"],
+        beego.ControllerComments{
+            Method: "SearchByEs",
+            Router: `/chart/search_by_es`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/ai_predict_model:AiPredictModelIndexController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/ai_predict_model:AiPredictModelIndexController"],
         beego.ControllerComments{
             Method: "DashboardDetail",
@@ -8845,6 +8854,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: "GenerateAbstract",
+            Router: `/question/abstract/generate`,
+            AllowHTTPMethods: []string{"post"},
+            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: "Add",
@@ -8854,6 +8872,24 @@ 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: "SetDefault",
+            Router: `/question/default/set`,
+            AllowHTTPMethods: []string{"post"},
+            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: "UnSetDefault",
+            Router: `/question/default/unset`,
+            AllowHTTPMethods: []string{"post"},
+            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: "Del",
@@ -8890,6 +8926,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: "CheckOpAuth",
+            Router: `/question/op_auth/check`,
+            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: "TitleList",
@@ -8899,6 +8944,51 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"],
+        beego.ControllerComments{
+            Method: "Del",
+            Router: `/abstract/eta_report/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/abstract/eta_report/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"],
+        beego.ControllerComments{
+            Method: "AddVector",
+            Router: `/abstract/eta_report/vector/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:RagEtaReportAbstractController"],
+        beego.ControllerComments{
+            Method: "VectorDel",
+            Router: `/abstract/eta_report/vector/del`,
+            AllowHTTPMethods: []string{"post"},
+            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: "AbstractList",
+            Router: `/eta_report/article/abstract/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",
@@ -8980,6 +9070,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
+        beego.ControllerComments{
+            Method: "KnowledgeList",
+            Router: `/knowledge/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/llm:WechatPlatformController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:WechatPlatformController"],
         beego.ControllerComments{
             Method: "TagList",
@@ -8998,6 +9097,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:WechatPlatformController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:WechatPlatformController"],
+        beego.ControllerComments{
+            Method: "AbstractList",
+            Router: `/wechat_platform/article/abstract/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/llm:WechatPlatformController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:WechatPlatformController"],
         beego.ControllerComments{
             Method: "ArticleDel",

+ 1 - 0
routers/router.go

@@ -80,6 +80,7 @@ func init() {
 				&llm.AbstractController{},
 				&llm.PromoteController{},
 				&llm.RagEtaReportController{},
+				&llm.RagEtaReportAbstractController{},
 			),
 		),
 		web.NSNamespace("/banner",

+ 5 - 5
services/ai_predict_model_classify.go

@@ -156,7 +156,7 @@ func moveAiPredictModelClassify(parentClassify, edbClassifyInfo, prevClassify, n
 	updateCol := make([]string, 0)
 
 	// 移动对象为分类, 判断分类是否存在
-	if edbClassifyInfo != nil {
+	if edbClassifyInfo != nil && edbClassifyInfo.AiPredictModelClassifyId > 0 {
 		oldParentId := edbClassifyInfo.ParentId
 		oldLevel := edbClassifyInfo.Level
 		var classifyIds []int
@@ -271,7 +271,7 @@ func moveAiPredictModelClassify(parentClassify, edbClassifyInfo, prevClassify, n
 			}
 
 			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
-			if firstClassify != nil && firstClassify.Sort == 0 {
+			if firstClassify != nil && firstClassify.AiPredictModelClassifyId > 0 && firstClassify.Sort == 0 {
 				updateSortStr := ` sort + 1 `
 				_ = aiPredictModel.UpdateAiPredictModelClassifySortByParentId(parentClassifyId, firstClassify.AiPredictModelClassifyId-1, 0, updateSortStr)
 				//该分类下的所有指标也需要+1
@@ -286,7 +286,7 @@ func moveAiPredictModelClassify(parentClassify, edbClassifyInfo, prevClassify, n
 				}
 
 				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
-				if firstEdb != nil && firstEdb.Sort == 0 {
+				if firstEdb != nil && firstEdb.AiPredictModelIndexId > 0 && firstEdb.Sort == 0 {
 					updateSortStr := ` sort + 1 `
 					_ = aiPredictModel.UpdateAiPredictModelIndexSortByClassifyId(parentClassifyId, 0, firstEdb.AiPredictModelIndexId-1, updateSortStr)
 					_ = aiPredictModel.UpdateAiPredictModelClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
@@ -403,7 +403,7 @@ func moveAiPredictModelClassify(parentClassify, edbClassifyInfo, prevClassify, n
 			}
 
 			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
-			if firstClassify != nil && firstClassify.Sort == 0 {
+			if firstClassify != nil && firstClassify.AiPredictModelClassifyId> 0 && firstClassify.Sort == 0 {
 				updateSortStr := ` sort + 1 `
 				_ = aiPredictModel.UpdateAiPredictModelClassifySortByParentId(parentClassifyId, firstClassify.AiPredictModelClassifyId-1, 0, updateSortStr)
 				//该分类下的所有指标也需要+1
@@ -418,7 +418,7 @@ func moveAiPredictModelClassify(parentClassify, edbClassifyInfo, prevClassify, n
 				}
 
 				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
-				if firstEdb != nil && firstEdb.Sort == 0 {
+				if firstEdb != nil && firstEdb.AiPredictModelIndexId > 0 && firstEdb.Sort == 0 {
 					updateSortStr := ` sort + 1 `
 					_ = aiPredictModel.UpdateAiPredictModelIndexSortByClassifyId(parentClassifyId, 0, firstEdb.AiPredictModelIndexId-1, updateSortStr)
 					_ = aiPredictModel.UpdateAiPredictModelClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)

+ 179 - 4
services/ai_predict_model_index.go

@@ -8,10 +8,11 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"sort"
+	"strconv"
 	"time"
 )
 
-func ImportAiPredictModelIndexAndData(imports []*aiPredictModel.AiPredictModelImportData) (err error) {
+func ImportAiPredictModelIndexAndData(imports []*aiPredictModel.AiPredictModelImportData, adminId int, adminRealName string) (err error) {
 	if len(imports) == 0 {
 		return
 	}
@@ -33,6 +34,12 @@ func ImportAiPredictModelIndexAndData(imports []*aiPredictModel.AiPredictModelIm
 	updateCols := []string{indexOb.Cols().ClassifyId, indexOb.Cols().ModelFramework, indexOb.Cols().PredictDate, indexOb.Cols().PredictValue, indexOb.Cols().DirectionAccuracy, indexOb.Cols().AbsoluteDeviation, indexOb.Cols().ExtraConfig, indexOb.Cols().SysUserId, indexOb.Cols().SysUserRealName, indexOb.Cols().ModifyTime}
 	updateIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
 	createIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
+
+	maxSort, err := indexOb.GetSortMax()
+	if err != nil {
+		err = fmt.Errorf("获取标的最大排序失败, %v", err)
+		return
+	}
 	for _, v := range imports {
 		exist := indexNameItem[v.Index.IndexName]
 		// 编辑
@@ -52,28 +59,42 @@ func ImportAiPredictModelIndexAndData(imports []*aiPredictModel.AiPredictModelIm
 				b, _ := json.Marshal(oldConfig)
 				v.Index.ExtraConfig = string(b)
 			}
-
+			
 			v.Index.AiPredictModelIndexId = exist.AiPredictModelIndexId
 			v.Index.IndexCode = exist.IndexCode
 			updateIndexes = append(updateIndexes, v)
 			continue
 		}
 
-		// 新增
+		// 新增标的/图表
 		indexCode, e := utils.GenerateEdbCode(1, "IPM")
 		if e != nil {
 			err = fmt.Errorf("生成标的编码失败, %v", e)
 			return
 		}
 		v.Index.IndexCode = indexCode
+		v.Charts = GetAiPredictCharts(v.Index.IndexName, adminId, adminRealName)
+		maxSort = maxSort + 1
+		v.Index.Sort = maxSort
 		createIndexes = append(createIndexes, v)
 	}
 
 	// 新增/更新指标
-	if e := indexOb.ImportIndexAndData(createIndexes, updateIndexes, updateCols); e != nil {
+	chartIds, e := indexOb.ImportIndexAndData(createIndexes, updateIndexes, updateCols)
+	if e != nil {
 		err = fmt.Errorf("导入指标失败, %v", e)
 		return
 	}
+
+	// 更新图表ES
+	if len(chartIds) == 0 {
+		return
+	}
+	go func() {
+		for _, v := range chartIds {
+			data.EsAddOrEditChartInfo(v)
+		}
+	}()
 	return
 }
 
@@ -114,8 +135,29 @@ func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex
 		confLeftMax = indexItem.LeftMax
 	}
 
+	// 获取指标对应的图表
+	chartSourceMapping := map[int]int{
+		aiPredictModel.ModelDataSourceMonthly: utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY,
+		aiPredictModel.ModelDataSourceDaily:   utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY,
+	}
+	chartInfo, e := data_manage.GetAiPredictChartInfoByIndexId(chartSourceMapping[source], indexItem.AiPredictModelIndexId)
+	if e != nil && !utils.IsErrNoRow(e) {
+		err = fmt.Errorf("获取标的图表失败, %v", e)
+		return
+	}
+
 	// 获取曲线图主题样式
 	chartView := new(data_manage.ChartInfoView)
+	if chartInfo != nil && chartInfo.ChartInfoId > 0 {
+		chartView.ChartInfoId = chartInfo.ChartInfoId
+		chartView.ChartName = chartInfo.ChartName
+		chartView.ChartNameEn = chartInfo.ChartNameEn
+		chartView.Source = chartInfo.Source
+		chartView.ChartImage = chartInfo.ChartImage
+	} else {
+		chartView.ChartName = indexItem.IndexName
+		chartView.ChartNameEn = indexItem.IndexName
+	}
 	chartView.ChartType = utils.CHART_SOURCE_DEFAULT
 	chartTheme, e := data.GetChartThemeConfig(0, chartView.ChartType, utils.CHART_TYPE_CURVE)
 	if e != nil {
@@ -264,3 +306,136 @@ func GetAiPredictChartDetailByData(indexItem *aiPredictModel.AiPredictModelIndex
 	resp.EdbInfoList = edbList
 	return
 }
+
+// GetAiPredictCharts 获取AI预测模型图表
+func GetAiPredictCharts(indexName string, adminId int, adminRealName string) (charts []*aiPredictModel.AiPredictModelImportCharts) {
+	charts = make([]*aiPredictModel.AiPredictModelImportCharts, 0)
+
+	// 日度/月度图表
+	frequencyArr := []int{aiPredictModel.ModelDataSourceMonthly, aiPredictModel.ModelDataSourceDaily}
+	sourceMapping := map[int]int{
+		aiPredictModel.ModelDataSourceMonthly: utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY,
+		aiPredictModel.ModelDataSourceDaily:   utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY,
+	}
+	suffixNameMapping := map[int]string{
+		aiPredictModel.ModelDataSourceMonthly: "预测模型/回测",
+		aiPredictModel.ModelDataSourceDaily:   "预测模型",
+	}
+
+	for _, v := range frequencyArr {
+		chartSource := sourceMapping[v]
+		newChart := new(aiPredictModel.AiPredictModelImportCharts)
+
+		// 新增图表
+		chartName := fmt.Sprintf("%s%s", indexName, suffixNameMapping[v])
+		chartInfo := new(data_manage.ChartInfo)
+		chartInfo.ChartName = chartName
+		chartInfo.ChartNameEn = chartName
+		chartInfo.ChartType = utils.CHART_TYPE_CURVE
+		chartInfo.Calendar = "公历"
+		chartInfo.SysUserId = adminId
+		chartInfo.SysUserRealName = adminRealName
+		chartInfo.CreateTime = time.Now()
+		chartInfo.ModifyTime = time.Now()
+		chartInfo.Source = chartSource
+		time.Sleep(time.Microsecond)
+		chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10))
+		newChart.ChartInfo = chartInfo
+
+		// chart_edb_mapping中edb_info_id为标的ID
+		edbMapping := new(data_manage.ChartEdbMapping)
+		//edbMapping.EdbInfoId = indexId
+		//edbMapping.UniqueCode = utils.MD5(fmt.Sprint(utils.CHART_PREFIX, "_", indexId, "_", strconv.FormatInt(time.Now().UnixNano(), 10)))
+		edbMapping.Source = chartSource
+		edbMapping.CreateTime = time.Now().Local()
+		edbMapping.ModifyTime = time.Now().Local()
+		newChart.EdbMappings = append(newChart.EdbMappings, edbMapping)
+
+		charts = append(charts, newChart)
+	}
+	return
+}
+
+// FixAiPredictCharts 修复AI预测模型图表
+func FixAiPredictCharts() {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println(err)
+		}
+		fmt.Println("修复完成")
+	}()
+	fmt.Println("开始修复")
+
+	indexOb := new(aiPredictModel.AiPredictModelIndex)
+	indexes, e := indexOb.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
+	if e != nil {
+		err = fmt.Errorf("获取所有标的失败, %v", e)
+		return
+	}
+	// 日度/月度图表
+	frequencyArr := []int{aiPredictModel.ModelDataSourceMonthly, aiPredictModel.ModelDataSourceDaily}
+	sourceMapping := map[int]int{
+		aiPredictModel.ModelDataSourceMonthly: utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY,
+		aiPredictModel.ModelDataSourceDaily:   utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY,
+	}
+	suffixNameMapping := map[int]string{
+		aiPredictModel.ModelDataSourceMonthly: "预测模型/回测",
+		aiPredictModel.ModelDataSourceDaily:   "预测模型",
+	}
+	chartOb := new(data_manage.ChartInfo)
+	for _, v := range indexes {
+		for _, fre := range frequencyArr {
+			chartSource := sourceMapping[fre]
+			item, e := data_manage.GetAiPredictChartInfoByIndexId(chartSource, v.AiPredictModelIndexId)
+			if e != nil && !utils.IsErrNoRow(e) {
+				err = fmt.Errorf("获取AI预测模型图表失败, %v", e)
+				return
+			}
+			// 由于标的名称是固定的所以chart_info没有什么可更新的, 已加入过就忽略
+			if item != nil && item.ChartInfoId > 0 {
+				fmt.Printf("标的%d-%d图表已存在, continue\n", v.AiPredictModelIndexId, chartSource)
+				continue
+			}
+
+			// 新增图表
+			chartName := fmt.Sprintf("%s%s", v.IndexName, suffixNameMapping[fre])
+			chartInfo := new(data_manage.ChartInfo)
+			chartInfo.ChartName = chartName
+			chartInfo.ChartNameEn = chartName
+			chartInfo.ChartType = utils.CHART_TYPE_CURVE
+			chartInfo.Calendar = "公历"
+			chartInfo.SysUserId = v.SysUserId
+			chartInfo.SysUserRealName = v.SysUserRealName
+			chartInfo.CreateTime = time.Now()
+			chartInfo.ModifyTime = time.Now()
+			chartInfo.Source = chartSource
+			time.Sleep(time.Microsecond)
+			chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + strconv.FormatInt(time.Now().UnixNano(), 10))
+
+			// chart_edb_mapping中edb_info_id为标的ID
+			mappings := make([]*data_manage.ChartEdbMapping, 0)
+			edbMapping := new(data_manage.ChartEdbMapping)
+			edbMapping.EdbInfoId = v.AiPredictModelIndexId
+			edbMapping.UniqueCode = utils.MD5(fmt.Sprint(utils.CHART_PREFIX, "_", v.AiPredictModelIndexId, "_", strconv.FormatInt(time.Now().UnixNano(), 10)))
+			edbMapping.Source = chartSource
+			edbMapping.CreateTime = time.Now().Local()
+			edbMapping.ModifyTime = time.Now().Local()
+			mappings = append(mappings, edbMapping)
+
+			// 新增图表
+			if e = chartOb.AddChartInfoAndEdbMappings(chartInfo, mappings); e != nil {
+				err = fmt.Errorf("新增图表及mapping失败, %v", e)
+				return
+			}
+
+			// 写入ES
+			if chartInfo.ChartInfoId <= 0 {
+				err = fmt.Errorf("图表ID有误")
+				return
+			}
+			go data.EsAddOrEditChartInfo(chartInfo.ChartInfoId)
+		}
+	}
+	return
+}

+ 1 - 0
services/crm_eta.go

@@ -121,6 +121,7 @@ type GetCrmTokenData struct {
 	AdminId         int    `description:"系统用户id"`
 	ProductName     string `description:"产品名称:admin,ficc,权益"`
 	Authority       int    `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,4:ficc销售组长"`
+	Enabled         int    `description:"禁启用状态:0-禁用;1-启用"`
 }
 
 // CodeLoginFromMiddleServer 中间服务-编码登录

+ 91 - 8
services/data/chart_info.go

@@ -3071,7 +3071,7 @@ func GetEdbSourceByEdbInfoIdList(chartEdbInfoMappingList []*data_manage.ChartEdb
 	}
 
 	for source, sourceName := range sourceMap {
-		if utils.InArrayByInt([]int{utils.DATA_SOURCE_MANUAL, utils.DATA_SOURCE_MYSTEEL_CHEMICAL}, source) {
+		if utils.InArrayByInt([]int{utils.DATA_SOURCE_MANUAL}, source) {
 			continue
 		}
 		sourceNameList = append(sourceNameList, sourceName)
@@ -4388,6 +4388,10 @@ func SeasonChartData(dataList []*data_manage.ChartEdbInfoMapping, seasonExtraCon
 
 			for _, v := range dataTimeMap {
 				valueList := dataTimeValueMap[v]
+				if len(valueList) <= 0 {
+					err = errors.New(`数据为空`)
+					return
+				}
 				stdev := utils.CalculateStandardDeviation(valueList)
 				stdev, _ = decimal.NewFromFloat(stdev).Round(4).Float64()
 
@@ -4517,8 +4521,10 @@ func MarkerLineCalculate(markerLine data_manage.MarkersLine, dataList interface{
 						length += 1
 					}
 				}
-				averge = averge / float64(length)
-				value = fmt.Sprintf("%.2f", averge)
+				if length > 0 {
+					averge = averge / float64(length)
+					value = fmt.Sprintf("%.2f", averge)
+				}
 			}
 		} else {
 			dataList := dataList.([]*data_manage.EdbDataList)
@@ -4560,9 +4566,11 @@ func MarkerLineCalculate(markerLine data_manage.MarkersLine, dataList interface{
 					length += 1
 				}
 			}
-			averge = averge / float64(length)
+			if length > 0 {
+				averge = averge / float64(length)
+				value = fmt.Sprintf("%.2f", averge)
+			}
 
-			value = fmt.Sprintf("%.2f", averge)
 		}
 	} else if markerLine.Calculation == 2 {
 		// 区间均值加N倍标准差
@@ -4614,7 +4622,14 @@ func MarkerLineCalculate(markerLine data_manage.MarkersLine, dataList interface{
 						length += 1
 					}
 				}
-				averge = averge / float64(length)
+
+				if length > 0 {
+					averge = averge / float64(length)
+				}
+				if len(faloatList) <= 0 {
+					err = errors.New(`数据为空`)
+					return
+				}
 				stdev := utils.CalculateStandardDeviation(faloatList)
 				stdev, _ = decimal.NewFromFloat(stdev).Round(4).Float64()
 
@@ -4664,8 +4679,13 @@ func MarkerLineCalculate(markerLine data_manage.MarkersLine, dataList interface{
 					length += 1
 				}
 			}
-			averge = averge / float64(length)
-
+			if length > 0 {
+				averge = averge / float64(length)
+			}
+			if len(floatList) <= 0 {
+				err = errors.New(`数据为空`)
+				return
+			}
 			stdev := utils.CalculateStandardDeviation(floatList)
 			stdev, _ = decimal.NewFromFloat(stdev).Round(4).Float64()
 
@@ -5354,3 +5374,66 @@ func getEdbDataMapListForSeason(chartInfoId, chartType int, calendar, startDate,
 
 	return
 }
+
+// GetMarkerLine
+// @Description: 获取标识线
+// @author: Roc
+// @datetime 2025-05-16 18:42:59
+// @param markerLine data_manage.MarkersLine
+// @param edbList []*data_manage.ChartEdbInfoMapping
+// @param chartInfo *data_manage.ChartInfoView
+// @param startDate string
+// @param endDate string
+// @return newMarkerLine data_manage.MarkersLine
+// @return err error
+func GetMarkerLine(markerLine data_manage.MarkersLine, edbList []*data_manage.ChartEdbInfoMapping, chartInfo *data_manage.ChartInfoView, startDate, endDate string) (newMarkerLine data_manage.MarkersLine, err error) {
+	newMarkerLine = markerLine
+
+	// 如果是横轴,那么直接返回
+	if markerLine.Axis == 3 {
+		return
+	}
+
+	var dataList interface{}
+	switch markerLine.EdbType {
+	case 0: // 图中第一个指标
+		dataList = edbList[0].DataList
+
+	case 1: // 其他指标
+		edbInfo, tmpErr := data_manage.GetEdbInfoById(markerLine.EdbInfoId)
+		if tmpErr != nil {
+			err = fmt.Errorf("指标计算标识线获取指标信息异常" + tmpErr.Error())
+			return
+		}
+		// 判断时间区间不为跟随图表的情况
+		if markerLine.TimeIntervalType != 0 {
+			startDate = markerLine.StartDate.Date
+			endDate = markerLine.EndDate.Date
+		}
+		dataList, err = data_manage.GetEdbDataList(edbInfo.Source, edbInfo.SubSource, edbInfo.EdbInfoId, startDate, endDate)
+		if err != nil {
+			err = fmt.Errorf("指标计算标识线获取指标数据异常" + err.Error())
+			return
+		}
+	}
+
+	switch markerLine.TimeIntervalType {
+	// 0跟随图表 1自定义
+	case 0: // 0跟随图表
+		value, tmpErr := MarkerLineCalculate(markerLine, dataList, chartInfo)
+		if tmpErr != nil {
+			err = tmpErr
+			return
+		}
+		newMarkerLine.Value = value
+	case 1: // 自定义
+		value, tmpErr := MarkerLineCalculate(markerLine, dataList, chartInfo)
+		if tmpErr != nil {
+			err = fmt.Errorf("标识线配置异常" + err.Error())
+			return
+		}
+		newMarkerLine.Value = value
+	}
+
+	return
+}

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

@@ -49,7 +49,7 @@ func GetEdbChartClassifyList(source, subSource int) (resp data_manage.EdbChartCl
 			resp.List = append(resp.List, &item)
 		}
 
-	case 2: //钢联化工数据库
+	case 2: //上海钢联数据库
 		rootList, e := data_manage.GetBaseFromMysteelChemicalClassifyByParentId(0)
 		if e != nil && !utils.IsErrNoRow(e) {
 			err = e
@@ -207,7 +207,7 @@ func GetExcelMenuTreeRecursive(list []*excel.ExcelClassifyItems, parentId int) [
 }
 
 // GetMoveEdbChartList 获取待转移的指标/图表列表
-// @param source 来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+// @param source 来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 func GetMoveEdbChartList(source, subSource, userId int, keyword, classify string, startSize, pageSize int) (list []data_manage.MoveEdbChartList, total int, err error) {
 	var condition string
 	var pars []interface{}
@@ -265,7 +265,7 @@ func GetMoveEdbChartList(source, subSource, userId int, keyword, classify string
 			}
 		}
 
-	case 2: //钢联化工数据库
+	case 2: //上海钢联数据库
 		if keyword != `` {
 			condition += " AND (index_name like ? OR index_code like ? OR sys_user_real_name like ? ) "
 			pars = utils.GetLikeKeywordPars(pars, keyword, 3)
@@ -486,7 +486,7 @@ func GetMoveEdbChartList(source, subSource, userId int, keyword, classify string
 }
 
 // MoveEdbChart 转移指标/图表创建人
-// @param source 来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+// @param source 来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool, dataId, noDataId []string, keyword, classify string, opUserId int, opUserName string) (err error, errMsg string) {
 	adminInfo, err := system.GetSysAdminById(newUserId)
 	if err != nil {
@@ -564,8 +564,8 @@ func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool,
 			}
 		}
 
-	case 2: //钢联化工数据库
-		content += `(钢联化工数据库)`
+	case 2: //上海钢联数据库
+		content += `(上海钢联数据库)`
 		tmpList, tmpErr := data_manage.GetMysteelChemicalIndexListByIndexId(dataId)
 		if tmpErr != nil {
 			err = tmpErr
@@ -775,7 +775,7 @@ func MoveEdbChart(source, subSource, oldUserId, newUserId int, isSelectAll bool,
 // @Description: 通过原创建人转移指标/图表创建人
 // @author: Roc
 // @datetime 2024-03-26 15:11:12
-// @param sourceList []int 1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+// @param sourceList []int 1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 // @param oldUserId []int
 // @param userId int
 // @param opUserId int
@@ -825,8 +825,8 @@ func MoveAllEdbChartOld(sourceList, oldUserIdList []int, userId, opUserId int) (
 				err = models.ModifyEdbinfoUserIdByOldUserId(oldUserIdList, userId)
 			}
 
-		case 2: //钢联化工数据库
-			sourceStrList = append(sourceStrList, "钢联化工数据库")
+		case 2: //上海钢联数据库
+			sourceStrList = append(sourceStrList, "上海钢联数据库")
 			tmpList, tmpErr := data_manage.GetMysteelChemicalIndexListByUserId(oldUserIdList)
 			if tmpErr != nil {
 				err = tmpErr
@@ -1027,7 +1027,7 @@ func MoveAllEdbChartOld(sourceList, oldUserIdList []int, userId, opUserId int) (
 // @Description: 通过原创建人转移指标/图表创建人
 // @author: Roc
 // @datetime 2024-03-26 15:11:12
-// @param sourceList []int 1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+// @param sourceList []int 1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 // @param oldUserId []int
 // @param userId int
 // @param opUserId int
@@ -1049,7 +1049,7 @@ func MoveAllEdbChart(sourceList, oldUserIdList []int, userId, opUserId int) (err
 	var isMoveManual, isMoveMysteelChemical, isMoveEdb, isMovePredictEdb, isMoveChart, isMoveExcel bool
 	var customAnalysisIds []int
 
-	// 遍历需要转移的模块,1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格,并找出当前需要转移的资产
+	// 遍历需要转移的模块,1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格,并找出当前需要转移的资产
 	for _, source := range sourceList {
 		switch source {
 		case 1: //手工数据指标
@@ -1080,8 +1080,8 @@ func MoveAllEdbChart(sourceList, oldUserIdList []int, userId, opUserId int) (err
 				isMoveManual = true
 			}
 
-		case 2: //钢联化工数据库
-			sourceStrList = append(sourceStrList, "钢联化工数据库")
+		case 2: //上海钢联数据库
+			sourceStrList = append(sourceStrList, "上海钢联数据库")
 			tmpList, tmpErr := data_manage.GetMysteelChemicalIndexListByUserId(oldUserIdList)
 			if tmpErr != nil {
 				err = tmpErr
@@ -1311,7 +1311,7 @@ func GetMoveEdbChartCount(userId, countType int) (sourceMap map[int]int, err err
 	}
 
 	{
-		// 钢联化工数据库
+		// 上海钢联数据库
 		var condition string
 		var pars []interface{}
 		if userId > 0 {

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

@@ -7,7 +7,7 @@ import (
 
 type MessageDetailItem struct {
 	DataPermissionMoveRecordId int64  ` orm:"column(data_permission_move_record_id);pk"` // 数据操作记录id
-	Source                     int32  // 数据来源,1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+	Source                     int32  // 数据来源,1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 	SubSource                  int32  // 子来源 :ETA表格中的各种表格类型,以及图表的来源(这个是后续的扩展方向)
 	OpUniqueCode               string // 操作的唯一编码,主要是记录统一操作的日志
 	DataId                     string // 资产id(指标、图表、表格)

+ 4 - 2
services/data/edb_classify.go

@@ -399,13 +399,15 @@ func AddEdbClassify(classifyName string, parentId, level int, classifyType uint8
 }
 
 // EditEdbClassify 编辑指标分类
-func EditEdbClassify(classifyId, parentId int, classifyName, lang string, sysUser *system.Admin) (err error, errMsg string) {
+func EditEdbClassify(classifyId, parentId int, classifyName, lang string, sysUser *system.Admin, isPreditClassify bool) (err error, errMsg string) {
 	item, err := data_manage.GetEdbClassifyById(classifyId)
 	if err != nil {
 		errMsg = `保存失败`
 		return
 	}
-
+	if isPreditClassify {
+		parentId = item.ParentId // 预测指标的分类编辑时不支持修改父级分类
+	}
 	// 权限校验
 	{
 		// 已授权分类id

+ 9 - 9
services/data/edb_info.go

@@ -1819,7 +1819,7 @@ func EdbInfoAdd(source, subSource, classifyId int, edbCode, edbName, frequency,
 		utils.DATA_SOURCE_MANUAL:              "手工数据",
 		utils.DATA_SOURCE_LZ:                  "隆众",
 		utils.DATA_SOURCE_YS:                  "SMM",
-		utils.DATA_SOURCE_GL:                  "钢联",
+		utils.DATA_SOURCE_GL:                  "上海钢联",
 		utils.DATA_SOURCE_ZZ:                  "郑商所",
 		utils.DATA_SOURCE_DL:                  "大商所",
 		utils.DATA_SOURCE_SH:                  "上期所",
@@ -1829,7 +1829,7 @@ func EdbInfoAdd(source, subSource, classifyId int, edbCode, edbName, frequency,
 		utils.DATA_SOURCE_LT:                  "路透",
 		utils.DATA_SOURCE_COAL:                "中国煤炭市场网",
 		utils.DATA_SOURCE_GOOGLE_TRAVEL:       "our world in data",
-		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "钢联",
+		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "上海钢联",
 		utils.DATA_SOURCE_EIA_STEO:            "EIA STEO报告",
 		utils.DATA_SOURCE_COM_TRADE:           "UN",
 		utils.DATA_SOURCE_SCI:                 "SCI",
@@ -2470,7 +2470,7 @@ func determineDateRange(index, totalLength int, formulas []map[string]string) st
 }
 
 // GetEdbChartAdminList
-// @param source 来源 :1:手工数据指标 2:钢联化工数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
+// @param source 来源 :1:手工数据指标 2:上海钢联数据库 3:ETA指标库 4:ETA预测指标 5:图库 6:ETA表格
 func GetEdbChartAdminList(source int) (list []int, err error) {
 	switch source {
 	case 1: //手工数据指标
@@ -2479,7 +2479,7 @@ func GetEdbChartAdminList(source int) (list []int, err error) {
 			return
 		}
 
-	case 2: //钢联化工数据库
+	case 2: //上海钢联数据库
 		list, err = data_manage.GetMysteelChemicalIndexAdminList()
 		if err != nil {
 			return
@@ -2594,7 +2594,7 @@ func EdbInfoWsdAdd(item *data_manage.EdbInfo) (edbInfo *data_manage.EdbInfo, err
 		utils.DATA_SOURCE_MANUAL:              "手工数据",
 		utils.DATA_SOURCE_LZ:                  "隆众",
 		utils.DATA_SOURCE_YS:                  "SMM",
-		utils.DATA_SOURCE_GL:                  "钢联",
+		utils.DATA_SOURCE_GL:                  "上海钢联",
 		utils.DATA_SOURCE_ZZ:                  "郑商所",
 		utils.DATA_SOURCE_DL:                  "大商所",
 		utils.DATA_SOURCE_SH:                  "上期所",
@@ -2604,7 +2604,7 @@ func EdbInfoWsdAdd(item *data_manage.EdbInfo) (edbInfo *data_manage.EdbInfo, err
 		utils.DATA_SOURCE_LT:                  "路透",
 		utils.DATA_SOURCE_COAL:                "中国煤炭市场网",
 		utils.DATA_SOURCE_GOOGLE_TRAVEL:       "our world in data",
-		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "钢联",
+		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "上海钢联",
 		utils.DATA_SOURCE_EIA_STEO:            "EIA STEO报告",
 		utils.DATA_SOURCE_COM_TRADE:           "UN",
 		utils.DATA_SOURCE_SCI:                 "SCI",
@@ -2793,7 +2793,7 @@ func EdbInfoSmmApiAdd(item *data_manage.EdbInfo) (edbInfo *data_manage.EdbInfo,
 		utils.DATA_SOURCE_MANUAL:              "手工数据",
 		utils.DATA_SOURCE_LZ:                  "隆众",
 		utils.DATA_SOURCE_YS:                  "SMM",
-		utils.DATA_SOURCE_GL:                  "钢联",
+		utils.DATA_SOURCE_GL:                  "上海钢联",
 		utils.DATA_SOURCE_ZZ:                  "郑商所",
 		utils.DATA_SOURCE_DL:                  "大商所",
 		utils.DATA_SOURCE_SH:                  "上期所",
@@ -2803,7 +2803,7 @@ func EdbInfoSmmApiAdd(item *data_manage.EdbInfo) (edbInfo *data_manage.EdbInfo,
 		utils.DATA_SOURCE_LT:                  "路透",
 		utils.DATA_SOURCE_COAL:                "中国煤炭市场网",
 		utils.DATA_SOURCE_GOOGLE_TRAVEL:       "our world in data",
-		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "钢联",
+		utils.DATA_SOURCE_MYSTEEL_CHEMICAL:    "上海钢联",
 		utils.DATA_SOURCE_EIA_STEO:            "EIA STEO报告",
 		utils.DATA_SOURCE_COM_TRADE:           "UN",
 		utils.DATA_SOURCE_SCI:                 "SCI",
@@ -3009,7 +3009,7 @@ func GetEdbTerminalCodeBySource(source int, edbCode, stockCode string) (terminal
 // @datetime 2024-07-22 13:06:36
 // @param edbInfo *data_manage.EdbInfo
 func handleByAddEdbInfo(edbInfo *data_manage.EdbInfo) {
-	// 更新钢联化工状态为启用
+	// 更新上海钢联状态为启用
 	if edbInfo.Source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
 		// 启动钢联的刷新
 		_ = data_manage.UpdateMysteelChemicalRefreshStatus(edbInfo.EdbCode, 0)

+ 46 - 1
services/data/edb_info_relation.go

@@ -55,7 +55,7 @@ func saveEdbInfoRelation(edbInfoIds []int, objectId, objectType, objectSubType i
 		err = fmt.Errorf("查询计算指标信息失败,%s", e.Error())
 		return
 	}
-	// 只统计钢联化工和wind来源的指标
+	// 只统计上海钢联和wind来源的指标
 	for _, edbInfo := range edbInfoList {
 		/*if edbInfo.Source != utils.DATA_SOURCE_WIND && edbInfo.Source != utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
 			continue
@@ -474,6 +474,51 @@ func GetEdbRelationList(source, edbType int, classifyId, sysUserId, frequency, k
 	return
 }
 
+// GetEdbRelationList 获取指标引用列表
+func GetEdbRelationListByIds(edbIds []int, source int) (total int, list []*data_manage.BaseRelationEdbInfo, err error) {
+	var pars []interface{}
+	var condition string
+	if len(edbIds) == 0 {
+		return
+	}
+	condition += ` AND e.edb_info_id IN (` + utils.GetOrmInReplace(len(edbIds)) + `)`
+	pars = append(pars, edbIds)
+	list = make([]*data_manage.BaseRelationEdbInfo, 0)
+	// 关联表语句
+	var addFieldStr, joinTableStr string
+	// 供应商停用
+	if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+		joinTableStr = ` LEFT JOIN base_from_mysteel_chemical_index z ON e.edb_code = z.index_code `
+		addFieldStr = ` ,z.is_supplier_stop `
+	}
+	total, list, err = data_manage.GetEdbInfoRelationList(condition, pars, addFieldStr, joinTableStr, "", 0, len(edbIds))
+	return
+}
+
+// GetEdbRelationList 获取指标引用列表
+func GetEdbRelationListById(edbId int, source int) (item *data_manage.BaseRelationEdbInfo, err error) {
+	var pars []interface{}
+	var condition string
+	condition += ` AND e.edb_info_id = ?`
+	pars = append(pars, edbId)
+	list := make([]*data_manage.BaseRelationEdbInfo, 0)
+	// 关联表语句
+	var addFieldStr, joinTableStr string
+	// 供应商停用
+	if source == utils.DATA_SOURCE_MYSTEEL_CHEMICAL {
+		joinTableStr = ` LEFT JOIN base_from_mysteel_chemical_index z ON e.edb_code = z.index_code `
+		addFieldStr = ` ,z.is_supplier_stop `
+	}
+	_, list, err = data_manage.GetEdbInfoRelationList(condition, pars, addFieldStr, joinTableStr, "", 0, 1)
+	if err != nil {
+		return
+	}
+	if len(list) > 0 {
+		item = list[0]
+	}
+	return
+}
+
 // 查找当前计算指标的所有溯源指标
 func GetEdbListByEdbInfoId(edbInfoList []*data_manage.EdbInfo, needPredict bool) (edbMappingListMap map[int]*data_manage.EdbInfoCalculateMapping, edbInfoMappingRootIdsMap map[int][]int, err error) {
 	if len(edbInfoList) == 0 {

+ 1 - 1
services/data/excel/excel_info.go

@@ -757,7 +757,7 @@ func GetEdbSourceByEdbInfoIdList(edbInfoIdList []int) (sourceNameList, sourceNam
 	}
 
 	for source, sourceName := range sourceMap {
-		if utils.InArrayByInt([]int{utils.DATA_SOURCE_MANUAL, utils.DATA_SOURCE_MYSTEEL_CHEMICAL}, source) {
+		if utils.InArrayByInt([]int{utils.DATA_SOURCE_MANUAL}, source) {
 			continue
 		}
 		sourceNameList = append(sourceNameList, sourceName)

+ 15 - 15
services/data/mysteel_chemical.go

@@ -15,7 +15,7 @@ import (
 	"github.com/rdlucklib/rdluck_tools/http"
 )
 
-// AddMysteelChemicalClassify 添加钢联化工分类
+// AddMysteelChemicalClassify 添加上海钢联分类
 func AddMysteelChemicalClassify(classifyName string, parentId, level, sysUserId int, sysUserName, lange string) (classifyInfo *data_manage.BaseFromMysteelChemicalClassify, err error, errMsg string) {
 	// 校验分类名称相同的数量
 	{
@@ -59,7 +59,7 @@ func AddMysteelChemicalClassify(classifyName string, parentId, level, sysUserId
 	return
 }
 
-// EditMysteelChemicalClassify 编辑钢联化工分类
+// EditMysteelChemicalClassify 编辑上海钢联分类
 func EditMysteelChemicalClassify(classifyId int, classifyName, lang string, sysUser *system.Admin) (err error, errMsg string) {
 	item, err := data_manage.GetBaseFromMysteelChemicalClassifyById(classifyId)
 	if err != nil {
@@ -128,7 +128,7 @@ func EditMysteelChemicalClassify(classifyId int, classifyName, lang string, sysU
 	return
 }
 
-// MoveMysteelChemicalClassify 移动钢联化工分类
+// MoveMysteelChemicalClassify 移动上海钢联分类
 func MoveMysteelChemicalClassify(classifyId, parentClassifyId, prevClassifyId, nextClassifyId int, sysUser *system.Admin) (err error, errMsg string) {
 	//判断分类是否存在
 	classifyInfo, err := data_manage.GetBaseFromMysteelChemicalClassifyById(classifyId)
@@ -228,7 +228,7 @@ func MoveMysteelChemicalClassify(classifyId, parentClassifyId, prevClassifyId, n
 	return
 }
 
-// DelMysteelChemicalClassify 删除钢联化工分类
+// DelMysteelChemicalClassify 删除上海钢联分类
 func DelMysteelChemicalClassify(classifyId int, sysUser *system.Admin) (err error, errMsg string) {
 	//判断分类是否存在
 	classifyInfo, err := data_manage.GetBaseFromMysteelChemicalClassifyById(classifyId)
@@ -277,7 +277,7 @@ func DelMysteelChemicalClassify(classifyId int, sysUser *system.Admin) (err erro
 		indexCodeList = append(indexCodeList, v.IndexCode)
 	}
 
-	// 获取已经加入到EDB指标库的钢联化工指标
+	// 获取已经加入到EDB指标库的上海钢联指标
 	edbInfoList, err := data_manage.GetEdbInfoByEdbCodeList(utils.DATA_SOURCE_MYSTEEL_CHEMICAL, indexCodeList)
 	if err != nil {
 		errMsg = "删除失败"
@@ -304,7 +304,7 @@ func DelMysteelChemicalClassify(classifyId int, sysUser *system.Admin) (err erro
 	return
 }
 
-// BatchAddMysteelChemicalIndex 批量添加钢联化工指标
+// BatchAddMysteelChemicalIndex 批量添加上海钢联指标
 func BatchAddMysteelChemicalIndex(items []*data_manage.BaseFromMysteelChemicalIndex, lang string) (baseFromMysteelChemicalIndexs []*data_manage.BaseFromMysteelChemicalIndex, err error, errMsg string) {
 	indexCodeList := make([]string, 0)
 	for _, v := range items {
@@ -391,7 +391,7 @@ type MysteelChemicalIndexSource2EdbReq struct {
 	AdminRealName string
 }
 
-// MysteelChemicalIndexSource2Edb 新增钢联化工数据源到指标库
+// MysteelChemicalIndexSource2Edb 新增上海钢联数据源到指标库
 func MysteelChemicalIndexSource2Edb(req MysteelChemicalIndexSource2EdbReq, lang string) (edb *data_manage.EdbInfo, err error, errMsg string, skip bool) {
 	if req.EdbCode == "" {
 		err = fmt.Errorf("指标ID为空")
@@ -433,7 +433,7 @@ func MysteelChemicalIndexSource2Edb(req MysteelChemicalIndexSource2EdbReq, lang
 	return
 }
 
-// AddMysteelChemicalIndex 添加钢联化工指标
+// AddMysteelChemicalIndex 添加上海钢联指标
 func AddMysteelChemicalIndex(classifyId int, indexCode, updateWeek, updateTimeStr string, sysUserId int, sysUserName, lang string) (baseFromMysteelChemicalIndex *data_manage.BaseFromMysteelChemicalIndex, err error, errMsg string) {
 	baseFromMysteelChemicalIndex, err = data_manage.GetBaseFromMysteelChemicalIndexByCode(indexCode)
 	if err != nil && !utils.IsErrNoRow(err) {
@@ -493,7 +493,7 @@ func AddMysteelChemicalIndex(classifyId int, indexCode, updateWeek, updateTimeSt
 	return
 }
 
-// EditMysteelChemicalIndex 编辑钢联化工指标
+// EditMysteelChemicalIndex 编辑上海钢联指标
 func EditMysteelChemicalIndex(indexId, classifyId int, updateWeek, updateTimeStr string, sysUser *system.Admin) (baseFromMysteelChemicalIndex *data_manage.BaseFromMysteelChemicalIndex, err error, errMsg string) {
 	baseFromMysteelChemicalIndex, err = data_manage.GetBaseFromMysteelChemicalIndexByIndexId(indexId)
 	if err != nil {
@@ -542,7 +542,7 @@ func EditMysteelChemicalIndex(indexId, classifyId int, updateWeek, updateTimeStr
 	return
 }
 
-// DelMysteelChemical 删除钢联化工指标
+// DelMysteelChemical 删除上海钢联指标
 func DelMysteelChemical(indexId int, sysUser *system.Admin) (err error, errMsg string) {
 	baseFromMysteelChemicalIndex, err := data_manage.GetBaseFromMysteelChemicalIndexByIndexId(indexId)
 	if err != nil {
@@ -561,7 +561,7 @@ func DelMysteelChemical(indexId int, sysUser *system.Admin) (err error, errMsg s
 		return
 	}
 
-	// 获取已经加入到EDB指标库的钢联化工指标
+	// 获取已经加入到EDB指标库的上海钢联指标
 	edbInfo, err := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_MYSTEEL_CHEMICAL, baseFromMysteelChemicalIndex.IndexCode)
 	if err != nil && !utils.IsErrNoRow(err) {
 		errMsg = "删除失败"
@@ -585,7 +585,7 @@ func DelMysteelChemical(indexId int, sysUser *system.Admin) (err error, errMsg s
 	return
 }
 
-// MoveMysteelChemical 移动钢联化工指标
+// MoveMysteelChemical 移动上海钢联指标
 func MoveMysteelChemical(indexId, classifyId, prevIndexId, nextIndexId int, sysUser *system.Admin) (err error, errMsg string) {
 	//分类信息
 	baseFromMysteelChemicalIndex, err := data_manage.GetBaseFromMysteelChemicalIndexByIndexId(indexId)
@@ -704,7 +704,7 @@ func MoveMysteelChemical(indexId, classifyId, prevIndexId, nextIndexId int, sysU
 	return
 }
 
-// GetMysteelChemicalOpButton 获取钢联化工的操作权限
+// GetMysteelChemicalOpButton 获取上海钢联的操作权限
 func GetMysteelChemicalOpButton(sysUser *system.Admin, belongUserId int) (button data_manage.BaseFromMysteelChemicalClassifyItemsButton) {
 	// 统一跟随角色权限进行管理分类, 不对特定角色进行权限控制
 	button.AddButton = true
@@ -836,11 +836,11 @@ func RefreshMysteelChemicalData(edbCode string) {
 	var errMsg string
 	defer func() {
 		if err != nil {
-			go alarm_msg.SendAlarmMsg("根据钢联化工的code刷新指标数据失败提醒,Err"+err.Error(), 3)
+			go alarm_msg.SendAlarmMsg("根据上海钢联的code刷新指标数据失败提醒,Err"+err.Error(), 3)
 			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"导入手工数据后,根据手工指标code刷新手工指标数据失败提醒", "errmsg:"+err.Error(), utils.EmailSendToUsers)
 		}
 		if errMsg != "" {
-			go alarm_msg.SendAlarmMsg("根据钢联化工的code刷新指标数据失败提醒,errMsg"+errMsg, 3)
+			go alarm_msg.SendAlarmMsg("根据上海钢联的code刷新指标数据失败提醒,errMsg"+errMsg, 3)
 		}
 	}()
 

+ 37 - 30
services/data/predict_edb_info_rule.go

@@ -774,6 +774,8 @@ func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, configValue strin
 		tmpHistoryVal := decimal.NewFromFloat(0) //往期的差值总和
 		tmpHistoryValNum := 0                    // 往期差值计算的数量
 
+		//fmt.Println(`填充后的数据长度:`, len(handleDataMap))
+
 		tmpLenAllDataList := len(allDataList)
 		tmpK := tmpLenAllDataList - 1    //上1期数据的下标
 		lastDayData := allDataList[tmpK] // 上1期的数据
@@ -783,6 +785,7 @@ func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, configValue strin
 		if tmpErr != nil {
 			err = errors.New("获取上期日期转换失败:" + tmpErr.Error())
 		}
+		//fmt.Println("当前需要计算的日期:", currentDate.Format(utils.FormatDate))
 		for _, year := range yearList {
 			moveDay := moveDayMap[year] //需要移动的天数
 			var tmpHistoryCurrentVal, tmpHistoryLastVal float64
@@ -790,39 +793,43 @@ func GetChartPredictEdbInfoDataListByRuleSeason(edbInfoId int, configValue strin
 
 			//前几年当日的日期
 			tmpHistoryCurrentDate := currentDate.AddDate(year-currentDate.Year(), 0, -moveDay)
-			for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
-				tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, i)
-				if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
-					tmpHistoryCurrentVal = val
-					isFindHistoryCurrent = true
-					break
-				} else {
-					tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, -i)
-					if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
-						tmpHistoryCurrentVal = val
-						isFindHistoryCurrent = true
-						break
-					}
-				}
-			}
+			tmpHistoryCurrentVal, isFindHistoryCurrent = handleDataMap[tmpHistoryCurrentDate.Format(utils.FormatDate)]
+			//for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找(但是这个其实没有意义,因为这整个数据都通过插值法进行数据填充了, 2025-5-30 14:10:22)
+			//	tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, i)
+			//	if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
+			//		tmpHistoryCurrentVal = val
+			//		isFindHistoryCurrent = true
+			//		break
+			//	} else {
+			//		tmpDate := tmpHistoryCurrentDate.AddDate(0, 0, -i)
+			//		if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
+			//			tmpHistoryCurrentVal = val
+			//			isFindHistoryCurrent = true
+			//			break
+			//		}
+			//	}
+			//}
 
 			//前几年上一期的日期
 			tmpHistoryLastDate := lastDay.AddDate(year-lastDay.Year(), 0, -moveDay)
-			for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找
-				tmpDate := tmpHistoryLastDate.AddDate(0, 0, i)
-				if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
-					tmpHistoryLastVal = val
-					isFindHistoryLast = true
-					break
-				} else {
-					tmpDate := tmpHistoryLastDate.AddDate(0, 0, -i)
-					if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
-						tmpHistoryLastVal = val
-						isFindHistoryLast = true
-						break
-					}
-				}
-			}
+			tmpHistoryLastVal, isFindHistoryLast = handleDataMap[tmpHistoryLastDate.Format(utils.FormatDate)]
+			//for i := 0; i <= 35; i++ { // 前后35天找数据,找到最近的值,先向后面找,再往前面找(但是这个其实没有意义,因为这整个数据都通过插值法进行数据填充了, 2025-5-30 14:10:22)
+			//	tmpDate := tmpHistoryLastDate.AddDate(0, 0, i)
+			//	if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
+			//		tmpHistoryLastVal = val
+			//		isFindHistoryLast = true
+			//		break
+			//	} else {
+			//		tmpDate := tmpHistoryLastDate.AddDate(0, 0, -i)
+			//		if val, ok := handleDataMap[tmpDate.Format(utils.FormatDate)]; ok {
+			//			tmpHistoryLastVal = val
+			//			isFindHistoryLast = true
+			//			break
+			//		}
+			//	}
+			//}
+			
+			//fmt.Println("第一期日期:", tmpHistoryCurrentDate.Format(utils.FormatDate), ";第二期的日期:", tmpHistoryLastDate.Format(utils.FormatDate))
 
 			// 如果两个日期对应的数据都找到了,那么计算两期的差值
 			if isFindHistoryCurrent && isFindHistoryLast {

+ 43 - 4
services/elastic/elastic.go

@@ -10,9 +10,10 @@ import (
 	dataSourceModel "eta/eta_api/models/data_source"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/olivere/elastic/v7"
 	"strconv"
 	"strings"
+
+	"github.com/olivere/elastic/v7"
 )
 
 // indexName:索引名称
@@ -1939,7 +1940,7 @@ func EsAddOrEditDataSourceIndex(indexName, docId string, item *dataSourceModel.S
 	}()
 	client := utils.EsClient
 
-	resp, e := client.Index().Index(indexName).Id(docId).BodyJson(item).Refresh("true").Do(context.Background())
+	resp, e := client.Index().Index(indexName).Id(docId).BodyJson(item).Do(context.Background())
 	if e != nil {
 		err = fmt.Errorf("resp err, %v", e)
 		return
@@ -2138,7 +2139,7 @@ func SearchDataSourceIndex(indexName, keyword string, source, subSource int, cla
 }
 
 // CreateEsIndex 创建ES索引
-func CreateEsIndex(indexName, jsonMapping string) (err error) {
+func CreateEsIndex(indexName, jsonMapping string) (exists bool, err error) {
 	defer func() {
 		if err != nil {
 			tips := fmt.Sprintf("CreateEsIndex err: %v", err)
@@ -2148,11 +2149,12 @@ func CreateEsIndex(indexName, jsonMapping string) (err error) {
 	client := utils.EsClient
 
 	// 校验是否存在
-	exists, e := client.IndexExists(indexName).Do(context.Background())
+	exist, e := client.IndexExists(indexName).Do(context.Background())
 	if e != nil {
 		err = fmt.Errorf("check exists err: %v", e)
 		return
 	}
+	exists = exist
 	if exists {
 		fmt.Printf("索引已存在: %s, 跳过\n", indexName)
 		return
@@ -2167,3 +2169,40 @@ func CreateEsIndex(indexName, jsonMapping string) (err error) {
 	fmt.Printf("create index success: %s\n", createIndex.Index)
 	return
 }
+
+// ClearEsIndex 清空指定索引的所有数据
+func ClearEsIndex(indexName string) (err error) {
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("ClearEsIndex err: %v", err)
+			utils.FileLog.Info(tips)
+		}
+	}()
+
+	client := utils.EsClient
+
+	// 校验索引是否存在
+	exists, e := client.IndexExists(indexName).Do(context.Background())
+	if e != nil {
+		err = fmt.Errorf("check index exists err: %v", e)
+		return
+	}
+	if !exists {
+		err = fmt.Errorf("index does not exist: %s", indexName)
+		return
+	}
+
+	query := elastic.NewMatchAllQuery()
+	res, e := client.DeleteByQuery().
+		Index(indexName).
+		Query(query).
+		ProceedOnVersionConflict(). // 忽略版本冲突
+		Do(context.Background())
+	if e != nil {
+		err = fmt.Errorf("delete by query err: %v", e)
+		return
+	}
+
+	fmt.Printf("Cleared index: %s, Deleted documents count: %d\n", indexName, res.Deleted)
+	return
+}

+ 317 - 0
services/elastic/rag_eta_report_abstract.go

@@ -0,0 +1,317 @@
+package elastic
+
+import (
+	"context"
+	"encoding/json"
+	"eta/eta_api/models/rag"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/olivere/elastic/v7"
+	"strings"
+	"time"
+)
+
+// 摘要索引
+var EsRagEtaReportAbstractName = utils.EsRagEtaReportAbstractName
+
+type RagEtaReportAbstractItem struct {
+	RagEtaReportAbstractId int       `gorm:"primaryKey;column:rag_eta_report_abstract_id" description:"-"`
+	RagEtaReportId         int       `gorm:"column:rag_eta_report_id" description:"ETA报告id"`
+	Abstract               string    `gorm:"column:abstract;type:longtext;comment:摘要内容;" description:"摘要内容"`
+	QuestionId             int       `gorm:"column:question_id" description:"提示词Id"`
+	Version                int       `gorm:"column:version" description:"版本号"`
+	VectorKey              string    `gorm:"column:vector_key" description:"向量key标识"`
+	ModifyTime             time.Time `gorm:"column:modify_time" description:"modifyTime"`
+	CreateTime             time.Time `gorm:"column:create_time" description:"createTime"`
+	Title                  string    `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
+	TagIdList              []int     `description:"品种id列表"`
+	TagNameList            []string  `description:"品种名称列表"`
+}
+
+func (m *RagEtaReportAbstractItem) ToView() rag.RagEtaReportAbstractView {
+	var modifyTime, createTime string
+
+	if !m.CreateTime.IsZero() {
+		createTime = m.CreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.ModifyTime.IsZero() {
+		modifyTime = m.ModifyTime.Format(utils.FormatDateTime)
+	}
+
+	//tagId := 0
+	//if len(m.TagIdList) > 0 {
+	//	tagId = m.TagIdList[0]
+	//}
+	tagsName := ``
+	if len(m.TagNameList) > 0 {
+		tagsName = strings.Join(m.TagNameList, `,`)
+	}
+
+	return rag.RagEtaReportAbstractView{
+		RagEtaReportAbstractId: m.RagEtaReportAbstractId,
+		RagEtaReportId:         m.RagEtaReportId,
+		Abstract:               m.Abstract,
+		QuestionId:             m.QuestionId,
+		//QuestionContent:        m.,
+		Version: m.Version,
+		//Tags:                   m.Ta,
+		VectorKey:  m.VectorKey,
+		ModifyTime: modifyTime,
+		CreateTime: createTime,
+		Title:      m.Title,
+		TagsName:   tagsName,
+	}
+}
+
+func (m *RagEtaReportAbstractItem) ToViewList(list []*RagEtaReportAbstractItem) (wechatArticleViewList []rag.RagEtaReportAbstractView) {
+	wechatArticleViewList = make([]rag.RagEtaReportAbstractView, 0)
+
+	for _, v := range list {
+		wechatArticleViewList = append(wechatArticleViewList, v.ToView())
+	}
+	return
+}
+
+// RagEtaReportEsAddOrEdit
+// @Description: 新增/编辑微信文章
+// @author: Roc
+// @datetime 2025-03-13 10:24:05
+// @param docId string
+// @param item RagEtaReportAndPlatform
+// @return err error
+func RagEtaReportAbstractEsAddOrEdit(docId string, item RagEtaReportAbstractItem) (err error) {
+	if docId == "" {
+		return
+	}
+	if EsRagEtaReportAbstractName == `` {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("RagEtaReportEsAddOrEdit Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Index().Index(EsRagEtaReportAbstractName).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("RagEtaReportEsAddOrEdit", resp.Status, resp.Result)
+	}
+
+	return
+}
+
+// RagEtaReportEsDel
+// @Description: 删除微信文章
+// @author: Roc
+// @datetime 2025-03-13 10:23:55
+// @param docId string
+// @return err error
+func RagEtaReportAbstractEsDel(docId string) (err error) {
+	if docId == "" {
+		return
+	}
+	if EsRagEtaReportAbstractName == `` {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("EsDeleteEdbInfoData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Delete().Index(EsRagEtaReportAbstractName).Id(docId).Refresh(`true`).Do(context.Background())
+	if err != nil {
+		return
+	}
+	if resp.Status == 0 {
+		fmt.Println("删除成功")
+	} else {
+		fmt.Println("RagEtaReportEsDel", resp.Status, resp.Result)
+	}
+
+	return
+}
+
+// RagEtaReportAbstractEsSearch
+// @Description: 搜索
+// @author: Roc
+// @datetime 2025-03-13 19:54:54
+// @param keywordStr string
+// @param tagIdList []int
+// @param from int
+// @param size int
+// @param sortMap map[string]string
+// @return total int64
+// @return list []*RagEtaReportAbstractItem
+// @return err error
+func RagEtaReportAbstractEsSearch(keywordStr string, tagIdList []int, questionId, from, size int, sortMap map[string]string) (total int64, list []*RagEtaReportAbstractItem, err error) {
+	indexName := EsRagEtaReportAbstractName
+	list = make([]*RagEtaReportAbstractItem, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("RagEtaReportAbstractEsSearch Err:", err.Error())
+		}
+	}()
+
+	query := elastic.NewBoolQuery()
+
+	if len(tagIdList) > 0 {
+		termsList := make([]interface{}, 0)
+		for _, v := range tagIdList {
+			termsList = append(termsList, v)
+		}
+		query = query.Must(elastic.NewTermsQuery("TagIdList", termsList...))
+	}
+
+	// 提示词id
+	if questionId > 0 {
+		query = query.Must(elastic.NewTermsQuery("QuestionId", questionId))
+	}
+
+	// 名字匹配
+	if keywordStr != `` {
+		query = query.Must(elastic.NewMultiMatchQuery(keywordStr, "Abstract"))
+	}
+
+	// 排序
+	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 searchRagEtaReportAbstract(indexName, query, sortList, from, size)
+}
+
+// searchEdbInfoDataV2 查询es中的数据
+func searchRagEtaReportAbstract(indexName string, query elastic.Query, sortList []*elastic.FieldSort, from, size int) (total int64, list []*RagEtaReportAbstractItem, err error) {
+	total, err = searchRagEtaReportAbstractTotal(indexName, query)
+	if err != nil {
+		return
+	}
+
+	// 获取列表数据
+	list, err = searchRagEtaReportAbstractList(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 searchRagEtaReportAbstractTotal(indexName string, query elastic.Query) (total int64, err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("searchRagEtaReportAbstractTotal 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 searchRagEtaReportAbstractList(indexName string, query elastic.Query, sortList []*elastic.FieldSort, from, size int) (list []*RagEtaReportAbstractItem, err error) {
+	list = make([]*RagEtaReportAbstractItem, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("searchRagEtaReportAbstractList 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(RagEtaReportAbstractItem)
+				tmpErr = json.Unmarshal(itemJson, &item)
+				if tmpErr != nil {
+					fmt.Println("json.Unmarshal movieJson err:", tmpErr)
+					err = tmpErr
+					return
+				}
+				if len(v.Highlight["Content"]) > 0 {
+					item.Abstract = v.Highlight["Content"][0]
+				}
+				list = append(list, item)
+				searchMap[v.Id] = v.Id
+			}
+		}
+	}
+
+	return
+}

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