Browse Source

Merge branch 'master' into feature/yb11.1_rai_report

# Conflicts:
#	utils/constants.go
xyxie 1 month ago
parent
commit
7c0c97e56a
100 changed files with 11818 additions and 517 deletions
  1. 53 0
      cache/wechat_platform.go
  2. 20 0
      controllers/base_auth.go
  3. 1152 0
      controllers/data_manage/base_from_radish_research.go
  4. 518 0
      controllers/data_manage/base_from_radish_research_classify.go
  5. 9 1
      controllers/data_manage/ccf_data.go
  6. 358 133
      controllers/data_manage/chart_classify.go
  7. 60 0
      controllers/data_manage/chart_common.go
  8. 151 3
      controllers/data_manage/chart_info.go
  9. 1 1
      controllers/data_manage/correlation/correlation_chart_classify.go
  10. 43 5
      controllers/data_manage/edb_info.go
  11. 1 1
      controllers/data_manage/edb_info_refresh.go
  12. 6 1
      controllers/data_manage/excel/balance_table.go
  13. 6 0
      controllers/data_manage/excel/excel_info.go
  14. 1 1
      controllers/data_manage/future_good/future_good_chart_classify.go
  15. 1 1
      controllers/data_manage/line_equation/line_chart_classify.go
  16. 1 1
      controllers/data_manage/line_feature/classify.go
  17. 26 14
      controllers/data_manage/my_chart.go
  18. 55 12
      controllers/data_manage/mysteel_chemical_data.go
  19. 1045 0
      controllers/data_manage/purang_data.go
  20. 1 1
      controllers/data_manage/range_analysis/chart_classify.go
  21. 44 13
      controllers/data_manage/sci_hq_data.go
  22. 24 0
      controllers/data_source/data_source.go
  23. 93 4
      controllers/english_report/report.go
  24. 420 0
      controllers/llm/abstract.go
  25. 186 0
      controllers/llm/chat_ws_controller.go
  26. 51 0
      controllers/llm/kb_controller.go
  27. 41 0
      controllers/llm/llm_http/request.go
  28. 28 0
      controllers/llm/llm_http/response.go
  29. 305 0
      controllers/llm/promote_controller.go
  30. 418 0
      controllers/llm/question.go
  31. 334 0
      controllers/llm/user_chat_controller.go
  32. 804 0
      controllers/llm/wechat_platform.go
  33. 54 54
      controllers/ppt_english.go
  34. 59 59
      controllers/ppt_v2.go
  35. 6 0
      controllers/report.go
  36. 10 2
      controllers/report_approve/report_approve.go
  37. 17 2
      controllers/report_chapter.go
  38. 137 11
      controllers/report_v2.go
  39. 12 7
      controllers/smart_report/smart_report.go
  40. 85 14
      controllers/sys_group.go
  41. 5 1
      controllers/sys_role.go
  42. 75 12
      controllers/sys_team.go
  43. 1 1
      controllers/trade_analysis/warehouse_classify.go
  44. 1 0
      controllers/user_login.go
  45. 1 0
      go.mod
  46. 1 0
      go.sum
  47. 0 2
      main.go
  48. 24 2
      models/ai_predict_model/ai_predict_model_index.go
  49. 90 33
      models/business_conf.go
  50. 10 1
      models/cloud_disk_resource.go
  51. 14 12
      models/data_manage/base_from_ccf.go
  52. 292 0
      models/data_manage/base_from_purang.go
  53. 139 0
      models/data_manage/base_from_purang_classify.go
  54. 309 0
      models/data_manage/base_from_radish_research_classify.go
  55. 192 0
      models/data_manage/base_from_radish_research_data.go
  56. 531 0
      models/data_manage/base_from_radish_research_index.go
  57. 27 0
      models/data_manage/base_from_sci_hq_classify.go
  58. 10 4
      models/data_manage/base_from_sci_hq_index.go
  59. 88 10
      models/data_manage/chart_classify.go
  60. 81 0
      models/data_manage/chart_description.go
  61. 6 0
      models/data_manage/chart_edb_mapping.go
  62. 7 4
      models/data_manage/chart_info.go
  63. 1 1
      models/data_manage/edb_data_base.go
  64. 1 2
      models/data_manage/edb_info_relation.go
  65. 2 0
      models/data_manage/excel/excel_info.go
  66. 15 0
      models/data_manage/excel/excel_info_rule_mapping.go
  67. 3 0
      models/data_manage/excel/excel_sheet.go
  68. 48 0
      models/data_manage/forum_chart_edb_mapping.go
  69. 51 13
      models/data_manage/mysteel_chemical_index.go
  70. 4 4
      models/data_manage/request/sci_hq_data.go
  71. 20 0
      models/english_report.go
  72. 40 0
      models/llm/user_chat_record.go
  73. 73 0
      models/llm/user_llm_chat.go
  74. 1 1
      models/ppt_english/ppt_english_group_mapping.go
  75. 37 0
      models/rag/article_kb_mapping.go
  76. 68 0
      models/rag/promote_train_record.go
  77. 161 0
      models/rag/question.go
  78. 33 0
      models/rag/request/wechat_platform.go
  79. 11 0
      models/rag/response/abstract.go
  80. 11 0
      models/rag/response/question.go
  81. 30 0
      models/rag/response/wechat_platform.go
  82. 83 0
      models/rag/tag.go
  83. 307 0
      models/rag/wechat_article.go
  84. 255 0
      models/rag/wechat_article_abstract.go
  85. 66 0
      models/rag/wechat_article_chat_record.go
  86. 184 0
      models/rag/wechat_platform.go
  87. 100 0
      models/rag/wechat_platform_tag_mapping.go
  88. 88 0
      models/rag/wechat_platform_user_mapping.go
  89. 65 39
      models/report.go
  90. 2 0
      models/report_approve/report_approve.go
  91. 2 0
      models/smart_report/smart_report.go
  92. 4 2
      models/system/sys_admin.go
  93. 13 2
      models/system/sys_group.go
  94. 1 0
      models/system/sys_user.go
  95. 576 0
      routers/commentsRouter.go
  96. 17 0
      routers/router.go
  97. 49 0
      services/data/base_from_purang.go
  98. 611 0
      services/data/base_from_radish_research_classify.go
  99. 88 15
      services/data/base_from_sci_hq.go
  100. 157 15
      services/data/chart_classify.go

+ 53 - 0
cache/wechat_platform.go

@@ -0,0 +1,53 @@
+package cache
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+)
+
+type WechatArticleOp struct {
+	Source           string
+	WechatPlatformId int
+}
+
+// AddWechatArticleOpToCache
+// @Description: 将公众号文章操作加入缓存
+// @param wechatPlatformId
+// @param source
+// @return bool
+func AddWechatArticleOpToCache(wechatPlatformId int, source string) bool {
+	record := new(WechatArticleOp)
+	record.Source = source
+	record.WechatPlatformId = wechatPlatformId
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_WECHAT_PLATFORM_ARTICLE, record)
+
+		utils.FileLog.Info(fmt.Sprintf("将公众号文章操作 加入缓存 AddWechatArticleOpToCache LPush: 操作类型:%s,公众号id:%d", source, wechatPlatformId))
+		if err != nil {
+			fmt.Println("AddWechatArticleOpToCache LPush Err:" + err.Error())
+		}
+		return true
+	}
+	return false
+}
+
+// AddWechatArticleLlmOpToCache
+// @Description: 将公众号文章llm操作加入缓存
+// @param wechatPlatformId
+// @param source
+// @return bool
+func AddWechatArticleLlmOpToCache(wechatPlatformId int, source string) bool {
+	record := new(WechatArticleOp)
+	record.Source = source
+	record.WechatPlatformId = wechatPlatformId
+	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))
+		if err != nil {
+			fmt.Println("AddWechatArticleOpToCache LPush Err:" + err.Error())
+		}
+		return true
+	}
+	return false
+}

+ 20 - 0
controllers/base_auth.go

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

+ 1152 - 0
controllers/data_manage/base_from_radish_research.go

@@ -0,0 +1,1152 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	dataSourceModel "eta/eta_api/models/data_source"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/data"
+	"eta/eta_api/services/elastic"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// BaseFromRadishResearchController 萝卜投研
+type BaseFromRadishResearchController struct {
+	controllers.BaseAuthController
+}
+
+// IndexPageList
+// @Title 指标列表-分页
+// @Description 指标列表-分页
+// @Success 200 {object} data_manage.RadishResearchIndexPageListResp
+// @router /radish_research/index/page_list [get]
+func (this *BaseFromRadishResearchController) IndexPageList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var params data_manage.RadishResearchIndexListForm
+	if e := this.ParseForm(&params); e != nil {
+		br.Msg = "参数解析失败"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, err: %v", e)
+		return
+	}
+	resp := new(data_manage.RadishResearchIndexPageListResp)
+	resp.List = make([]*data_manage.BaseFromRadishResearchIndexItem, 0)
+	classifyId, _ := this.GetInt("ClassifyId", -1)
+
+	// 分页查询
+	var startSize int
+	if params.PageSize <= 0 {
+		params.PageSize = utils.PageSize20
+	}
+	if params.CurrentIndex <= 0 {
+		params.CurrentIndex = 1
+	}
+	startSize = utils.StartIndex(params.CurrentIndex, params.PageSize)
+
+	// 筛选项
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	var (
+		cond        string
+		pars        []interface{}
+		classifyIds []int
+	)
+	// 未分类
+	if classifyId == 0 {
+		cond += fmt.Sprintf(` AND %s = ?`, indexOb.Cols().ClassifyId)
+		pars = append(pars, classifyId)
+	}
+	// 包含所有子分类的指标
+	if classifyId > 0 {
+		classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+		classifies, e := classifyOb.GetItemsByCondition(fmt.Sprintf(" AND FIND_IN_SET(%d, %s)", classifyId, classifyOb.Cols().LevelPath), make([]interface{}, 0), []string{classifyOb.Cols().PrimaryId}, fmt.Sprintf("%s ASC, %s ASC", classifyOb.Cols().ParentId, classifyOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取子分类IDs失败, %v", e)
+			return
+		}
+		for _, v := range classifies {
+			if v.BaseFromRadishResearchClassifyId <= 0 {
+				continue
+			}
+			classifyIds = append(classifyIds, v.BaseFromRadishResearchClassifyId)
+		}
+		if len(classifyIds) > 0 {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().ClassifyId)
+			pars = append(pars, classifyIds)
+		}
+	}
+	// 分类多选
+	params.ClassifyIds = strings.TrimSpace(params.ClassifyIds)
+	if params.ClassifyIds != "" {
+		idsArr := strings.Split(params.ClassifyIds, ",")
+		for _, v := range idsArr {
+			id, _ := strconv.Atoi(v)
+			if id > 0 {
+				classifyIds = append(classifyIds, id)
+			}
+		}
+		if len(classifyIds) > 0 {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().ClassifyId)
+			pars = append(pars, classifyIds)
+		}
+	}
+	// 频度多选
+	params.Frequencies = strings.TrimSpace(params.Frequencies)
+	if params.Frequencies != "" {
+		freArr := strings.Split(params.Frequencies, ",")
+		if len(freArr) > 0 {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().Frequency)
+			pars = append(pars, freArr)
+		}
+	}
+	// 关键词
+	params.Keyword = strings.TrimSpace(params.Keyword)
+	if params.Keyword != "" {
+		kw := fmt.Sprint("%", params.Keyword, "%")
+		cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, indexOb.Cols().IndexName, indexOb.Cols().IndexCode)
+		pars = append(pars, kw, kw)
+	}
+	// 是否忽略已加入指标库的
+	if params.IgnoreEdbExist {
+		cond += fmt.Sprintf(` AND %s = 0`, indexOb.Cols().EdbExist)
+	}
+
+	// 列表合计
+	total, e := indexOb.GetCountByCondition(cond, pars)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标总数失败, %v", e)
+		return
+	}
+	if total <= 0 {
+		page := paging.GetPaging(params.CurrentIndex, params.PageSize, 0)
+		resp.Paging = page
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	items, e := indexOb.GetPageItemsByCondition(cond, pars, []string{}, "", startSize, params.PageSize)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标列表失败, %v", e)
+		return
+	}
+	for _, v := range items {
+		t := v.Format2Item()
+		resp.List = append(resp.List, t)
+	}
+
+	page := paging.GetPaging(params.CurrentIndex, params.PageSize, total)
+	resp.Paging = page
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// IndexDetail
+// @Title 指标详情
+// @Description 指标详情
+// @Param   IndexId  query  string  true  "指标ID"
+// @Success 200 {object} data_manage.BaseFromRadishResearchIndexDetail
+// @router /radish_research/index/detail [get]
+func (this *BaseFromRadishResearchController) IndexDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	indexId, _ := this.GetInt("IndexId")
+	if indexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", indexId)
+		return
+	}
+	resp := new(data_manage.BaseFromRadishResearchIndexDetail)
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	item, e := indexOb.GetItemById(indexId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if item != nil && item.BaseFromRadishResearchIndexId <= 0 {
+		br.Msg = "指标不存在, 请刷新页面"
+		return
+	}
+	resp.BaseFromRadishResearchIndexItem = item.Format2Item()
+	resp.DataList = make([]*data_manage.BaseFromRadishResearchDataItem, 0)
+
+	dataOb := new(data_manage.BaseFromRadishResearchData)
+	cond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
+	pars := make([]interface{}, 0)
+	pars = append(pars, item.IndexCode)
+	dataList, e := dataOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标数据失败, %v", e)
+		return
+	}
+	for _, v := range dataList {
+		resp.DataList = append(resp.DataList, v.Format2Item())
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// IndexEdit
+// @Title 编辑指标
+// @Description 编辑指标
+// @Success 200 {object} data_manage.RadishResearchIndexEditReq
+// @router /radish_research/index/edit [post]
+func (this *BaseFromRadishResearchController) IndexEdit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.RadishResearchIndexEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", req.IndexId)
+		return
+	}
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	item, e := indexOb.GetItemById(req.IndexId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if item != nil && item.BaseFromRadishResearchIndexId <= 0 {
+		br.Msg = "指标不存在, 请刷新页面"
+		return
+	}
+
+	// 更新指标(这里不多限制,允许移到未分类)
+	item.ClassifyId = req.ClassifyId
+	item.ModifyTime = time.Now().Local()
+	updateCols := []string{indexOb.Cols().ClassifyId, indexOb.Cols().ModifyTime}
+	if e = item.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新指标分类失败, %v", e)
+		return
+	}
+
+	// 更新ES
+	go func() {
+		indexItem := new(dataSourceModel.SearchDataSource)
+		indexItem.PrimaryId = item.BaseFromRadishResearchIndexId
+		indexItem.IndexCode = item.IndexCode
+		indexItem.IndexName = item.IndexName
+		indexItem.ClassifyId = item.ClassifyId
+		indexItem.Unit = item.Unit
+		indexItem.Frequency = item.Frequency
+		indexItem.StartDate = item.StartDate.Format(utils.FormatDate)
+		indexItem.EndDate = item.EndDate.Format(utils.FormatDate)
+		indexItem.LatestValue = fmt.Sprint(item.LatestValue)
+		indexItem.Source = utils.DATA_SOURCE_RADISH_RESEARCH
+		indexItem.SourceName = utils.DATA_SOURCE_NAME_RADISH_RESEARCH
+		indexItem.IsDeleted = 0
+		indexItem.CreateTime = item.CreateTime.Format(utils.FormatDateTime)
+		indexItem.ModifyTime = item.ModifyTime.Format(utils.FormatDateTime)
+
+		docId := fmt.Sprintf("%d-%d", utils.DATA_SOURCE_RADISH_RESEARCH, item.BaseFromRadishResearchIndexId)
+		if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+			utils.FileLog.Warning("RadishResearch-写入指标ES失败, %v", e)
+			return
+		}
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// IndexRemove
+// @Title 删除指标
+// @Description 删除指标
+// @Success 200 {object} data_manage.RadishResearchIndexRemoveReq
+// @router /radish_research/index/remove [post]
+func (this *BaseFromRadishResearchController) IndexRemove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.RadishResearchIndexRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if req.IndexId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", req.IndexId)
+		return
+	}
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	item, e := indexOb.GetItemById(req.IndexId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "指标不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if item != nil && item.BaseFromRadishResearchIndexId <= 0 {
+		br.Msg = "指标不存在, 请刷新页面"
+		return
+	}
+	if item.EdbExist == 1 {
+		br.Msg = "指标已被引用, 不允许删除"
+		return
+	}
+	if e = indexOb.RemoveIndexAndData(item.IndexCode); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("删除指标和数据失败, %v", e)
+		return
+	}
+
+	// 更新ES
+	go func() {
+		indexItem := new(dataSourceModel.SearchDataSource)
+		indexItem.PrimaryId = item.BaseFromRadishResearchIndexId
+		indexItem.IndexCode = item.IndexCode
+		indexItem.IndexName = item.IndexName
+		indexItem.ClassifyId = item.ClassifyId
+		indexItem.Unit = item.Unit
+		indexItem.Frequency = item.Frequency
+		indexItem.StartDate = item.StartDate.Format(utils.FormatDate)
+		indexItem.EndDate = item.EndDate.Format(utils.FormatDate)
+		indexItem.LatestValue = fmt.Sprint(item.LatestValue)
+		indexItem.Source = utils.DATA_SOURCE_RADISH_RESEARCH
+		indexItem.SourceName = utils.DATA_SOURCE_NAME_RADISH_RESEARCH
+		indexItem.IsDeleted = 1 // 标记已删除
+		indexItem.CreateTime = item.CreateTime.Format(utils.FormatDateTime)
+		indexItem.ModifyTime = item.ModifyTime.Format(utils.FormatDateTime)
+
+		docId := fmt.Sprintf("%d-%d", utils.DATA_SOURCE_RADISH_RESEARCH, item.BaseFromRadishResearchIndexId)
+		if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+			utils.FileLog.Warning("RadishResearch-写入指标ES失败, %v", e)
+			return
+		}
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// IndexExport
+// @Title 导出指标数据
+// @Description 导出指标数据
+// @Param   ClassifyId  query  int  false  "分类Id"
+// @Success 200  导出成功
+// @router /radish_research/index/export [get]
+func (this *BaseFromRadishResearchController) IndexExport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	classifyId, _ := this.GetInt("ClassifyId", -1)
+
+	var (
+		cond        string
+		pars        []interface{}
+		classifyIds []int
+	)
+	// 未分类
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	if classifyId == 0 {
+		cond += fmt.Sprintf(` AND %s = ?`, indexOb.Cols().ClassifyId)
+		pars = append(pars, classifyId)
+	}
+	// 包含所有子分类的指标
+	if classifyId > 0 {
+		classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+		classifies, e := classifyOb.GetItemsByCondition(fmt.Sprintf(" AND FIND_IN_SET(%d, %s)", classifyId, classifyOb.Cols().LevelPath), make([]interface{}, 0), []string{classifyOb.Cols().PrimaryId}, fmt.Sprintf("%s ASC, %s ASC", classifyOb.Cols().ParentId, classifyOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取子分类IDs失败, %v", e)
+			return
+		}
+		for _, v := range classifies {
+			if v.BaseFromRadishResearchClassifyId <= 0 {
+				continue
+			}
+			classifyIds = append(classifyIds, v.BaseFromRadishResearchClassifyId)
+		}
+		if len(classifyIds) > 0 {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().ClassifyId)
+			pars = append(pars, classifyIds)
+		}
+	}
+
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downFile := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+
+	// 获取指标数据
+	indexes, e := indexOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC, %s ASC", indexOb.Cols().Sort, indexOb.Cols().PrimaryId))
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标失败, %v", e)
+		return
+	}
+	if len(indexes) == 0 {
+		// 无数据返回空文件
+		if e := xlsxFile.Save(downFile); e != nil {
+			sheet, e := xlsxFile.AddSheet("无数据")
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("新增Sheet失败, %v", e)
+				return
+			}
+			rowSecName := sheet.AddRow()
+			celSecName := rowSecName.AddCell()
+			celSecName.SetValue("")
+			if e = xlsxFile.Save(downFile); e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("保存文件失败, %v", e)
+				return
+			}
+		}
+		fileName := fmt.Sprintf("%s%s%s", utils.DATA_SOURCE_NAME_RADISH_RESEARCH, time.Now().Format("06.01.02"), ".xlsx")
+		this.Ctx.Output.Download(downFile, fileName)
+		defer func() {
+			_ = os.Remove(downFile)
+		}()
+		return
+	}
+
+	// 划分指标频度
+	frequencyIndex := make(map[string][]*data_manage.BaseFromRadishResearchIndex)
+	frequencyIndexIds := make(map[string][]int)
+	for _, v := range indexes {
+		if frequencyIndex[v.Frequency] == nil {
+			frequencyIndex[v.Frequency] = make([]*data_manage.BaseFromRadishResearchIndex, 0)
+		}
+		if frequencyIndexIds[v.Frequency] == nil {
+			frequencyIndexIds[v.Frequency] = make([]int, 0)
+		}
+		frequencyIndexIds[v.Frequency] = append(frequencyIndexIds[v.Frequency], v.BaseFromRadishResearchIndexId)
+		frequencyIndex[v.Frequency] = append(frequencyIndex[v.Frequency], v)
+	}
+
+	frequencyArr := []string{"日度", "周度", "旬度", "月度", "季度", "半年度", "年度"}
+	//frequencyMap := map[string]string{
+	//	"日度":  "Daily",
+	//	"周度":  "Weekly",
+	//	"旬度":  "ten-day",
+	//	"月度":  "Monthly",
+	//	"季度":  "Quarterly",
+	//	"半年度": "Semi-annual",
+	//	"年度":  "Annual",
+	//}
+	dataOb := new(data_manage.BaseFromRadishResearchData)
+	for _, frequency := range frequencyArr {
+		// 获取对应频度指标
+		secNameList := frequencyIndex[frequency]
+		if len(secNameList) == 0 {
+			continue
+		}
+
+		//sheetName := fmt.Sprintf("%s(%s)", frequency, frequencyMap[frequency])
+		sheetNew, e := xlsxFile.AddSheet(frequency)
+		if e != nil {
+			utils.FileLog.Warning(fmt.Sprintf("萝卜投研导出-AddSheet err: %v", e))
+			continue
+		}
+		secNameRow := sheetNew.AddRow()
+		frequencyRow := sheetNew.AddRow()
+		unitRow := sheetNew.AddRow()
+		updateTimeRow := sheetNew.AddRow()
+
+		// 指标日期序列
+		indexIds := frequencyIndexIds[frequency]
+		dataTimeList, e := dataOb.GetDataTimeByIndexIds(indexIds)
+		if e != nil {
+			utils.FileLog.Warning(fmt.Sprintf("萝卜投研导出-GetDataTimeByIndexIds err: %v", e))
+			continue
+		}
+
+		// 添加excel左侧指标日期
+		setRowIndex := 4
+		for rk, dv := range dataTimeList {
+			rowIndex := setRowIndex + rk
+			row := sheetNew.Row(rowIndex)
+			displayDate, _ := time.Parse(utils.FormatDate, dv)
+			displayDateCell := row.AddCell()
+			style := new(xlsx.Style)
+			style.ApplyAlignment = true
+			style.Alignment.WrapText = true
+			displayDateCell.SetStyle(style)
+			displayDateCell.SetDate(displayDate)
+		}
+		for k, sv := range secNameList {
+			// 获取数据
+			dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
+			dataPars := make([]interface{}, 0)
+			dataPars = append(dataPars, sv.IndexCode)
+			dataList, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
+			if e != nil {
+				utils.FileLog.Warning(fmt.Sprintf("萝卜投研导出-GetIndexDataByCondition err: %v", e))
+				continue
+			}
+
+			if k == 0 {
+				secNameRow.AddCell().SetValue("指标名称")
+				frequencyRow.AddCell().SetValue("频度")
+				unitRow.AddCell().SetValue("单位")
+				updateTimeRow.AddCell().SetValue("更新时间")
+				minCol := k * 3
+				sheetNew.SetColWidth(minCol, minCol, 15)
+			}
+			if len(dataList) == 0 {
+				continue
+			}
+			secNameRow.AddCell().SetValue(sv.IndexName)
+			frequencyRow.AddCell().SetValue(sv.Frequency)
+			unitRow.AddCell().SetValue(sv.Unit)
+
+			updateTimeRow.AddCell().SetValue(sv.ModifyTime)
+			dataInfoMap := make(map[string]*data_manage.BaseFromRadishResearchData)
+			for _, v := range dataList {
+				dt := v.DataTime.Format(utils.FormatDate)
+				dataInfoMap[dt] = v
+			}
+
+			for rk, dtv := range dataTimeList {
+				rowIndex := setRowIndex + rk
+				row := sheetNew.Row(rowIndex)
+				displayDateCell := row.AddCell()
+				tmpData, ok := dataInfoMap[dtv]
+				if ok {
+					displayDateCell.SetValue(tmpData.Value)
+				}
+			}
+		}
+	}
+
+	// 保存文件出错返回空文件
+	if e := xlsxFile.Save(downFile); e != nil {
+		sheet, e := xlsxFile.AddSheet("无数据")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("新增Sheet失败, %v", e)
+			return
+		}
+		rowSecName := sheet.AddRow()
+		celSecName := rowSecName.AddCell()
+		celSecName.SetValue("")
+		if e = xlsxFile.Save(downFile); e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("保存文件失败, %v", e)
+			return
+		}
+	}
+	fileName := fmt.Sprintf("%s%s%s", utils.DATA_SOURCE_NAME_RADISH_RESEARCH, time.Now().Format("06.01.02"), ".xlsx")
+	this.Ctx.Output.Download(downFile, fileName)
+	defer func() {
+		_ = os.Remove(downFile)
+	}()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+
+}
+
+// IndexSelect
+// @Title 批量加入指标库-选择指标
+// @Description 批量加入指标库-选择指标
+// @Success 200 {object} data_manage.BaseFromRadishResearchIndexItem
+// @router /radish_research/index/select [post]
+func (this *BaseFromRadishResearchController) IndexSelect() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.RadishResearchIndexSelectReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	resp := make([]*data_manage.BaseFromRadishResearchIndexItem, 0)
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	var (
+		cond string
+		pars []interface{}
+	)
+	// 忽略已加入指标库的
+	cond += fmt.Sprintf(` AND %s = 0`, indexOb.Cols().EdbExist)
+	if len(req.ClassifyIds) > 0 {
+		cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().ClassifyId)
+		pars = append(pars, req.ClassifyIds)
+	}
+	if len(req.Frequencies) > 0 {
+		cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().Frequency)
+		pars = append(pars, req.Frequencies)
+	}
+	req.Keyword = strings.TrimSpace(req.Keyword)
+	if req.Keyword != "" {
+		kw := fmt.Sprint("%", req.Keyword, "%")
+		cond += fmt.Sprintf(` AND (%s LIKE ? OR %s LIKE ?)`, indexOb.Cols().IndexName, indexOb.Cols().IndexCode)
+		pars = append(pars, kw, kw)
+	}
+	// 列表全选-SelectAll-true: IndexCodes为排除的指标, SelectAll-false: IndexCodes为选择的指标
+	if len(req.IndexCodes) > 0 {
+		if req.SelectAll {
+			cond += fmt.Sprintf(` AND %s NOT IN ?`, indexOb.Cols().IndexCode)
+		} else {
+			cond += fmt.Sprintf(` AND %s IN ?`, indexOb.Cols().IndexCode)
+		}
+		pars = append(pars, req.IndexCodes)
+	}
+
+	items, e := indexOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取指标列表失败, %v", e)
+		return
+	}
+	if len(items) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	for _, v := range items {
+		t := v.Format2Item()
+		resp = append(resp, t)
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// EdbAdd
+// @Title 加入指标库
+// @Description 加入指标库
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success Ret=200 保存成功
+// @router /radish_research/edb/add [post]
+func (this *BaseFromRadishResearchController) EdbAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := fmt.Sprintf("CACHE_EDB_INFO_ADD_%d_%d", utils.DATA_SOURCE_RADISH_RESEARCH, sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试"
+		return
+	}
+	var req data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析失败, %v", e)
+		return
+	}
+	req.EdbCode = strings.TrimSpace(req.EdbCode)
+	req.EdbName = strings.TrimSpace(req.EdbName)
+	if req.EdbCode == "" {
+		br.Msg = "指标编码不能为空"
+		return
+	}
+	if req.EdbName == "" {
+		br.Msg = "指标名称不能为空"
+		return
+	}
+	if req.Frequency == "" {
+		br.Msg = "频率不能为空"
+		return
+	}
+	if req.Unit == "" {
+		br.Msg = "单位不能为空"
+		return
+	}
+	if req.ClassifyId <= 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	// 是否加入过指标库
+	exist, e := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_RADISH_RESEARCH, req.EdbCode)
+	if e != nil && !utils.IsErrNoRow(e) {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("校验是否加入过指标库失败, %v", e)
+		return
+	}
+	if exist != nil && exist.EdbInfoId > 0 {
+		br.Msg = "指标库已存在,请刷新页面"
+		return
+	}
+
+	// 指标入库
+	edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(utils.DATA_SOURCE_RADISH_RESEARCH, utils.DATA_SUB_SOURCE_EDB, req.ClassifyId, req.EdbCode, req.EdbName, req.Frequency, req.Unit, req.StartDate, req.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+	if err != nil {
+		br.Msg = "保存失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	// 刷新指标数据
+	refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+		return
+	}
+	if refreshRes != nil && refreshRes.Ret != 200 {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+		return
+	}
+
+	// 更新指标EdbExist
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	if e := indexOb.UpdateEdbExists(1, []string{req.EdbCode}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新数据源EdbExist失败, %v", e)
+		return
+	}
+
+	//新增操作日志
+	{
+		edbLog := new(data_manage.EdbInfoLog)
+		edbLog.EdbInfoId = edbInfo.EdbInfoId
+		edbLog.SourceName = edbInfo.SourceName
+		edbLog.Source = edbInfo.Source
+		edbLog.EdbCode = edbInfo.EdbCode
+		edbLog.EdbName = edbInfo.EdbName
+		edbLog.ClassifyId = edbInfo.ClassifyId
+		edbLog.SysUserId = sysUser.AdminId
+		edbLog.SysUserRealName = sysUser.RealName
+		edbLog.CreateTime = time.Now()
+		edbLog.Content = string(this.Ctx.Input.RequestBody)
+		edbLog.Status = "新增指标"
+		edbLog.Method = this.Ctx.Input.URI()
+		go data_manage.AddEdbInfoLog(edbLog)
+	}
+
+	// 更新es
+	go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+
+	// 试用平台更新用户累计新增指标数
+	if utils.BusinessCode == utils.BusinessCodeSandbox {
+		go func() {
+			adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+			if e != nil {
+				return
+			}
+			if adminItem != nil && adminItem.AdminId <= 0 {
+				return
+			}
+			if adminItem.DepartmentName != "ETA试用客户" {
+				return
+			}
+
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserIndexNum(r)
+		}()
+	}
+
+	resp := new(data_manage.AddEdbInfoResp)
+	resp.EdbInfoId = edbInfo.EdbInfoId
+	resp.UniqueCode = edbInfo.UniqueCode
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// EdbNameCheck
+// @Title 批量加入指标库-重名校验
+// @Description 批量加入指标库-重名校验
+// @Success 200 {object} data_manage.EdbNameCheckResult
+// @router /radish_research/edb/name_check [post]
+func (this *BaseFromRadishResearchController) EdbNameCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req []*data_manage.NameCheckEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	codeMaxT := 30
+	codeLen := len(req)
+	if codeLen > codeMaxT {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+
+	indexNames := make([]string, 0)
+	resp := make([]*data_manage.EdbNameCheckResult, 0)
+	nameCount := make(map[string]int)
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		nameCount[v.EdbName] += 1
+
+		indexNames = append(indexNames, v.EdbName)
+		resp = append(resp, &data_manage.EdbNameCheckResult{
+			EdbCode: v.EdbCode,
+			EdbName: v.EdbName,
+		})
+	}
+	// 本次提交的名称中也不允许重复
+	for _, v := range resp {
+		if nameCount[v.EdbName] > 1 {
+			v.Exist = true
+		}
+	}
+
+	// 重名校验
+	edbList, e := data_manage.GetEdbInfoByNameArr(indexNames, utils.EDB_INFO_TYPE)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取重名指标失败, Err: " + e.Error()
+		return
+	}
+	nameExists := make(map[string]bool)
+	for _, v := range edbList {
+		nameExists[v.EdbName] = true
+	}
+	if len(nameExists) > 0 {
+		for _, v := range resp {
+			v.Exist = nameExists[v.EdbName]
+		}
+	}
+
+	br.Data = resp
+	br.Msg = "校验成功"
+	br.Ret = 200
+	br.Success = true
+}
+
+// EdbMultiAdd
+// @Title 批量加入指标库
+// @Description 批量加入指标库
+// @Param	request	body data_manage.AddEdbInfoReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/edb/multi_add [post]
+func (this *BaseFromRadishResearchController) EdbMultiAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	deleteCache := true
+	cacheKey := fmt.Sprintf("CACHE_EDB_INFO_BATCH_ADD_%d_%d", utils.DATA_SOURCE_RADISH_RESEARCH, sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			_ = utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}
+	var req []*data_manage.AddEdbInfoReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + e.Error()
+		return
+	}
+	if len(req) == 0 {
+		br.Msg = "请选择指标"
+		return
+	}
+	if len(req) > 30 {
+		br.Msg = "批量添加指标数量不得超过30个"
+		return
+	}
+	for _, v := range req {
+		v.EdbCode = strings.TrimSpace(v.EdbCode)
+		if v.EdbCode == "" {
+			br.Msg = "指标ID不可为空"
+			return
+		}
+		v.EdbName = strings.TrimSpace(v.EdbName)
+		if v.EdbName == "" {
+			br.Msg = "请输入指标名称"
+			return
+		}
+		v.Frequency = strings.TrimSpace(v.Frequency)
+		if v.Frequency == "" {
+			br.Msg = "请选择频度"
+			return
+		}
+		v.Unit = strings.TrimSpace(v.Unit)
+		if v.Unit == "" {
+			br.Msg = "请输入单位"
+			return
+		}
+		if v.ClassifyId <= 0 {
+			br.Msg = "请选择分类"
+			return
+		}
+	}
+
+	// 限定同一时间最多批量新增30个指标
+	var successCodes []string
+	for _, v := range req {
+		// 是否加入过指标库
+		exist, e := data_manage.GetEdbInfoByEdbCode(utils.DATA_SOURCE_RADISH_RESEARCH, v.EdbCode)
+		if e != nil && !utils.IsErrNoRow(e) {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("校验是否加入过指标库失败, %v", e)
+			return
+		}
+		if exist != nil && exist.EdbInfoId > 0 {
+			// 加入过指标库这里直接忽略掉
+			continue
+		}
+
+		// 指标入库
+		edbInfo, err, errMsg, isSendEmail := data.EdbInfoAdd(utils.DATA_SOURCE_RADISH_RESEARCH, utils.DATA_SUB_SOURCE_EDB, v.ClassifyId, v.EdbCode, v.EdbName, v.Frequency, v.Unit, v.StartDate, v.EndDate, sysUser.AdminId, sysUser.RealName, this.Lang)
+		if err != nil {
+			br.Msg = "保存失败"
+			if errMsg != `` {
+				br.Msg = errMsg
+			}
+			br.ErrMsg = err.Error()
+			br.IsSendEmail = isSendEmail
+			return
+		}
+		successCodes = append(successCodes, v.EdbCode)
+
+		// 刷新指标数据
+		refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+			return
+		}
+		if refreshRes != nil && refreshRes.Ret != 200 {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+			return
+		}
+
+		// 试用平台更新用户累计新增指标数
+		if utils.BusinessCode == utils.BusinessCodeSandbox {
+			go func() {
+				adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+				if e != nil {
+					tips := fmt.Sprintf("试用平台更新用户累计新增指标数-获取用户失败, Err: " + e.Error())
+					utils.FileLog.Info(tips)
+					return
+				}
+				if adminItem.DepartmentName != "ETA试用客户" {
+					return
+				}
+				var ur etaTrialService.EtaTrialUserReq
+				ur.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.UpdateUserIndexNum(ur)
+			}()
+		}
+
+		// 新增操作日志
+		{
+			edbLog := new(data_manage.EdbInfoLog)
+			edbLog.EdbInfoId = edbInfo.EdbInfoId
+			edbLog.SourceName = edbInfo.SourceName
+			edbLog.Source = edbInfo.Source
+			edbLog.EdbCode = edbInfo.EdbCode
+			edbLog.EdbName = edbInfo.EdbName
+			edbLog.ClassifyId = edbInfo.ClassifyId
+			edbLog.SysUserId = sysUser.AdminId
+			edbLog.SysUserRealName = sysUser.RealName
+			edbLog.CreateTime = time.Now()
+			edbLog.Content = string(this.Ctx.Input.RequestBody)
+			edbLog.Status = "新增指标"
+			edbLog.Method = this.Ctx.Input.URI()
+			go data_manage.AddEdbInfoLog(edbLog)
+		}
+
+		// 更新es
+		go data.AddOrEditEdbInfoToEs(edbInfo.EdbInfoId)
+	}
+
+	// 更新指标EdbExist
+	if len(successCodes) > 0 {
+		indexOb := new(data_manage.BaseFromRadishResearchIndex)
+		if e := indexOb.UpdateEdbExists(1, successCodes); e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("更新数据源EdbExist失败, %v", e)
+			return
+		}
+	}
+
+	br.Msg = "操作成功"
+	br.Ret = 200
+	br.Success = true
+	br.IsAddLog = true
+}

+ 518 - 0
controllers/data_manage/base_from_radish_research_classify.go

@@ -0,0 +1,518 @@
+package data_manage
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage"
+	"eta/eta_api/services/data"
+	"eta/eta_api/utils"
+	"fmt"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// ClassifyList
+// @Title 分类列表-含指标
+// @Description 分类列表-含指标
+// @Param   ParentId  query  int  false  "父级ID"
+// @Success 200 {object} data_manage.RadishResearchSearchEdbResp
+// @router /radish_research/classify/list [get]
+func (this *BaseFromRadishResearchController) ClassifyList() {
+	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
+	}
+	parentId, _ := this.GetInt("ParentId", -1)
+
+	resp := make([]*data_manage.BaseFromRadishResearchClassifyListItem, 0)
+
+	// 默认查询未分类及一级分类
+	var classifyId int
+	if parentId == -1 {
+		resp = append(resp, &data_manage.BaseFromRadishResearchClassifyListItem{
+			ClassifyName: "未分类",
+			Level:        1,
+			Sort:         -100,
+			UniqueCode:   "unclassified",
+		})
+	}
+	// 查询未分类下的指标
+	if parentId == 0 {
+		classifyId = -1
+	}
+	if parentId > 0 {
+		classifyId = parentId
+	}
+
+	// 查询分类
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, classifyId)
+		list, e := classifyOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", classifyOb.Cols().Sort))
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取子分类失败, %v", e)
+			return
+		}
+		for _, v := range list {
+			resp = append(resp, &data_manage.BaseFromRadishResearchClassifyListItem{
+				ClassifyId:   v.BaseFromRadishResearchClassifyId,
+				ClassifyName: v.ClassifyName,
+				ParentId:     v.ParentId,
+				Level:        v.Level,
+				Sort:         v.Sort,
+				UniqueCode:   v.UniqueCode,
+			})
+		}
+	}
+
+	// 查询指标
+	if parentId > -1 {
+		indexOb := new(data_manage.BaseFromRadishResearchIndex)
+		{
+			cond := fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
+			pars := make([]interface{}, 0)
+			pars = append(pars, parentId)
+			list, e := indexOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", indexOb.Cols().Sort))
+			if e != nil {
+				br.Msg = "获取失败"
+				br.ErrMsg = fmt.Sprintf("获取分类下指标失败, %v", e)
+				return
+			}
+			for _, v := range list {
+				resp = append(resp, &data_manage.BaseFromRadishResearchClassifyListItem{
+					NodeType:   1,
+					IndexId:    v.BaseFromRadishResearchIndexId,
+					IndexCode:  v.IndexCode,
+					IndexName:  v.IndexName,
+					ParentId:   parentId,
+					Sort:       v.Sort,
+					UniqueCode: v.IndexCode,
+				})
+			}
+		}
+	}
+	sort.Slice(resp, func(i, j int) bool {
+		return resp[i].Sort < resp[j].Sort
+	})
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ClassifyTree
+// @Title 分类树
+// @Description 分类树
+// @Success 200 {object} data_manage.RadishResearchSearchEdbResp
+// @router /radish_research/classify/tree [get]
+func (this *BaseFromRadishResearchController) ClassifyTree() {
+	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
+	}
+
+	// 获取所有分类
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	list, e := classifyOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC, %s ASC", classifyOb.Cols().ParentId, classifyOb.Cols().Sort))
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = fmt.Sprintf("获取分类列表失败, %v", e)
+		return
+	}
+	items := make([]*data_manage.BaseFromRadishResearchClassifyItem, 0)
+	for _, v := range list {
+		items = append(items, v.Format2Item())
+	}
+	tree := data.GetRadishResearchClassifyTreeRecursive(items, 0)
+
+	// 未分类置顶
+	resp := make([]*data_manage.BaseFromRadishResearchClassifyItem, 0)
+	resp = append(resp, &data_manage.BaseFromRadishResearchClassifyItem{
+		ClassifyName: "未分类",
+		Level:        1,
+		Sort:         -100,
+		UniqueCode:   "unclassified",
+	})
+	resp = append(resp, tree...)
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ClassifyAdd
+// @Title 新增分类
+// @Description 新增分类
+// @Param	request	body data_manage.RadishResearchClassifyAddReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/add [post]
+func (this *BaseFromRadishResearchController) ClassifyAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.RadishResearchClassifyAddReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "请选择上级分类"
+		return
+	}
+	if req.Level > 6 {
+		br.Msg = "目前只支持6级目录"
+		return
+	}
+
+	// 校验分类名称
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	{
+		cond := fmt.Sprintf(" AND %s = ? AND %s = ?", classifyOb.Cols().ParentId, classifyOb.Cols().ClassifyName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ParentId, req.ClassifyName)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类名称重复数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在"
+			return
+		}
+	}
+
+	// 层级路径
+	var levelPath string
+	var rootId int
+	if req.ParentId > 0 {
+		parent, e := classifyOb.GetItemById(req.ParentId)
+		if e != nil {
+			br.Msg = "上级分类有误"
+			br.ErrMsg = fmt.Sprintf("获取上级分类失败, %v", e)
+			return
+		}
+		levelPath = parent.LevelPath
+		rootId = parent.RootId
+	}
+
+	sortMax, e := classifyOb.GetSortMax(req.ParentId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类最大排序失败, %v", e)
+		return
+	}
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	classifyOb.ParentId = req.ParentId
+	classifyOb.ClassifyName = req.ClassifyName
+	classifyOb.Level = req.Level + 1
+	classifyOb.Sort = sortMax + 1
+	classifyOb.SysUserId = sysUser.AdminId
+	classifyOb.SysUserRealName = sysUser.RealName
+	classifyOb.UniqueCode = utils.MD5(classifyOb.TableName() + "_" + timestamp)
+	classifyOb.CreateTime = time.Now().Local()
+	classifyOb.ModifyTime = time.Now().Local()
+	if e = classifyOb.Create(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("新增分类失败, %v", e)
+		return
+	}
+	if req.ParentId > 0 {
+		// 用英文逗号拼接方便查询
+		classifyOb.LevelPath = fmt.Sprintf("%s,%d", levelPath, classifyOb.BaseFromRadishResearchClassifyId)
+		classifyOb.RootId = rootId
+	} else {
+		classifyOb.LevelPath = fmt.Sprint(classifyOb.BaseFromRadishResearchClassifyId)
+		classifyOb.RootId = classifyOb.BaseFromRadishResearchClassifyId
+	}
+	if e = classifyOb.Update([]string{classifyOb.Cols().LevelPath, classifyOb.Cols().RootId}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新分类失败, %v", e)
+		return
+	}
+
+	br.Data = classifyOb.BaseFromRadishResearchClassifyId
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ClassifyEdit
+// @Title 编辑分类
+// @Description 编辑分类
+// @Param	request	body data_manage.RadishResearchClassifyEditReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/edit [post]
+func (this *BaseFromRadishResearchController) ClassifyEdit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.RadishResearchClassifyEditReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId < 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+	req.ClassifyName = strings.TrimSpace(req.ClassifyName)
+	if req.ClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		return
+	}
+
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	classifyItem, e := classifyOb.GetItemById(req.ClassifyId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Msg = "分类不存在, 请刷新页面"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+		return
+	}
+	if classifyItem != nil && classifyItem.BaseFromRadishResearchClassifyId <= 0 {
+		br.Msg = "分类不存在, 请刷新页面"
+		return
+	}
+
+	// 校验分类名称
+	{
+		cond := fmt.Sprintf(" AND %s <> ? AND %s = ?", classifyOb.Cols().PrimaryId, classifyOb.Cols().ClassifyName)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ClassifyId, req.ClassifyName)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类名称重复数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "分类名称已存在"
+			return
+		}
+	}
+	classifyItem.ClassifyName = req.ClassifyName
+	classifyItem.ModifyTime = time.Now().Local()
+	updateCols := []string{classifyOb.Cols().ClassifyName, classifyOb.Cols().ModifyTime}
+	if e = classifyItem.Update(updateCols); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("更新分类失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ClassifyRemove
+// @Title 删除分类
+// @Description 删除分类
+// @Param	request	body data_manage.RadishResearchClassifyRemoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/remove [post]
+func (this *BaseFromRadishResearchController) ClassifyRemove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.RadishResearchClassifyRemoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId < 0 {
+		br.Msg = "请选择分类"
+		return
+	}
+
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	classifyItem, e := classifyOb.GetItemById(req.ClassifyId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "操作成功"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
+		return
+	}
+	if classifyItem != nil && classifyItem.BaseFromRadishResearchClassifyId <= 0 {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	// 查询子分类
+	{
+		cond := fmt.Sprintf(" AND %s = ?", classifyOb.Cols().ParentId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ClassifyId)
+		count, e := classifyOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取子分类数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "该分类下含有子分类, 不允许删除"
+			return
+		}
+	}
+
+	// 查询指标
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+	{
+		cond := fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
+		pars := make([]interface{}, 0)
+		pars = append(pars, req.ClassifyId)
+		count, e := indexOb.GetCountByCondition(cond, pars)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("获取分类下指标数失败, %v", e)
+			return
+		}
+		if count > 0 {
+			br.Msg = "该分类下含有指标, 不允许删除"
+			return
+		}
+	}
+
+	if e := classifyItem.Remove(); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("删除分类失败, %v", e)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ClassifyMove
+// @Title 移动分类
+// @Description 移动分类
+// @Param	request	body data_manage.BaseFromRadishResearchClassifyMoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /radish_research/classify/move [post]
+func (this *BaseFromRadishResearchController) ClassifyMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req data_manage.BaseFromRadishResearchClassifyMoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数解析异常"
+		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
+		return
+	}
+	if req.ClassifyId <= 0 && req.ItemId <= 0 {
+		br.Msg = "请选择分类或指标"
+		return
+	}
+
+	err, errMsg := data.RadishResearchMoveClassify(req, sysUser)
+	if errMsg != `` {
+		br.Msg = errMsg
+		br.ErrMsg = errMsg
+		if err != nil {
+			br.ErrMsg = err.Error()
+		} else {
+			br.IsSendEmail = false
+		}
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 9 - 1
controllers/data_manage/ccf_data.go

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

+ 358 - 133
controllers/data_manage/chart_classify.go

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

+ 60 - 0
controllers/data_manage/chart_common.go

@@ -11,6 +11,7 @@ import (
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
+	"eta/eta_api/services"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/data/excel"
@@ -351,3 +352,62 @@ func getBalanceChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoVie
 
 	return
 }
+
+// GeneralChartToken
+// @Title 根据图表唯一code生成token
+// @Description 根据编码获取图表详情接口
+// @Param   UniqueCode   query   string  true       "图表/表格唯一编码"
+// @Param   Source   query   string  true       "来源,枚举值:chart、table"
+// @Success 200 {object} data_manage.ChartInfoDetailFromUniqueCodeResp
+// @router /chart_info/common/general_token [get]
+func (this *ChartInfoController) GeneralChartToken() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	uniqueCode := this.GetString("UniqueCode")
+	if uniqueCode == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,uniqueCode is empty"
+		return
+	}
+	source := this.GetString("Source", "chart")
+
+	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取配置失败,Err:" + err.Error()
+		return
+	}
+
+	var token string
+	if businessConf.ConfVal == `true` {
+		// 缓存key
+		sourceType := source
+		if source == `table` {
+			sourceType = source
+		}
+		token, err = services.GeneralChartToken(sourceType, uniqueCode)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败"
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = token
+
+	return
+}

+ 151 - 3
controllers/data_manage/chart_info.go

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

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

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

+ 43 - 5
controllers/data_manage/edb_info.go

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

+ 1 - 1
controllers/data_manage/edb_info_refresh.go

@@ -368,7 +368,7 @@ func (c *EdbInfoController) GetEdbRefreshDefaultConfig() {
 	}
 
 	// 非有色的来源,频度不能为空
-	if source != utils.DATA_SOURCE_YS && frequency == `` {
+	if source != utils.DATA_SOURCE_YS && source != utils.DATA_SOURCE_RADISH_RESEARCH && frequency == `` {
 		br.Msg = "频度不能为空"
 		br.IsSendEmail = false
 		return

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

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

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

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

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

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

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

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

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

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

+ 26 - 14
controllers/data_manage/my_chart.go

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

+ 55 - 12
controllers/data_manage/mysteel_chemical_data.go

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

+ 1045 - 0
controllers/data_manage/purang_data.go

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

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

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

+ 44 - 13
controllers/data_manage/sci_hq_data.go

@@ -264,7 +264,7 @@ func (this *SciHqDataController) MoveClassify() {
 		return
 	}
 
-	err, errMsg := data.MoveSciHqClassify(req.ClassifyId, req.ParentId, req.PrevClassifyId, req.NextClassifyId)
+	err, errMsg := data.MoveSciHqClassify(req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -333,12 +333,12 @@ func (this *SciHqDataController) BatchIndexList() {
 	}
 	if req.IsSelectAll {
 		if len(req.SelectedId) > 0 {
-			condition += ` AND base_from_sci_hq_index_id NOT IN (` + utils.GetOrmInReplace(len(req.SelectedId)) + `)`
+			condition += ` AND base_from_sci_hq_index_id NOT IN (?)`
 			pars = append(pars, req.SelectedId)
 		}
 	} else {
 		if len(req.SelectedId) > 0 {
-			condition += ` AND base_from_sci_hq_index_id IN (` + utils.GetOrmInReplace(len(req.SelectedId)) + `)`
+			condition += ` AND base_from_sci_hq_index_id IN (?)`
 			pars = append(pars, req.SelectedId)
 		}
 	}
@@ -353,11 +353,14 @@ func (this *SciHqDataController) BatchIndexList() {
 			br.ErrMsg = "获取分类失败,Err:" + err.Error()
 			return
 		}
-		if len(childClassify) > 0 {
-			condition += `AND classify_id IN (` + utils.GetOrmInReplace(len(childClassify)) + `)`
-			for _, child := range childClassify {
-				pars = append(pars, child.ClassifyId)
-			}
+		var childIds []int
+		for _, v := range childClassify {
+			childIds = append(childIds, v.ClassifyId)
+		}
+		if len(childIds) > 0 {
+			childIds = append(childIds, req.ClassifyId)
+			condition += `AND classify_id IN (?)`
+			pars = append(pars, childIds)
 		} else {
 			condition += ` AND classify_id=?`
 			pars = append(pars, req.ClassifyId)
@@ -757,6 +760,19 @@ func (this *SciHqDataController) AddEdbInfo() {
 		return
 	}
 
+	// 刷新指标数据
+	refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+		return
+	}
+	if refreshRes != nil && refreshRes.Ret != 200 {
+		br.Msg = "操作失败"
+		br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+		return
+	}
+
 	// 试用平台更新用户累计新增指标数
 	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
 	if e != nil {
@@ -1091,6 +1107,19 @@ func (this *SciHqDataController) BatchAdd() {
 			continue
 		}
 
+		// 刷新指标数据
+		refreshRes, e := data.RefreshEdbData(edbInfo.EdbInfoId, edbInfo.Source, edbInfo.SubSource, edbInfo.EdbCode, "")
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, %v", e)
+			return
+		}
+		if refreshRes != nil && refreshRes.Ret != 200 {
+			br.Msg = "操作失败"
+			br.ErrMsg = fmt.Sprintf("刷新指标数据失败, Ret: %d, Msg: %s, ErrMsg: %s", refreshRes.Ret, refreshRes.Msg, refreshRes.ErrMsg)
+			return
+		}
+
 		// 试用平台更新用户累计新增指标数
 		if utils.BusinessCode == utils.BusinessCodeSandbox {
 			go func() {
@@ -1417,11 +1446,13 @@ func (this *SciHqDataController) ExportSciHqList() {
 			br.ErrMsg = "获取分类失败,Err:" + err.Error()
 			return
 		}
-		if len(childClassify) > 0 {
-			condition += `AND classify_id IN (` + utils.GetOrmInReplace(len(childClassify)) + `)`
-			for _, child := range childClassify {
-				pars = append(pars, child.ClassifyId)
-			}
+		var childIds []int
+		for _, v := range childClassify {
+			childIds = append(childIds, v.ClassifyId)
+		}
+		if len(childIds) > 0 {
+			condition += `AND classify_id IN (?)`
+			pars = append(pars, childIds)
 		} else {
 			condition += ` AND classify_id=?`
 			pars = append(pars, classifyId)

+ 24 - 0
controllers/data_source/data_source.go

@@ -260,6 +260,21 @@ func (c *DataSourceController) SearchByEs() {
 	//	}
 	//}
 
+	// 萝卜投研-LevelPath用作前端定位
+	levelPathMap := make(map[int]string)
+	if source == utils.DATA_SOURCE_RADISH_RESEARCH {
+		classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+		classifies, e := classifyOb.GetItemsByCondition("", make([]interface{}, 0), []string{classifyOb.Cols().PrimaryId, classifyOb.Cols().LevelPath}, "")
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = fmt.Sprintf("获取萝卜投研分类失败, %v", e)
+			return
+		}
+		for _, v := range classifies {
+			levelPathMap[v.BaseFromRadishResearchClassifyId] = v.LevelPath
+		}
+	}
+
 	for _, v := range listMap {
 		classifyId, ok := v[classifyIdKey].(int)
 		if !ok {
@@ -271,6 +286,15 @@ func (c *DataSourceController) SearchByEs() {
 		v["StartDate"] = utils.GormDateStrToDateStr(startDate)
 		endDate := v["EndDate"].(string)
 		v["EndDate"] = utils.GormDateStrToDateStr(endDate)
+
+		if source == utils.DATA_SOURCE_RADISH_RESEARCH {
+			// 未分类
+			if classifyId == 0 {
+				v["ClassifyLevelPath"] = "0"
+				continue
+			}
+			v["ClassifyLevelPath"] = levelPathMap[classifyId]
+		}
 	}
 
 	page := paging.GetPaging(currentIndex, pageSize, total)

+ 93 - 4
controllers/english_report/report.go

@@ -66,6 +66,8 @@ func (this *EnglishReportController) Add() {
 
 	var contentSub string
 	if req.Content != "" {
+		req.Content = services.HandleReportContent(req.Content, "del", nil)
+
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
 			br.Msg = "存在非法标签"
@@ -226,6 +228,7 @@ func (this *EnglishReportController) Edit() {
 	var contentSub string
 	if req.Content != "" {
 		req.Content = services.HandleReportContentTable(int(req.ReportId), req.Content)
+		req.Content = services.HandleReportContent(req.Content, "del", nil)
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
 			br.Msg = "存在非法标签"
@@ -351,6 +354,17 @@ func (this *EnglishReportController) Detail() {
 	// 处理关联excel的表格id
 	item.Content = services.HandleReportContentTable(item.Id, item.Content)
 
+	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取配置失败,Err:" + err.Error()
+		return
+	}
+	if businessConf.ConfVal == `true` {
+		tokenMap := make(map[string]string)
+		item.Content = services.HandleReportContent(item.Content, "add", tokenMap)
+	}
+
 	classifyNameMap := make(map[int]*models.EnglishClassifyFullName)
 	if item.ClassifyIdSecond > 0 {
 		nameList, tErr := models.GetEnglishClassifyFullNameByIds([]int{item.ClassifyIdSecond})
@@ -736,8 +750,9 @@ func (this *EnglishReportController) PublishReport() {
 			}()
 
 			// 生成报告pdf和长图
-			if req.ReportUrl != "" {
-				go services.Report2pdfAndJpeg(req.ReportUrl, report.Id, 2)
+			pdfUrl := services.GetGeneralEnglishReportPdfUrl(report.Id, report.ReportCode)
+			if pdfUrl != "" {
+				go services.Report2pdfAndJpeg(pdfUrl, report.Id, 2)
 			}
 		} else {
 			// 从无审批切换为有审批, 状态重置
@@ -839,8 +854,9 @@ func (this *EnglishReportController) PrePublishReport() {
 	}
 
 	// 生成报告pdf和长图
-	if req.ReportUrl != "" {
-		go services.Report2pdfAndJpeg(req.ReportUrl, report.Id, 2)
+	pdfUrl := services.GetGeneralEnglishReportPdfUrl(report.Id, report.ReportCode)
+	if pdfUrl != "" {
+		go services.Report2pdfAndJpeg(pdfUrl, report.Id, 2)
 	}
 
 	br.Ret = 200
@@ -999,6 +1015,7 @@ func (this *EnglishReportController) SaveReportContent() {
 	}
 
 	if noChangeFlag != 1 {
+		req.Content = services.HandleReportContent(req.Content, "del", nil)
 		content := req.Content
 		if content == "" {
 			content = this.GetString("Content")
@@ -1080,6 +1097,18 @@ func (this *EnglishReportController) ClassifyIdDetail() {
 		item.Content = html.UnescapeString(item.Content)
 		item.ContentSub = html.UnescapeString(item.ContentSub)
 
+		businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取配置失败,Err:" + err.Error()
+			return
+		}
+
+		if businessConf.ConfVal == `true` {
+			tokenMap := make(map[string]string)
+			item.Content = services.HandleReportContent(item.Content, "add", tokenMap)
+		}
+
 		classifyNameMap := make(map[int]*models.EnglishClassifyFullName)
 		if item.ClassifyIdSecond > 0 {
 			nameList, tErr := models.GetEnglishClassifyFullNameByIds([]int{item.ClassifyIdSecond})
@@ -1502,3 +1531,63 @@ func (this *EnglishReportController) CancelApprove() {
 	br.Success = true
 	br.Msg = "操作成功"
 }
+
+// @Title 获取报告分享链接
+// @Description 获取报告分享链接
+// @Param   ReportId   query   int  true       "报告id"
+// @Success 200 {object} models.EnglishReportDetailView
+// @router /share_url [get]
+func (this *EnglishReportController) GetShareUrl() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		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
+	}*/
+	reportId, err := this.GetInt("ReportId")
+	if err != nil {
+		br.Msg = "获取参数失败!"
+		br.ErrMsg = "获取参数失败,Err:" + err.Error()
+		return
+	}
+	if reportId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+	item, err := models.GetEnglishReportById(reportId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	token, err := services.GetEnglishReportToken(reportId, item.ReportCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "报告已被删除"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Data = token
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 420 - 0
controllers/llm/abstract.go

@@ -0,0 +1,420 @@
+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"
+)
+
+// AbstractController
+// @Description: 摘要管理
+type AbstractController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 列表
+// @Description 列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /abstract/list [get]
+func (c *AbstractController) 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")
+	tagId, _ := c.GetInt("TagId")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	// 获取列表
+	total, viewList, err := getAbstractList(keyWord, tagId, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.AbstractListListResp{
+		List:   viewList,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+func getAbstractList(keyWord string, tagId 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)
+		}
+	}
+
+	return
+}
+
+// Del
+// @Title 删除摘要
+// @Description 删除摘要
+// @Param	request	body request.BeachOpAbstractReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /abstract/del [post]
+func (c *AbstractController) Del() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.BeachOpAbstractReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if len(req.WechatArticleAbstractIdList) <= 0 && !req.IsSelectAll {
+		br.Msg = "请选择摘要"
+		br.IsSendEmail = false
+		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)
+	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 = `删除成功`
+}
+
+// VectorDel
+// @Title 删除摘要向量库
+// @Description 删除摘要向量库
+// @Param	request	body request.BeachOpAbstractReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /abstract/vector/del [post]
+func (c *AbstractController) VectorDel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.BeachOpAbstractReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if len(req.WechatArticleAbstractIdList) <= 0 && !req.IsSelectAll {
+		br.Msg = "请选择摘要"
+		br.IsSendEmail = false
+		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
+	}
+
+	// 修改ES数据
+	for _, wechatArticleAbstractId := range wechatArticleAbstractIdList {
+		go services.AddOrEditEsWechatArticleAbstract(wechatArticleAbstractId)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `删除成功`
+}
+
+// AddVector
+// @Title 删除摘要向量库
+// @Description 删除摘要向量库
+// @Param	request	body request.BeachOpAbstractReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /abstract/vector/add [post]
+func (c *AbstractController) AddVector() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.BeachOpAbstractReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if len(req.WechatArticleAbstractIdList) <= 0 && !req.IsSelectAll {
+		br.Msg = "请选择摘要"
+		br.IsSendEmail = false
+		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)
+			}
+		}
+	}
+
+	for _, wechatArticleAbstractId := range wechatArticleAbstractIdList {
+		cache.AddWechatArticleLlmOpToCache(wechatArticleAbstractId, ``)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `添加向量库中,请稍后查看`
+}

+ 186 - 0
controllers/llm/chat_ws_controller.go

@@ -0,0 +1,186 @@
+package llm
+
+import (
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/llm/facade"
+	"eta/eta_api/utils"
+	"eta/eta_api/utils/ws"
+	"fmt"
+	"github.com/gorilla/websocket"
+	"net"
+	"net/http"
+	"strings"
+	"time"
+)
+
+type ChatWsController struct {
+	controllers.BaseAuthController
+}
+
+func (cc *ChatWsController) Prepare() {
+	method := cc.Ctx.Input.Method()
+	uri := cc.Ctx.Input.URI()
+	if method == "GET" {
+		authorization := cc.Ctx.Input.Header("authorization")
+		if authorization == "" {
+			authorization = cc.Ctx.Input.Header("Authorization")
+		}
+		if strings.Contains(authorization, ";") {
+			authorization = strings.Replace(authorization, ";", "$", 1)
+		}
+		if authorization == "" {
+			strArr := strings.Split(uri, "?")
+			for k, v := range strArr {
+				fmt.Println(k, v)
+			}
+			if len(strArr) > 1 {
+				authorization = strArr[1]
+				authorization = strings.Replace(authorization, "Authorization", "authorization", -1)
+			}
+		}
+		if authorization == "" {
+			utils.FileLog.Error("authorization为空,未授权")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		tokenStr := authorization
+		tokenArr := strings.Split(tokenStr, "=")
+		token := tokenArr[1]
+
+		session, err := system.GetSysSessionByToken(token)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				utils.FileLog.Error("authorization已过期")
+				cc.Ctx.ResponseWriter.WriteHeader(http.StatusUnauthorized)
+				return
+			}
+			utils.FileLog.Error("authorization查询用户信息失败")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
+			return
+		}
+		if session == nil {
+			utils.FileLog.Error("会话不存在")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
+			return
+		}
+		//校验token是否合法
+		// JWT校验Token和Account
+		account := utils.MD5(session.UserName)
+		if !utils.CheckToken(account, token) {
+			utils.FileLog.Error("authorization校验不合法")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		if time.Now().After(session.ExpiredTime) {
+			utils.FileLog.Error("authorization过期法")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		admin, err := system.GetSysUserById(session.SysUserId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				utils.FileLog.Error("权限不够")
+				cc.Ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
+				return
+			}
+			utils.FileLog.Error("获取用户信息失败")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
+			return
+		}
+		if admin == nil {
+			utils.FileLog.Error("权限不够")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
+			return
+		}
+		//如果不是启用状态
+		if admin.Enabled != 1 {
+			utils.FileLog.Error("用户被禁用")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
+			return
+		}
+
+		//接口权限校验
+		roleId := admin.RoleId
+		list, e := system.GetMenuButtonApisByRoleId(roleId)
+		if e != nil {
+			utils.FileLog.Error("接口权限查询出错", e)
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
+			return
+		}
+		var api string
+		for _, v := range list {
+			if v.Api != "" {
+				api += v.Api + "&"
+			}
+		}
+		api += "&" + models.BusinessConfMap["PublicApi"]
+		//处理uri请求,去除前缀和参数
+		api = strings.TrimRight(api, "&")
+		uri = strings.Replace(uri, "/adminapi", "", 1)
+		uris := strings.Split(uri, "?")
+		uri = uris[0]
+		//fmt.Println("uri:", uri)
+		apis := strings.Split(api, "&")
+		apiMap := make(map[string]bool, 0)
+		for _, s := range apis {
+			apiMap[s] = true
+		}
+		if !apiMap[uri] {
+			utils.FileLog.Error("用户无权访问")
+			cc.Ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
+			return
+		}
+		cc.SysUser = admin
+	} else {
+		utils.FileLog.Error("请求方法类型错误")
+		cc.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
+		return
+	}
+}
+
+// ChatConnect @Title 知识库问答创建对话连接
+// @Description 知识库问答创建对话连接
+// @Success 101 {object} response.ListResp
+// @router /chat/connect [get]
+func (cc *ChatWsController) ChatConnect() {
+	if !ws.Allow(cc.SysUser.AdminId, ws.CONNECT_LIMITER) {
+		utils.FileLog.Error("WebSocket连接太频繁,主动拒绝链接")
+		cc.Ctx.ResponseWriter.WriteHeader(http.StatusTooManyRequests)
+		return
+	}
+	wsCon, err := webSocketHandler(cc.Ctx.ResponseWriter, cc.Ctx.Request)
+	if err != nil {
+		utils.FileLog.Error("WebSocket连接失败:", err)
+		cc.Ctx.ResponseWriter.WriteHeader(http.StatusBadRequest)
+		return
+	}
+	facade.AddSession(cc.SysUser.AdminId, wsCon)
+}
+
+// upGrader 用于将HTTP连接升级为WebSocket连接
+var upGrader = websocket.Upgrader{
+	ReadBufferSize:  1024,
+	WriteBufferSize: 1024,
+	CheckOrigin: func(r *http.Request) bool {
+		return true
+	},
+}
+
+// WebSocketHandler 处理WebSocket连接
+func webSocketHandler(w http.ResponseWriter, r *http.Request) (conn *websocket.Conn, err error) {
+	conn, err = upGrader.Upgrade(w, r, nil)
+	if err != nil {
+		utils.FileLog.Error("升级协议失败:WebSocket:%s", err.Error())
+		return
+	}
+	// 获取底层 TCP 连接并设置保活
+	if tcpConn, ok := conn.NetConn().(*net.TCPConn); ok {
+		_ = tcpConn.SetKeepAlive(true)
+		_ = tcpConn.SetKeepAlivePeriod(ws.TcpTimeout)
+		utils.FileLog.Info("TCP KeepAlive 已启用")
+	}
+	_ = conn.SetReadDeadline(time.Now().Add(ws.ReadTimeout))
+	return
+}

+ 51 - 0
controllers/llm/kb_controller.go

@@ -0,0 +1,51 @@
+package llm
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/services/llm/facade"
+)
+
+type KbController struct {
+	controllers.BaseAuthController
+}
+
+// SearchDocs  @Title 搜索知识库文档
+// @Description 搜索知识库文档
+// @Success 101 {object} response.ListResp
+// @router /knowledge_base/searchDocs [post]
+func (kbctrl *KbController) SearchDocs() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		kbctrl.Data["json"] = br
+		kbctrl.ServeJSON()
+	}()
+	sysUser := kbctrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req facade.LLMKnowledgeSearch
+	err := json.Unmarshal(kbctrl.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	searchResp, err := facade.LLMKnowledgeBaseSearchDocs(req)
+	if err != nil {
+		br.Msg = "搜索知识库失败"
+		br.ErrMsg = "搜索知识库失败:" + err.Error()
+		return
+	}
+	br.Data = searchResp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 41 - 0
controllers/llm/llm_http/request.go

@@ -0,0 +1,41 @@
+package llm_http
+
+import "encoding/json"
+
+type LLMQuestionReq struct {
+	Question      string `description:"提问"`
+	KnowledgeBase string `description:"知识库"`
+	SessionId     string `description:"会话ID"`
+}
+
+type UserChatReq struct {
+	ChatId    int    `json:"ChatId"`
+	ChatTitle string `json:"ChatTitle" description:"会话名称"`
+}
+
+type UserChatRecordReq struct {
+	Id           int    `json:"Id"`
+	ChatId       int    `json:"ChatId"`
+	Content      string `json:"Content" description:"会话名称"`
+	ChatUserType string `json:"ChatUserType" description:"用户类型"`
+	SendTime     string `json:"SendTime" description:"发送时间"`
+}
+
+type GenerateContentReq struct {
+	WechatArticleId int    `json:"WechatArticleId" description:"公众号Id"`
+	Promote         string `json:"Promote" description:"提示词"`
+	LLMModel        string `json:"LLMModel"`
+}
+type SaveContentReq struct {
+	WechatArticleId int             `json:"WechatArticleId" description:"公众号Id"`
+	Title           string          `json:"Title" description:"标题"`
+	Llm             string          `json:"LLM"`
+	Promote         json.RawMessage `json:"Promote" description:"提示词"`
+	AigcContent     json.RawMessage `json:"AigcContent" description:"生成内容"`
+}
+type DeleteContentReq struct {
+	RecordId int
+}
+type ContentListReq struct {
+	WechatArticleId int
+}

+ 28 - 0
controllers/llm/llm_http/response.go

@@ -0,0 +1,28 @@
+package llm_http
+
+import "eta/eta_api/models/llm"
+
+type UserChatListResp struct {
+	TodayList     []llm.UserLlmChatListViewItem
+	YesterdayList []llm.UserLlmChatListViewItem
+	WeekList      []llm.UserLlmChatListViewItem
+}
+type UserChatResp struct {
+	ChatId    int
+	ChatTitle string
+	SendTime  string
+}
+type UserChatAddResp struct {
+	SendTime string
+}
+
+type AIGCResp struct {
+	Promote Content
+	Answer Content
+}
+
+type Content struct {
+	Role     string
+	Content  string
+	SendTime string
+}

+ 305 - 0
controllers/llm/promote_controller.go

@@ -0,0 +1,305 @@
+package llm
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/controllers/llm/llm_http"
+	"eta/eta_api/models"
+	"eta/eta_api/models/rag"
+	"eta/eta_api/services/llm/facade"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+type PromoteController struct {
+	controllers.BaseAuthController
+}
+
+// GenerateContent @Title 生成问答内容
+// @Description 生成问答内容
+// @Success 101 {object} response.ListResp
+// @router /promote/generate_content [post]
+func (pCtrl *PromoteController) GenerateContent() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		pCtrl.Data["json"] = br
+		pCtrl.ServeJSON()
+	}()
+	var gcReq llm_http.GenerateContentReq
+	err := json.Unmarshal(pCtrl.Ctx.Input.RequestBody, &gcReq)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := pCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if gcReq.Promote == "" {
+		br.Msg = "提示词不能为空"
+		br.ErrMsg = "提示词不能为空"
+		return
+	}
+	if gcReq.WechatArticleId <= 0 {
+		br.Msg = "公众号文章编号非法"
+		br.ErrMsg = "公众号文章编号非法"
+		return
+	}
+	if gcReq.LLMModel == "" {
+		br.Msg = "LLM模型不能为空"
+		br.ErrMsg = "LLM模型不能为空"
+		return
+	}
+	userSendTime := time.Now()
+	userContent := llm_http.Content{
+		Content:  gcReq.Promote,
+		Role:     "user",
+		SendTime: userSendTime.Format(utils.FormatDateTime),
+	}
+	article, err := rag.GetArticleById(gcReq.WechatArticleId)
+	if err != nil {
+		br.Msg = "获取公众号内容失败"
+		br.ErrMsg = "获取公众号内容失败,Err:" + err.Error()
+		return
+	}
+	if article.TextContent == "" {
+		br.Msg = "暂不支持纯文本以外的内容生成"
+		br.ErrMsg = "暂不支持纯文本以外的内容生成"
+		return
+	}
+	res, err := facade.AIGCBaseOnPromote(facade.AIGC{
+		Promote:   gcReq.Promote,
+		ArticleId: gcReq.WechatArticleId,
+		LLMModel:  gcReq.LLMModel,
+	})
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = "内容生成失败,Err:" + err.Error()
+		return
+	}
+	aiSendTime := time.Now()
+	aiContent := llm_http.Content{
+		Content:  res.Answer,
+		Role:     "assistant",
+		SendTime: aiSendTime.Format(utils.FormatDateTime),
+	}
+	br.Data = llm_http.AIGCResp{
+		Promote: userContent,
+		Answer:  aiContent,
+	}
+	llm := strings.ReplaceAll(gcReq.LLMModel, ":", "")
+	saveContentReq := rag.PromoteTrainRecord{
+		WechatArticleId: gcReq.WechatArticleId,
+		Title:           userContent.Content,
+		Llm:             llm,
+		AigcContent:     res.Answer,
+		AigcSendTime:    aiSendTime,
+		TemplatePromote: userContent.Content,
+		PromoteSendTime: userSendTime,
+		CreatedTime:     time.Now(),
+	}
+	err = saveContentReq.SaveContent()
+	if err != nil {
+		br.Msg = "保存内容失败"
+		br.ErrMsg = "保存内容失败,err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "内容生成成功"
+}
+
+// SavePromoteContent @Title 保存问答内容
+// @Description 生成问答内容
+// @Success 101 {object} response.ListResp
+// @router /promote/save_content [post]
+func (pCtrl *PromoteController) SavePromoteContent() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		pCtrl.Data["json"] = br
+		pCtrl.ServeJSON()
+	}()
+	var gcReq llm_http.SaveContentReq
+	err := json.Unmarshal(pCtrl.Ctx.Input.RequestBody, &gcReq)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := pCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if gcReq.Promote == nil {
+		br.Msg = "提示词内容不能为空"
+		br.ErrMsg = "提示词不能为空"
+		return
+	}
+	if gcReq.AigcContent == nil {
+		br.Msg = "回答内容不能为空"
+		br.ErrMsg = "提示词不能为空"
+		return
+	}
+	if gcReq.WechatArticleId <= 0 {
+		br.Msg = "公众号文章编号非法"
+		br.ErrMsg = "公众号文章编号非法"
+		return
+	}
+	var userContent, assistantContent llm_http.Content
+	parseErr := json.Unmarshal(gcReq.AigcContent, &assistantContent)
+	if parseErr != nil {
+		br.Msg = "内容参数解析异常!"
+		br.ErrMsg = "内容参数解析异,err" + parseErr.Error()
+		return
+	}
+	parseErr = json.Unmarshal(gcReq.Promote, &userContent)
+	if parseErr != nil {
+		br.Msg = "内容参数解析异常!"
+		br.ErrMsg = "内容参数解析异,err" + parseErr.Error()
+		return
+	}
+	var titile string
+	if gcReq.Title != "" {
+		titile = gcReq.Title
+	} else {
+		titile = userContent.Content
+	}
+	var userSendTime, assistantSendTime time.Time
+	if userContent.SendTime != "" {
+		userSendTime, parseErr = time.ParseInLocation(utils.FormatDateTime, userContent.SendTime, time.Local)
+		if parseErr != nil {
+			br.Msg = "用户发送时间解析异常!"
+			br.ErrMsg = "用户发送时间解析异常,err" + parseErr.Error()
+			return
+		}
+	} else {
+		br.Msg = "用户发送时间不能为空!"
+		br.ErrMsg = "用户发送时间不能为空"
+		return
+	}
+	if assistantContent.SendTime != "" {
+		assistantSendTime, parseErr = time.ParseInLocation(utils.FormatDateTime, assistantContent.SendTime, time.Local)
+		if parseErr != nil {
+			br.Msg = "AI生成时间解析异常!"
+			br.ErrMsg = "AI生成时间解析异常,err" + parseErr.Error()
+			return
+		}
+	} else {
+		br.Msg = "AI生成时间不能为空!"
+		br.ErrMsg = "AI生成时间不能为空"
+		return
+	}
+	llm := strings.ReplaceAll(gcReq.Llm, ":", "")
+	saveContentReq := rag.PromoteTrainRecord{
+		WechatArticleId: gcReq.WechatArticleId,
+		Title:           titile,
+		Llm:             llm,
+		AigcContent:     assistantContent.Content,
+		AigcSendTime:    assistantSendTime,
+		TemplatePromote: userContent.Content,
+		PromoteSendTime: userSendTime,
+		CreatedTime:     time.Now(),
+	}
+	err = saveContentReq.SaveContent()
+	if err != nil {
+		br.Msg = "保存内容失败"
+		br.ErrMsg = "保存内容失败,err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存内容成功"
+}
+
+// DeletePromoteContent @Title 删除问答内容
+// @Description 生成问答内容
+// @Success 101 {object} response.ListResp
+// @router /promote/delete_content [post]
+func (pCtrl *PromoteController) DeletePromoteContent() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		pCtrl.Data["json"] = br
+		pCtrl.ServeJSON()
+	}()
+	var gcReq llm_http.DeleteContentReq
+	err := json.Unmarshal(pCtrl.Ctx.Input.RequestBody, &gcReq)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := pCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	if gcReq.RecordId <= 0 {
+		br.Msg = "记录编号非法"
+		br.ErrMsg = "记录编号非法"
+		return
+	}
+
+	err = rag.DeleteContent(gcReq.RecordId)
+	if err != nil {
+		br.Msg = "删除内容失败"
+		br.ErrMsg = "删除内容失败,err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除内容成功"
+}
+
+// PromoteContentList @Title 删除问答内容
+// @Description 生成问答内容
+// @Success 101 {object} response.ListResp
+// @router /promote/content_list [get]
+func (pCtrl *PromoteController) PromoteContentList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		pCtrl.Data["json"] = br
+		pCtrl.ServeJSON()
+	}()
+	wechatArticleId, _ := pCtrl.GetInt("WechatArticleId")
+	sysUser := pCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	if wechatArticleId <= 0 {
+		br.Msg = "记录编号非法"
+		br.ErrMsg = "记录编号非法"
+		return
+	}
+
+	list, err := rag.GetRecordList(wechatArticleId)
+	if err != nil {
+		br.Msg = "查询列表失败"
+		br.ErrMsg = "查询列表失败,err:" + err.Error()
+		return
+	}
+	total := len(list)
+	for _, item := range list {
+		item.Title = fmt.Sprintf("%d.%s", total, item.Title)
+		total--
+	}
+	br.Data = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询列表成功"
+}

+ 418 - 0
controllers/llm/question.go

@@ -0,0 +1,418 @@
+package llm
+
+import (
+	"encoding/json"
+	"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"
+	"strings"
+	"time"
+)
+
+// QuestionController
+// @Description: 问题库管理
+type QuestionController struct {
+	controllers.BaseAuthController
+}
+
+// List
+// @Title 列表
+// @Description 列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /question/list [get]
+func (c *QuestionController) 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")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var total int
+	viewList := make([]rag.QuestionView, 0)
+
+	if keyWord == `` {
+		var condition string
+		var pars []interface{}
+
+		if keyWord != "" {
+			condition += fmt.Sprintf(` AND %s like ?`, rag.QuestionColumns.QuestionContent)
+			pars = append(pars, `%`+keyWord+`%`)
+		}
+		obj := new(rag.Question)
+		tmpTotal, list, err := obj.GetPageListByCondition(condition, pars, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = tmpTotal
+
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ListToViewList(list)
+		}
+	} else {
+		sortMap := map[string]string{
+			//"ArticleCreateTime": "desc",
+			//"WechatArticleId":   "desc",
+		}
+		tmpTotal, list, err := elastic.RagQuestionEsSearch(keyWord, startSize, pageSize, sortMap)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = int(tmpTotal)
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ToViewList(list)
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.QuestionListListResp{
+		List:   viewList,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// TitleList
+// @Title 标题列表
+// @Description 标题列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /question/title/list [get]
+func (c *QuestionController) TitleList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyWord := c.GetString("KeyWord")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var total int
+	viewList := make([]rag.QuestionView, 0)
+
+	if keyWord == `` {
+		var condition string
+		var pars []interface{}
+
+		if keyWord != "" {
+			condition += fmt.Sprintf(` AND %s like ?`, rag.QuestionColumns.QuestionContent)
+			pars = append(pars, `%`+keyWord+`%`)
+		}
+		obj := new(rag.Question)
+		tmpTotal, list, err := obj.GetTitlePageListByCondition(condition, pars, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = tmpTotal
+
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ListToViewList(list)
+		}
+	} else {
+		sortMap := map[string]string{
+			//"ArticleCreateTime": "desc",
+			//"WechatArticleId":   "desc",
+		}
+		tmpTotal, list, err := elastic.RagQuestionEsSearch(keyWord, startSize, pageSize, sortMap)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = int(tmpTotal)
+		if list != nil && len(list) > 0 {
+			viewList = list[0].ToViewList(list)
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.QuestionListListResp{
+		List:   viewList,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Detail
+// @Title 列表
+// @Description 列表
+// @Param   QuestionId   query   int  true       "问题id"
+// @Success 200 {object} []*rag.QuestionListListResp
+// @router /question/detail [get]
+func (c *QuestionController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	questionId, _ := c.GetInt("QuestionId")
+	if questionId <= 0 {
+		br.Msg = "问题id不能为空"
+		br.ErrMsg = "问题id不能为空"
+		return
+	}
+
+	obj := new(rag.Question)
+	questionItem, err := obj.GetByID(questionId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Data = questionItem.ToView()
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// Add
+// @Title 新增问题
+// @Description 新增问题
+// @Param	request	body request.AddQuestionReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /question/add [post]
+func (c *QuestionController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.AddQuestionReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.Content = strings.TrimSpace(req.Content)
+	if req.Content == "" {
+		br.Msg = "请输入问题"
+		br.IsSendEmail = false
+		return
+	}
+	//obj := rag.Question{}
+	//_, err = obj.GetByCondition(` AND question_content = ? `, []interface{}{req.Content})
+	//if err == nil {
+	//	br.Msg = "问题已入库,请不要重复添加"
+	//	br.IsSendEmail = false
+	//	return
+	//}
+
+	title := utils.GetFirstNChars(req.Content, 20)
+	item := &rag.Question{
+		QuestionId:      0,
+		QuestionTitle:   title,
+		QuestionContent: req.Content,
+		Sort:            0,
+		ModifyTime:      time.Now(),
+		CreateTime:      time.Now(),
+	}
+	err = item.Create()
+	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 = `添加成功`
+}
+
+// Edit
+// @Title 编辑问题
+// @Description 编辑问题
+// @Param	request	body request.EditQuestionReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /question/edit [post]
+func (c *QuestionController) Edit() {
+	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
+	}
+	req.Content = strings.TrimSpace(req.Content)
+	if req.Content == "" {
+		br.Msg = "请输入问题"
+		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
+	}
+	item.QuestionTitle = utils.GetFirstNChars(req.Content, 20)
+	item.QuestionContent = req.Content
+	item.ModifyTime = time.Now()
+	err = item.Update([]string{"question_title", "question_content", "modify_time"})
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,Err:" + err.Error()
+		return
+	}
+	// 新增/编辑ES数据
+	go services.AddOrEditEsRagQuestion(item.QuestionId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `添加成功`
+}
+
+// Del
+// @Title 删除问题
+// @Description 删除问题
+// @Param	request	body request.EditQuestionReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /question/del [post]
+func (c *QuestionController) Del() {
+	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
+	}
+	err = item.Del()
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// 删除ES数据
+	go services.DelEsRagQuestion(item.QuestionId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `删除成功`
+}
+
+//func init() {
+//	// 问题加到es
+//	{
+//		obj := rag.Question{}
+//		list, _ := obj.GetListByCondition(``, ` `, []interface{}{}, 0, 10000)
+//		total := len(list)
+//		for k, item := range list {
+//			fmt.Println(k, "/", total)
+//			services.AddOrEditEsRagQuestion(item.QuestionId)
+//		}
+//
+//		fmt.Println("结束了")
+//	}
+//}

+ 334 - 0
controllers/llm/user_chat_controller.go

@@ -0,0 +1,334 @@
+package llm
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/controllers/llm/llm_http"
+	"eta/eta_api/models"
+	"eta/eta_api/models/llm"
+	llmService "eta/eta_api/services/llm"
+	"eta/eta_api/utils"
+	"time"
+)
+
+type UserChatController struct {
+	controllers.BaseAuthController
+}
+
+// NewChat @Title 新建对话框
+// @Description 新建对话框
+// @Success 101 {object} response.ListResp
+// @router /chat/new_chat [post]
+func (ucCtrl *UserChatController) NewChat() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		ucCtrl.Data["json"] = br
+		ucCtrl.ServeJSON()
+	}()
+	var req llm_http.UserChatReq
+	err := json.Unmarshal(ucCtrl.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := ucCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if req.ChatTitle == "" {
+		req.ChatTitle = "新会话"
+	}
+	session := llm.UserLlmChat{
+		UserId:      sysUser.AdminId,
+		CreatedTime: time.Now(),
+		ChatTitle:   req.ChatTitle,
+	}
+	var chatResp = new(llm_http.UserChatResp)
+	chatResp.ChatTitle = req.ChatTitle
+	chatResp.ChatId, err = session.CreateChatSession()
+	chatResp.SendTime = time.Now().Format(utils.FormatDateTime)
+	if err != nil {
+		br.Msg = "创建失败"
+		br.ErrMsg = "创建失败,Err:" + err.Error()
+		return
+	}
+	_ = llmService.AddChatRecord(&llm.UserChatRecordRedis{
+		ChatId:       session.Id,
+		ChatUserType: "user",
+		Content:      req.ChatTitle,
+		SendTime:     chatResp.SendTime,
+	})
+	br.Data = chatResp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "创建成功"
+}
+
+// RenameChat @Title 新建对话框
+// @Description 新建对话框
+// @Success 101 {object} response.ListResp
+// @router /chat/rename_chat [post]
+func (ucCtrl *UserChatController) RenameChat() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		ucCtrl.Data["json"] = br
+		ucCtrl.ServeJSON()
+	}()
+	var req llm_http.UserChatReq
+	err := json.Unmarshal(ucCtrl.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := ucCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if req.ChatId <= 0 {
+		br.Msg = "非法的对话框Id"
+		br.ErrMsg = "非法的对话框Id"
+		return
+	}
+	if req.ChatTitle == "" {
+		br.Msg = "重命名不能为空"
+		br.ErrMsg = "重命名不能为空"
+		return
+	}
+	session := llm.UserLlmChat{
+		Id:         req.ChatId,
+		UpdateTime: time.Now(),
+		UserId:     sysUser.AdminId,
+		ChatTitle:  req.ChatTitle,
+	}
+	err = session.RenameChatSession()
+	if err != nil {
+		br.Msg = "重命名失败"
+		br.ErrMsg = "重命名失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "重命名成功"
+}
+
+// DeleteChat @Title 删除对话框
+// @Description 删除对话框
+// @Success 101 {object} response.ListResp
+// @router /chat/delete_chat [post]
+func (ucCtrl *UserChatController) DeleteChat() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		ucCtrl.Data["json"] = br
+		ucCtrl.ServeJSON()
+	}()
+	var req llm_http.UserChatReq
+	err := json.Unmarshal(ucCtrl.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := ucCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if req.ChatId <= 0 {
+		br.Msg = "非法的对话框Id"
+		br.ErrMsg = "非法的对话框Id"
+		return
+	}
+
+	session := llm.UserLlmChat{
+		Id:         req.ChatId,
+		UpdateTime: time.Now(),
+		IsDeleted:  1,
+	}
+	err = session.DeleteChatSession()
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+// GetUserChatList @Title 获取用户对话框列表
+// @Description  获取用户对话框列表
+// @Success 101 {object} response.ListResp
+// @router /chat/user_chat_list [get]
+func (ucCtrl *UserChatController) GetUserChatList() {
+	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
+	}
+	//周日是0,周六是6
+	weekDay := time.Now().Weekday()
+	offset := int(time.Monday - weekDay)
+	if offset > 0 {
+		offset -= 7
+	}
+	today := time.Now().Format(utils.FormatDate)
+	monDay := time.Now().AddDate(0, 0, offset).Format(utils.FormatDate)
+	yesterday := time.Now().AddDate(0, 0, -1).Format(utils.FormatDate)
+	chatList, err := llm.GetUserChatList(sysUser.AdminId, monDay, time.Now().Format(utils.FormatDate))
+	if err != nil {
+		br.Msg = "获取用户聊天列表失败"
+		br.ErrMsg = "获取用户聊天列表失败,Err:" + err.Error()
+		return
+	}
+	data := new(llm_http.UserChatListResp)
+	data.WeekList = make([]llm.UserLlmChatListViewItem, 0)
+	data.YesterdayList = make([]llm.UserLlmChatListViewItem, 0)
+	data.TodayList = make([]llm.UserLlmChatListViewItem, 0)
+	for _, v := range chatList {
+		list, _ := llmService.GetChatRecordsFromRedis(v.Id)
+		item := llm.CovertItemToView(v)
+		item.RecordCount = len(list)
+		if v.CreatedTime.Format(utils.FormatDate) == today {
+			data.TodayList = append(data.TodayList, item)
+		} else if v.CreatedTime.Format(utils.FormatDate) == yesterday {
+			data.YesterdayList = append(data.YesterdayList, item)
+		} else {
+			data.WeekList = append(data.WeekList, item)
+		}
+	}
+
+	br.Data = data
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取用户聊天列表成功"
+}
+
+// ChatRecordAdd @Title 保存聊天记录
+// @Description 保存聊天记录
+// @Success 101 {object} response.ListResp
+// @router /chat/chat_record_save [post]
+func (ucCtrl *UserChatController) ChatRecordAdd() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		ucCtrl.Data["json"] = br
+		ucCtrl.ServeJSON()
+	}()
+	var req llm_http.UserChatRecordReq
+	err := json.Unmarshal(ucCtrl.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	sysUser := ucCtrl.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	if req.ChatId <= 0 {
+		br.Msg = "非法的对话框Id"
+		br.ErrMsg = "非法的对话框Id"
+		return
+	}
+	if req.Content == "" {
+		br.Msg = "聊天记录不能为空"
+		br.ErrMsg = "聊天记录不能为空"
+		return
+	}
+	if req.Id < 0 {
+		br.Msg = "非法的Id"
+		br.ErrMsg = "非法的Id"
+		return
+	}
+	if req.ChatUserType != "user" && req.ChatUserType != "assistant" {
+		br.Msg = "非法的用户类型"
+		br.ErrMsg = "非法的用户类型,用户类型支持:user/assistant"
+		return
+	}
+	if req.SendTime == "" {
+		req.SendTime = time.Now().Format(utils.FormatDateTime)
+	} else {
+		_, err = time.Parse(utils.FormatDateTime, req.SendTime)
+		if err != nil {
+			br.Msg = "非法的发送时间"
+			br.ErrMsg = "非法的发送时间,Err:" + err.Error()
+			return
+		}
+	}
+	record := llm.UserChatRecordRedis{
+		ChatId:       req.ChatId,
+		ChatUserType: req.ChatUserType,
+		Content:      req.Content,
+		SendTime:     req.SendTime,
+	}
+	err = llmService.AddChatRecord(&record)
+	if err != nil {
+		br.Msg = "添加聊天记录失败"
+		br.ErrMsg = "添加聊天记录失败,Err:" + err.Error()
+		return
+	}
+
+	br.Data = llm_http.UserChatAddResp{
+		SendTime: record.SendTime,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "添加聊天记录成功"
+}
+
+// ChatRecordList @Title 获取聊天记录
+// @Description 获取聊天记录
+// @Success 101 {object} response.ListResp
+// @router /chat/chat_record_list [get]
+func (ucCtrl *UserChatController) ChatRecordList() {
+	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
+	}
+	chatId, _ := ucCtrl.GetInt("ChatId", 0)
+	if chatId <= 0 {
+		br.Msg = "非法的对话Id"
+		br.ErrMsg = "非法的对话Id"
+		return
+	}
+
+	list, err := llmService.GetChatRecordsFromRedis(chatId)
+	if err != nil {
+		br.Msg = "获取聊天记录失败"
+		br.ErrMsg = "获取聊天记录失败,Err:" + err.Error()
+		return
+	}
+	br.Data = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取聊天记录成功"
+}
+
+

+ 804 - 0
controllers/llm/wechat_platform.go

@@ -0,0 +1,804 @@
+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/models/system"
+	"eta/eta_api/services"
+	"eta/eta_api/services/elastic"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"html"
+	"strings"
+	"time"
+)
+
+// WechatPlatformController
+// @Description: 微信公众号管理
+type WechatPlatformController struct {
+	controllers.BaseAuthController
+}
+
+// TagList
+// @Title 获取ppt列表
+// @Description 获取ppt列表接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} models.TagListResp
+// @router /tag/list [get]
+func (c *WechatPlatformController) TagList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyWord := c.GetString("KeyWord")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += fmt.Sprintf(` AND %s like ?`, rag.WechatPlatformColumns.Nickname)
+		pars = append(pars, `%`+keyWord+`%`)
+	}
+
+	obj := new(rag.Tag)
+	total, list, err := obj.GetPageListByCondition(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := new(response.TagListResp)
+	resp.Paging = page
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// Add
+// @Title 新增公众号
+// @Description 新增公众号
+// @Param	request	body request.AddWechatPlatformReq true "type json string"
+// @Success 200 Ret=200 新增成功
+// @router /wechat_platform/add [post]
+func (c *WechatPlatformController) Add() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	var req request.AddWechatPlatformReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.Name = strings.TrimSpace(req.Name)
+	if req.Name == "" {
+		br.Msg = "请输入公众号名称"
+		br.IsSendEmail = false
+		return
+	}
+	req.Link = strings.TrimSpace(req.Link)
+	if req.Link == "" {
+		br.Msg = "请输入文章链接"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 名称校验
+	{
+		var condition string
+		var pars []interface{}
+		condition += fmt.Sprintf(` AND %s = ?`, rag.WechatPlatformColumns.Nickname)
+		pars = append(pars, req.Name)
+		obj := new(rag.WechatPlatform)
+		item, err := obj.GetByCondition(condition, pars)
+		if err != nil && !utils.IsErrNoRow(err) {
+			br.Msg = "公众号信息获取失败"
+			br.ErrMsg = "公众号信息获取失败,Err:" + err.Error()
+			return
+		}
+
+		if item.WechatPlatformId > 0 {
+			br.Msg = "公众号名称重复"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	// 链接校验
+	{
+		var condition string
+		var pars []interface{}
+		condition += fmt.Sprintf(` AND %s = ?`, rag.WechatPlatformColumns.ArticleLink)
+		pars = append(pars, req.Link)
+		obj := new(rag.WechatPlatform)
+		item, err := obj.GetByCondition(condition, pars)
+		if err != nil && !utils.IsErrNoRow(err) {
+			br.Msg = "公众号信息获取失败"
+			br.ErrMsg = "公众号信息获取失败,Err:" + err.Error()
+			return
+		}
+
+		if item.WechatPlatformId > 0 {
+			br.Msg = "公众号链接重复"
+			br.IsSendEmail = false
+			return
+		}
+	}
+
+	item := &rag.WechatPlatform{
+		WechatPlatformId: 0,
+		FakeId:           "",
+		Nickname:         req.Name,
+		Alias:            "",
+		RoundHeadImg:     "",
+		ServiceType:      0,
+		Signature:        "",
+		Verified:         0,
+		ArticleLink:      req.Link,
+		Enabled:          0,
+		SysUserId:        c.SysUser.AdminId,
+		ModifyTime:       time.Now(),
+		CreateTime:       time.Now(),
+	}
+	err = item.Add(req.TagIdList)
+	if err != nil {
+		br.Msg = "添加失败"
+		br.ErrMsg = "添加失败,Err:" + err.Error()
+		return
+	}
+
+	// 异步新增公众号
+	go cache.AddWechatArticleOpToCache(item.WechatPlatformId, `add`)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = `添加成功`
+}
+
+// FollowList
+// @Title 我关注的接口
+// @Description 我关注的接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} []*rag.WechatPlatform
+// @router /wechat_platform/list/follow [get]
+func (c *WechatPlatformController) FollowList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyWord := c.GetString("KeyWord")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += fmt.Sprintf(` AND %s like ?`, rag.WechatPlatformColumns.Nickname)
+		pars = append(pars, `%`+keyWord+`%`)
+	}
+
+	condition += fmt.Sprintf(` AND b.%s = ?`, rag.WechatPlatformUserMappingColumns.SysUserID)
+	pars = append(pars, c.SysUser.AdminId)
+
+	obj := new(rag.WechatPlatformUserMapping)
+	list, err := obj.GetListByCondition(condition, pars, startSize, pageSize)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = list
+}
+
+// PublicList
+// @Title 公共列表
+// @Description 公共列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} models.WechatPlatformListResp
+// @router /wechat_platform/list/public [get]
+func (c *WechatPlatformController) PublicList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	keyWord := c.GetString("KeyWord")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = utils.StartIndex(currentIndex, pageSize)
+
+	var condition string
+	var pars []interface{}
+
+	if keyWord != "" {
+		condition += fmt.Sprintf(` AND %s like ?`, rag.WechatPlatformColumns.Nickname)
+		pars = append(pars, `%`+keyWord+`%`)
+	}
+
+	obj := new(rag.WechatPlatformUserMapping)
+	list, err := obj.GetListByCondition(condition, pars, startSize, 100000)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	resp := make([]response.WechatPlatformPublicListResp, 0)
+
+	if list != nil && len(list) > 0 {
+		userIdList := make([]int, 0)
+		uerIdMap := make(map[int]bool)
+		userFollowIndexMap := make(map[int]int)
+		for _, v := range list {
+			if _, ok := uerIdMap[v.FollowUserId]; !ok {
+				userIdList = append(userIdList, v.FollowUserId)
+				uerIdMap[v.FollowUserId] = true
+			}
+
+			index, ok := userFollowIndexMap[v.FollowUserId]
+			if !ok {
+				userFollowIndexMap[v.FollowUserId] = len(resp)
+
+				resp = append(resp, response.WechatPlatformPublicListResp{
+					UserId: v.FollowUserId,
+					List:   []*rag.UserFollowWechatPlatform{v},
+				})
+			} else {
+				resp[index].List = append(resp[index].List, v)
+			}
+		}
+
+		userList, err := system.GetAdminListByIdList(userIdList)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		userNameMap := make(map[int]*system.Admin)
+		for _, v := range userList {
+			userNameMap[v.AdminId] = v
+		}
+
+		for k, v := range resp {
+			userInfo, ok := userNameMap[v.UserId]
+			if !ok {
+				continue
+			}
+			resp[k].Name = userInfo.RealName + `关注`
+		}
+
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// PublicList
+// @Title 公共列表
+// @Description 公共列表
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} models.WechatPlatformListResp
+// @router /wechat_platform/op [post]
+func (c *WechatPlatformController) Op() {
+	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
+	}
+
+	var req request.OpWechatPlatformReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.WechatPlatformId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	if req.Status < 0 || req.Status > 1 {
+		br.Msg = "参数错误"
+		return
+	}
+	obj := rag.WechatPlatform{}
+	wechatPlatform, err := obj.GetById(req.WechatPlatformId)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "公众号不存在"
+			br.IsSendEmail = false
+		}
+		return
+	}
+	wechatPlatform.Enabled = req.Status
+	wechatPlatform.ModifyTime = time.Now()
+	err = wechatPlatform.Update([]string{"enabled", `modify_time`})
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,Err:" + err.Error()
+		return
+	}
+
+	// 异步处理公众号下面的文章
+	go services.AddOrEditEsWechatPlatformId(wechatPlatform.WechatPlatformId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "修改成功"
+}
+
+// Refresh
+// @Title 公共列表
+// @Description 公共列表
+// @Success 200 {object} models.WechatPlatformListResp
+// @router /wechat_platform/refresh [post]
+func (c *WechatPlatformController) Refresh() {
+	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
+	}
+
+	var req request.RefreshWechatPlatformReq
+	err := json.Unmarshal(c.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.WechatPlatformId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	obj := rag.WechatPlatform{}
+	wechatPlatform, err := obj.GetById(req.WechatPlatformId)
+	if err != nil {
+		br.Msg = "修改失败"
+		br.ErrMsg = "修改失败,Err:" + err.Error()
+		if utils.IsErrNoRow(err) {
+			br.Msg = "公众号不存在"
+			br.IsSendEmail = false
+		}
+		return
+	}
+	if wechatPlatform.FakeId != `` {
+		br.Msg = "公众号已添加成功"
+		br.ErrMsg = "公众号已添加成功"
+		br.IsSendEmail = false
+		return
+	}
+
+	go cache.AddWechatArticleOpToCache(wechatPlatform.WechatPlatformId, `add`)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "刷新成功"
+}
+
+// ArticleList
+// @Title 我关注的接口
+// @Description 我关注的接口
+// @Param   PageSize   query   int  true       "每页数据条数"
+// @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   WechatPlatformId   query   int  true       "微信公众号id"
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} []*rag.WechatPlatform
+// @router /wechat_platform/article/list [get]
+func (c *WechatPlatformController) ArticleList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	pageSize, _ := c.GetInt("PageSize")
+	currentIndex, _ := c.GetInt("CurrentIndex")
+	wechatPlatformId, _ := c.GetInt("WechatPlatformId")
+	keyWord := c.GetString("KeyWord")
+
+	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
+	viewList := make([]rag.WechatArticleView, 0)
+
+	if keyWord == `` {
+		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)
+
+		obj := new(rag.WechatArticle)
+		tmpTotal, list, err := obj.GetPageListByPlatformCondition(condition, pars, startSize, pageSize)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		total = tmpTotal
+
+		if list != nil && len(list) > 0 {
+			viewList = obj.ArticleAndPlatformListToViewList(list)
+		}
+	} else {
+		sortMap := map[string]string{
+			//"ArticleCreateTime": "desc",
+			//"WechatArticleId":   "desc",
+		}
+		tmpTotal, list, err := elastic.WechatArticleEsSearch(keyWord, wechatPlatformId, 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].ArticleAndPlatformListToViewList(list)
+		}
+	}
+
+	// 标签处理
+	{
+		wechatPlateIdList := make([]int, 0)
+		for _, v := range viewList {
+			wechatPlateIdList = append(wechatPlateIdList, v.WechatPlatformId)
+		}
+		obj := rag.WechatPlatformTagMapping{}
+		mappingList, err := obj.GetWechatPlatformTagListByWechatPlatformIdList(wechatPlateIdList)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		wechatPlatformIdMap := make(map[int]*rag.WechatPlatformTagItem)
+		for _, v := range mappingList {
+			wechatPlatformIdMap[v.WechatPlatformId] = v
+		}
+		for k, v := range viewList {
+			item, ok := wechatPlatformIdMap[v.WechatPlatformId]
+			if !ok {
+				continue
+			}
+			viewList[k].TagName = item.TagName
+			viewList[k].TagId = item.TagId
+		}
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := response.WechatArticleListListResp{
+		List:   viewList,
+		Paging: page,
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ArticleList
+// @Title 我关注的接口
+// @Description 我关注的接口
+// @Param   WechatArticleId   query   int  true       "文章id"
+// @Success 200 {object} []*rag.WechatArticle
+// @router /wechat_platform/article/detail [get]
+func (c *WechatPlatformController) ArticleDetail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	wechatArticleId, _ := c.GetInt("WechatArticleId")
+	if wechatArticleId <= 0 {
+		br.Msg = "请选择文章"
+		br.IsSendEmail = false
+		return
+	}
+	obj := new(rag.WechatArticle)
+	item, err := obj.GetById(wechatArticleId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if item.IsDeleted == 1 {
+		br.Msg = "文章已删除"
+		br.IsSendEmail = false
+		return
+	}
+	resp := item.ToView()
+	resp.Content = html.UnescapeString(item.Content)
+
+	// 获取摘要信息
+	{
+		abstractObj := rag.WechatArticleAbstract{}
+		abstractItem, err := abstractObj.GetByWechatArticleId(wechatArticleId)
+		if err != nil && !utils.IsErrNoRow(err) {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,Err:" + err.Error()
+			return
+		}
+		resp.Abstract = abstractItem.Content
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ArticleList
+// @Title 我关注的接口
+// @Description 我关注的接口
+// @Param   WechatArticleId   query   int  true       "文章id"
+// @Success 200 {object} []*rag.WechatPlatform
+// @router /wechat_platform/article/del [get]
+func (c *WechatPlatformController) ArticleDel() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		return
+	}
+	wechatArticleId, _ := c.GetInt("WechatArticleId")
+	if wechatArticleId <= 0 {
+		br.Msg = "请选择文章"
+		br.IsSendEmail = false
+		return
+	}
+	obj := new(rag.WechatArticle)
+	item, err := obj.GetById(wechatArticleId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+
+	if item.IsDeleted == 1 {
+		br.Msg = "文章已删除"
+		br.IsSendEmail = false
+		return
+	}
+	item.IsDeleted = 1
+	err = item.Update([]string{"is_deleted"})
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	// 修改ES信息
+	services.AddOrEditEsWechatArticle(item.WechatArticleId)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}
+
+//func init() {
+//	//obj := rag.WechatPlatform{}
+//	//item, _ := obj.GetByID(2)
+//	//fmt.Println(llm.BeachAddWechatPlatform(item))
+//
+//	obj := rag.WechatArticle{}
+//	//item, _ := obj.GetById(30)
+//	list, _ := obj.GetListByCondition(``, ` `, []interface{}{}, 0, 1000)
+//	//llm.ArticleToTmpFile(item.TextContent)
+//	for _, item := range list {
+//		//llm.ArticleToKnowledge(item)
+//		llm.GenerateArticleAbstract(item)
+//	}
+//}
+
+//func init() {
+//	//obj := rag.WechatPlatform{}
+//	//item, _ := obj.GetByID(2)
+//	//fmt.Println(llm.BeachAddWechatPlatform(item))
+//
+//	//obj := rag.WechatArticle{}
+//	//list, _ := obj.GetListByCondition(`wechat_article_id,content`, `  `, []interface{}{}, 0, 1)
+//	////obj := rag.WechatPlatform{}
+//	////list, _ := obj.GetListByCondition(` AND wechat_platform_id !=1 `, []interface{}{}, 0, 100)
+//	////llm.ArticleToTmpFile(item.TextContent)
+//	//for _, item := range list {
+//	//	//llm.ArticleToKnowledge(item)
+//	//	services.ReGenerateArticleAbstract(item)
+//	//}
+//
+//	// 重新生成摘要
+//	{
+//		obj := rag.WechatArticle{}
+//		list, _ := obj.GetListByCondition(``, ` AND text_content !='' AND abstract_status = 0`, []interface{}{}, 0, 300)
+//		for _, item := range list {
+//			services.GenerateArticleAbstract(item)
+//		}
+//	}
+//
+//	//// 删除摘要向量库
+//	//{
+//	//	obj := rag.WechatArticleAbstract{}
+//	//	list, _ := obj.GetListByCondition(`vector_key,wechat_article_abstract_id`, ` AND wechat_article_id in (25,27) `, []interface{}{}, 0, 10000)
+//	//	fmt.Println(services.DelDoc(list))
+//	//}
+//
+//	fmt.Println("修复结束")
+//}
+
+//func init() {
+//	//// 微信文章加到es
+//	//{
+//	//	obj := rag.WechatArticle{}
+//	//	list, _ := obj.GetListByCondition(` wechat_article_id `, ` `, []interface{}{}, 0, 10000)
+//	//	total := len(list)
+//	//	for k, item := range list {
+//	//		fmt.Println(k, "/", total)
+//	//		services.AddOrEditEsWechatArticle(item.WechatArticleId)
+//	//	}
+//	//
+//	//	fmt.Println("结束了")
+//	//}
+//
+//	//// 微信文章加到es
+//	{
+//		obj := rag.WechatArticleAbstract{}
+//		list, _ := obj.GetListByCondition(` wechat_article_abstract_id `, ` `, []interface{}{}, 0, 10000)
+//		total := len(list)
+//		for k, item := range list {
+//			fmt.Println(k, "/", total)
+//			services.AddOrEditEsWechatArticleAbstract(item.WechatArticleAbstractId)
+//		}
+//
+//		fmt.Println("结束了")
+//	}
+//
+//}

+ 54 - 54
controllers/ppt_english.go

@@ -310,33 +310,33 @@ func (this *PptEnglishController) EditPpt() {
 		return
 	}
 
-	pptMap, err := ppt_english.GetPptMappingByPptId(req.PptId)
-	if err != nil {
-		br.Msg = `该PPT信息不存在, 保存失败`
-		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
-		br.IsSendEmail = false
-		return
-	}
-	pptMapList, err := ppt_english.GetPptMappingListByGroupIdDesc(pptMap.GroupId)
-	if err != nil {
-		br.ErrMsg = "PPT目录信息异常"
-		return
-	}
-	count, err := ppt_english.GetPptMappingCountByGroupPptId(pptMap.GroupPptId, this.SysUser.AdminId)
-	if err != nil {
-		br.Msg = "获取英文PPT和用户权限关系失败"
-		br.ErrMsg = "获取英文PPT和用户权限关系失败, Err:" + err.Error()
-		return
-	}
-	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
-		// 如果没有人为移动位置, 默认将当前ppt置顶
-		err = ppt.MoveGroupPptEnglish(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
-		if err != nil {
-			br.Msg = err.Error()
-			br.ErrMsg = "移动失败,Err:" + err.Error()
-			return
-		}
-	}
+	//pptMap, err := ppt_english.GetPptMappingByPptId(req.PptId)
+	//if err != nil {
+	//	br.Msg = `该PPT信息不存在, 保存失败`
+	//	br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+	//	br.IsSendEmail = false
+	//	return
+	//}
+	//pptMapList, err := ppt_english.GetPptMappingListByGroupIdDesc(pptMap.GroupId)
+	//if err != nil {
+	//	br.ErrMsg = "PPT目录信息异常"
+	//	return
+	//}
+	//count, err := ppt_english.GetPptMappingCountByGroupPptId(pptMap.GroupPptId, this.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "获取英文PPT和用户权限关系失败"
+	//	br.ErrMsg = "获取英文PPT和用户权限关系失败, Err:" + err.Error()
+	//	return
+	//}
+	//if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+	//	// 如果没有人为移动位置, 默认将当前ppt置顶
+	//	err = ppt.MoveGroupPptEnglish(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+	//	if err != nil {
+	//		br.Msg = err.Error()
+	//		br.ErrMsg = "移动失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
 
 	// 日志记录
 	{
@@ -752,33 +752,33 @@ func (this *PptEnglishController) SaveLog() {
 	pptItem.PptPage = len(pageContent)
 	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "ppt_page"})
 
-	pptMap, err := ppt_english.GetPptMappingByPptId(req.PptId)
-	if err != nil {
-		br.Msg = `该PPT信息不存在, 保存失败`
-		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
-		br.IsSendEmail = false
-		return
-	}
-	pptMapList, err := ppt_english.GetPptMappingListByGroupIdDesc(pptMap.GroupId)
-	if err != nil {
-		br.ErrMsg = "PPT目录信息异常"
-		return
-	}
-	count, err := ppt_english.GetPptMappingCountByGroupPptId(pptMap.GroupPptId, this.SysUser.AdminId)
-	if err != nil {
-		br.Msg = "获取英文PPT和用户权限关系失败"
-		br.ErrMsg = "获取英文PPT和用户权限关系失败, Err:" + err.Error()
-		return
-	}
-	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
-		// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
-		err = ppt.MoveGroupPptEnglish(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
-		if err != nil {
-			br.Msg = err.Error()
-			br.ErrMsg = "移动失败,Err:" + err.Error()
-			return
-		}
-	}
+	//pptMap, err := ppt_english.GetPptMappingByPptId(req.PptId)
+	//if err != nil {
+	//	br.Msg = `该PPT信息不存在, 保存失败`
+	//	br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+	//	br.IsSendEmail = false
+	//	return
+	//}
+	//pptMapList, err := ppt_english.GetPptMappingListByGroupIdDesc(pptMap.GroupId)
+	//if err != nil {
+	//	br.ErrMsg = "PPT目录信息异常"
+	//	return
+	//}
+	//count, err := ppt_english.GetPptMappingCountByGroupPptId(pptMap.GroupPptId, this.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "获取英文PPT和用户权限关系失败"
+	//	br.ErrMsg = "获取英文PPT和用户权限关系失败, Err:" + err.Error()
+	//	return
+	//}
+	//if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+	//	// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
+	//	err = ppt.MoveGroupPptEnglish(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+	//	if err != nil {
+	//		br.Msg = err.Error()
+	//		br.ErrMsg = "移动失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
 
 	//日志记录
 	logInfo := &ppt_english.PptEnglishSaveLog{

+ 59 - 59
controllers/ppt_v2.go

@@ -310,33 +310,33 @@ func (this *PptV2Controller) EditPpt() {
 		return
 	}
 
-	pptMap, err := models.GetPptMappingByPptId(req.PptId)
-	if err != nil {
-		br.Msg = `该PPT信息不存在, 保存失败`
-		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
-		br.IsSendEmail = false
-		return
-	}
-	pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
-	if err != nil {
-		br.ErrMsg = "PPT目录信息异常"
-		return
-	}
-	count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
-	if err != nil {
-		br.Msg = "查询映射关系失败"
-		br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
-		return
-	}
-	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
-		// 如果没有人为移动位置, 默认将当前ppt置顶
-		err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
-		if err != nil {
-			br.Msg = err.Error()
-			br.ErrMsg = "移动失败,Err:" + err.Error()
-			return
-		}
-	}
+	//pptMap, err := models.GetPptMappingByPptId(req.PptId)
+	//if err != nil {
+	//	br.Msg = `该PPT信息不存在, 保存失败`
+	//	br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+	//	br.IsSendEmail = false
+	//	return
+	//}
+	//pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
+	//if err != nil {
+	//	br.ErrMsg = "PPT目录信息异常"
+	//	return
+	//}
+	//count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "查询映射关系失败"
+	//	br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+	//	return
+	//}
+	//if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+	//	// 如果没有人为移动位置, 默认将当前ppt置顶
+	//	err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+	//	if err != nil {
+	//		br.Msg = err.Error()
+	//		br.ErrMsg = "移动失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
 
 	// 日志记录
 	{
@@ -722,38 +722,38 @@ func (this *PptV2Controller) SaveLog() {
 	err = pptItem.Update([]string{"TemplateType", "BackgroundImg", "Title", "ReportType", "PptDate", "Content", "ModifyTime", "TitleSetting", "ppt_page"})
 
 	// 将更新后的PPT, 置顶
-	pptMap, err := models.GetPptMappingByPptId(int64(req.PptId))
-	if err != nil {
-		if utils.IsErrNoRow(err) {
-			br.Msg = `该PPT信息不存在, 保存失败`
-			br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
-			br.IsSendEmail = false
-			return
-		}
-		br.Msg = "保存草稿失败"
-		br.ErrMsg = fmt.Sprintf("保存草稿失败, %v", err)
-		return
-	}
-	pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
-	if err != nil {
-		br.ErrMsg = "PPT目录信息异常"
-		return
-	}
-	count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
-	if err != nil {
-		br.Msg = "查询映射关系失败"
-		br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
-		return
-	}
-	if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
-		// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
-		err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
-		if err != nil {
-			br.Msg = err.Error()
-			br.ErrMsg = "移动失败,Err:" + err.Error()
-			return
-		}
-	}
+	//pptMap, err := models.GetPptMappingByPptId(int64(req.PptId))
+	//if err != nil {
+	//	if utils.IsErrNoRow(err) {
+	//		br.Msg = `该PPT信息不存在, 保存失败`
+	//		br.ErrMsg = `该PPT信息不存在, 保存失败, Err` + err.Error()
+	//		br.IsSendEmail = false
+	//		return
+	//	}
+	//	br.Msg = "保存草稿失败"
+	//	br.ErrMsg = fmt.Sprintf("保存草稿失败, %v", err)
+	//	return
+	//}
+	//pptMapList, err := models.GetPptMappingListByGroupId(pptMap.GroupId)
+	//if err != nil {
+	//	br.ErrMsg = "PPT目录信息异常"
+	//	return
+	//}
+	//count, err := models.GetPptMappingByGroupPptCountId(pptMap.GroupPptId, this.SysUser.AdminId)
+	//if err != nil {
+	//	br.Msg = "查询映射关系失败"
+	//	br.ErrMsg = "查询映射关系失败, 保存失败, Err:" + err.Error()
+	//	return
+	//}
+	//if !pptMap.IsMoved && len(pptMapList) > 1 && count > 0 {
+	//	// 如果没有人为移动位置, 且当前用户有权限, 默认将当前ppt置顶
+	//	err = ppt.MoveGroupPpt(pptMap.GroupId, pptMap.GroupPptId, pptMapList[0].GroupPptId, 0, this.SysUser.AdminId)
+	//	if err != nil {
+	//		br.Msg = err.Error()
+	//		br.ErrMsg = "移动失败,Err:" + err.Error()
+	//		return
+	//	}
+	//}
 
 	//日志记录
 	logInfo := &models.PptV2SaveLog{

+ 6 - 0
controllers/report.go

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

+ 10 - 2
controllers/report_approve/report_approve.go

@@ -359,6 +359,8 @@ func (this *ReportApproveController) List() {
 	// 审批通过的报告显示下载按钮
 	reportImg := make(map[string]string)
 	reportPdf := make(map[string]string)
+	reportImgMobile := make(map[string]string)
+	reportPdfMobile := make(map[string]string)
 	{
 		var reportIds, enReportIds []int
 		for _, v := range ormList {
@@ -373,7 +375,7 @@ func (this *ReportApproveController) List() {
 			}
 		}
 		if len(reportIds) > 0 {
-			reports, e := models.GetReportFieldsByIds(reportIds, []string{"id", "detail_img_url", "detail_pdf_url"})
+			reports, e := models.GetReportFieldsByIds(reportIds, []string{"id", "detail_img_url", "detail_pdf_url", "detail_img_url_mobile", "detail_pdf_url_mobile"})
 			if e != nil {
 				br.Msg = "获取失败"
 				br.ErrMsg = fmt.Sprintf("GetReportFieldsByIds, %v", e)
@@ -383,10 +385,12 @@ func (this *ReportApproveController) List() {
 				k := fmt.Sprintf("%d-%d", report_approve.FlowReportTypeChinese, r.Id)
 				reportImg[k] = r.DetailImgUrl
 				reportPdf[k] = r.DetailPdfUrl
+				reportImgMobile[k] = r.DetailImgUrlMobile
+				reportPdfMobile[k] = r.DetailPdfUrlMobile
 			}
 		}
 		if len(enReportIds) > 0 {
-			enReports, e := models.GetEnglishReportFieldsByIds(enReportIds, []string{"id", "detail_img_url", "detail_pdf_url"})
+			enReports, e := models.GetEnglishReportFieldsByIds(enReportIds, []string{"id", "detail_img_url", "detail_pdf_url", "detail_img_url_mobile", "detail_pdf_url_mobile"})
 			if e != nil {
 				br.Msg = "获取失败"
 				br.ErrMsg = fmt.Sprintf("GetEnglishReportFieldsByIds, %v", e)
@@ -396,6 +400,8 @@ func (this *ReportApproveController) List() {
 				k := fmt.Sprintf("%d-%d", report_approve.FlowReportTypeEnglish, r.Id)
 				reportImg[k] = r.DetailImgUrl
 				reportPdf[k] = r.DetailPdfUrl
+				reportImgMobile[k] = r.DetailImgUrlMobile
+				reportPdfMobile[k] = r.DetailPdfUrlMobile
 			}
 		}
 	}
@@ -439,6 +445,8 @@ func (this *ReportApproveController) List() {
 		k := fmt.Sprintf("%d-%d", t.ReportType, t.ReportId)
 		t.DetailImgUrl = reportImg[k]
 		t.DetailPdfUrl = reportPdf[k]
+		t.DetailImgUrlMobile = reportImgMobile[k]
+		t.DetailPdfUrlMobile = reportPdfMobile[k]
 
 		respList = append(respList, t)
 	}

+ 17 - 2
controllers/report_chapter.go

@@ -366,6 +366,7 @@ func (this *ReportController) EditDayWeekChapter() {
 	if req.Content != "" {
 		// 处理关联excel的表格id
 		req.Content = services.HandleReportContentTable(reportInfo.Id, req.Content)
+		req.Content = services.HandleReportContent(req.Content, "del", nil)
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
 			br.Msg = "存在非法标签"
@@ -408,6 +409,7 @@ func (this *ReportController) EditDayWeekChapter() {
 	reportChapterInfo.ContentModifyTime = time.Now()
 	if req.ContentStruct != `` {
 		req.ContentStruct = services.HandleReportContentStructTable(reportChapterInfo.ReportId, req.ContentStruct)
+		req.ContentStruct = services.HandleReportContentStruct(req.ContentStruct, "del", nil)
 	}
 	reportChapterInfo.ContentStruct = html.EscapeString(req.ContentStruct)
 
@@ -783,6 +785,19 @@ func (this *ReportController) GetDayWeekChapter() {
 	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 = "获取失败"
+		br.ErrMsg = "获取配置失败,Err:" + err.Error()
+		return
+	}
+
+	if businessConf.ConfVal == `true` {
+		tokenMap := make(map[string]string)
+		chapterItem.Content = services.HandleReportContent(chapterItem.Content, "add", tokenMap)
+		chapterItem.ContentStruct = services.HandleReportContentStruct(chapterItem.ContentStruct, "add", tokenMap)
+	}
+
 	// 授权用户列表map
 	chapterGrantIdList := make([]int, 0)
 	// 关联品种id列表map
@@ -1353,7 +1368,7 @@ func (this *ReportController) PublishDayWeekReportChapter() {
 
 	// 更新章节ES
 	{
-		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId, reportInfo.IsPublicPublish)
 	}
 
 	// 同时发布报告
@@ -1637,7 +1652,7 @@ func (this *ReportController) CancelPublishReportChapter() {
 
 	// 更新章节ES
 	{
-		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId)
+		go services.UpdateReportChapterEs(chapterInfo.ReportChapterId, reportInfo.IsPublicPublish)
 	}
 
 	br.Ret = 200

+ 137 - 11
controllers/report_v2.go

@@ -12,13 +12,14 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
 	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"html"
 	"io"
 	"os"
 	"strconv"
 	"strings"
 	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 // ListReport
@@ -393,6 +394,7 @@ func (this *ReportController) Add() {
 
 	var contentSub string
 	if req.Content != "" {
+		req.Content = services.HandleReportContent(req.Content, "del", nil)
 		e := utils.ContentXssCheck(req.Content)
 		if e != nil {
 			br.Msg = "存在非法标签"
@@ -414,6 +416,10 @@ func (this *ReportController) Add() {
 		}
 	}
 
+	if req.ContentStruct != `` {
+		req.ContentStruct = services.HandleReportContentStruct(req.ContentStruct, "del", nil)
+	}
+
 	// 报告期数
 	maxStage, err := models.GetReportStage(req.ClassifyIdFirst, req.ClassifyIdSecond, req.ClassifyIdThird)
 	if err != nil {
@@ -592,6 +598,8 @@ func (this *ReportController) Edit() {
 
 	req.Content = services.HandleReportContentTable(int(req.ReportId), req.Content)
 	req.ContentStruct = services.HandleReportContentStructTable(int(req.ReportId), req.ContentStruct)
+	req.Content = services.HandleReportContent(req.Content, "del", nil)
+	req.ContentStruct = services.HandleReportContentStruct(req.ContentStruct, "del", nil)
 
 	// 编辑报告信息
 	err, errMsg := services.EditReport(reportInfo, req, sysUser)
@@ -684,24 +692,28 @@ func (this *ReportController) Detail() {
 
 	if item.HeadResourceId > 0 {
 		headResource, err := smart_report.GetResourceItemById(item.HeadResourceId)
-		if err != nil {
+		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "操作失败"
 			br.ErrMsg = "获取资源库版头失败, Err: " + err.Error()
 			return
 		}
-		item.HeadImg = headResource.ImgUrl
-		item.HeadStyle = headResource.Style
+		if headResource != nil && headResource.ResourceId > 0 {
+			item.HeadImg = headResource.ImgUrl
+			item.HeadStyle = headResource.Style
+		}
 	}
 
 	if item.EndResourceId > 0 {
 		endResource, err := smart_report.GetResourceItemById(item.EndResourceId)
-		if err != nil {
+		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "操作失败"
-			br.ErrMsg = "获取资源库版失败, Err: " + err.Error()
+			br.ErrMsg = "获取资源库版失败, Err: " + err.Error()
 			return
 		}
-		item.EndImg = endResource.ImgUrl
-		item.EndStyle = endResource.Style
+		if endResource != nil && endResource.ResourceId > 0 {
+			item.EndImg = endResource.ImgUrl
+			item.EndStyle = endResource.Style
+		}
 	}
 
 	item.Content = services.HandleReportContentTable(item.Id, item.Content)
@@ -711,6 +723,24 @@ func (this *ReportController) Detail() {
 		v.ContentStruct = services.HandleReportContentStructTable(item.Id, v.ContentStruct)
 	}
 
+	businessConf, err := models.GetBusinessConfByKey(models.BusinessConfIsOpenChartExpired)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取配置失败,Err:" + err.Error()
+		return
+	}
+
+	if businessConf.ConfVal == `true` {
+		tokenMap := make(map[string]string)
+		item.Content = services.HandleReportContent(item.Content, "add", tokenMap)
+		item.ContentStruct = services.HandleReportContentStruct(item.ContentStruct, "add", tokenMap)
+		for _, v := range chapterList {
+			v.Content = services.HandleReportContent(v.Content, "add", tokenMap)
+			v.ContentStruct = services.HandleReportContentStruct(v.ContentStruct, "add", tokenMap)
+		}
+
+	}
+
 	resp := &models.ReportDetailView{
 		ReportDetail: item,
 		ChapterList:  chapterList,
@@ -789,14 +819,15 @@ func (this *ReportController) SaveReportContent() {
 		if content == "" {
 			content = this.GetString("Content")
 		}
+		content = services.HandleReportContent(content, "del", nil)
 		if content != "" {
-			e := utils.ContentXssCheck(req.Content)
+			e := utils.ContentXssCheck(content)
 			if e != nil {
 				br.Msg = "存在非法标签"
 				br.ErrMsg = "存在非法标签, Err: " + e.Error()
 				return
 			}
-			contentClean, e := services.FilterReportContentBr(req.Content)
+			contentClean, e := services.FilterReportContentBr(content)
 			if e != nil {
 				br.Msg = "内容去除前后空格失败"
 				br.ErrMsg = "内容去除前后空格失败, Err: " + e.Error()
@@ -804,6 +835,8 @@ func (this *ReportController) SaveReportContent() {
 			}
 			content = contentClean
 
+			req.ContentStruct = services.HandleReportContentStruct(req.ContentStruct, "del", nil)
+
 			contentSub, err := services.GetReportContentSub(content)
 			if err != nil {
 				go alarm_msg.SendAlarmMsg("解析 ContentSub 失败,Err:"+err.Error(), 3)
@@ -1462,7 +1495,7 @@ func (this *ReportController) PrePublishReport() {
 
 	// 生成报告pdf和长图
 	{
-		reportPdfUrl := services.GetGeneralPdfUrl(reportDetail.ReportCode, reportDetail.ClassifyNameFirst, reportDetail.ReportLayout)
+		reportPdfUrl := services.GetGeneralPdfUrl(reportDetail.Id, reportDetail.ReportCode, reportDetail.ClassifyNameFirst, reportDetail.ReportLayout)
 		go services.Report2pdfAndJpeg(reportPdfUrl, reportDetail.Id, 1)
 	}
 
@@ -1689,6 +1722,99 @@ func (this *ReportController) CancelApprove() {
 	br.Msg = "操作成功"
 }
 
+// ShareGenerate
+// @Title 获取复制链接
+// @Description 获取复制链接
+// @Param	request	body models.ReportShartLinkReq true "type json string"
+// @Success 200 Ret=200 操作成功
+// @router /share/generate [post]
+func (this *ReportController) ShareGenerate() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.ReportShartUrlReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	var title string
+	reportItem, _ := models.GetReportByReportId(req.ReportId)
+	if reportItem != nil && reportItem.Title != "" {
+		title = reportItem.Title
+	}
+
+	link, err := services.GetReportShareUrlToken(req, this.SysUser.AdminId)
+	if err != nil || link == "" {
+		br.Msg = "复制链接失败"
+		br.ErrMsg = "获取复制链接失败, Err: " + err.Error()
+		return
+	}
+
+	resp := new(models.ReportShartUrlResp)
+	resp.UrlToken = fmt.Sprint(link, " ", title)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ShareTransform
+// @Title 获取原始链接
+// @Description 获取原始链接
+// @Param	Token   query   string  true    "复制链接的token"
+// @Success 200 Ret=200 操作成功
+// @router /share/link [get]
+func (this *ReportCommonController) ShareTransform() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	token := this.GetString("Token")
+	tokenArr := strings.Split(token, " ")
+	if len(tokenArr) > 0 {
+		token = tokenArr[0]
+	}
+
+	link, msg, err := services.TransfromToOriginUrl(token)
+	if err != nil {
+		if msg == "" {
+			msg = "获取失败"
+		}
+
+		htmlTpl := `
+		<!DOCTYPE html>
+		<html lang="zh">
+		<head>
+		    <meta charset="UTF-8">
+		    <title>链接失效</title>
+		</head>
+		<body>
+		    <h1>链接失效</h1>
+		    <p>%s</p>
+		</body>
+		</html>
+		`
+		htmlTpl = fmt.Sprintf(htmlTpl, msg)
+		this.Ctx.Output.SetStatus(404)
+		this.Ctx.Output.Body([]byte(htmlTpl))
+		utils.FileLog.Info("获取复制链接失败, Err: " + err.Error())
+		return
+	}
+
+	this.Ctx.Redirect(302, link)
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
 // init
 // @Description: 修复历史报告数据
 // @author: Roc

+ 12 - 7
controllers/smart_report/smart_report.go

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

+ 85 - 14
controllers/sys_group.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
+	"eta/eta_api/services"
 	"eta/eta_api/services/eta_forum"
 	"eta/eta_api/utils"
 	"strings"
@@ -38,22 +39,75 @@ func (this *SysGroupController) Add() {
 		br.ErrMsg = "部门ID不可为空"
 		return
 	}
-	if req.GroupName == "" {
-		br.Msg = "分组名称不能为空"
+	//构建部门的分组树
+	groupList,err:=system.GetGroupByDepartmentId(req.DepartmentId)
+	if err!=nil{
+		br.Msg = "获取数据失败,获取当前部门的分组列表失败"
+		br.ErrMsg = "获取当前部门的分组列表失败,Err:" + err.Error()
 		return
 	}
+	//获取传入部门下的分组名称,分组名称为唯一标识,不会重复
 	groupNameArr := strings.Split(req.GroupName, ",")
-	groupIds := make([]int, 0)
+	root := new(services.GroupNode)
+	services.BuildGroupTree(groupList,0,2,root)
+	//现有的分组名称
+	existGroupMap:=make(map[string]bool,len(root.Child))
+	for _, v := range root.Child {
+		existGroupMap[v.GroupName] = false
+	}
+	//传入的分组名称
+	reqGroupMap:=make(map[string]int,len(groupNameArr))
 	for _, v := range groupNameArr {
-		count, err := system.GetSysGroupCount(req.DepartmentId, v)
-		if err != nil {
-			br.Msg = "获取数据失败"
-			br.ErrMsg = "获取数据失败,Err:" + err.Error()
-			return
+		reqGroupMap[v] = 1
+	}
+	//新增分组名称
+	newGroupMap := make(map[string]int)
+	groupIds := make([]int, 0)
+	if len(reqGroupMap)>0{
+		for k, _ := range reqGroupMap {
+			if _, ok := existGroupMap[k]; !ok {
+				newGroupMap[k] = 1
+				groupIds = append(groupIds, 0)
+			}else{
+				existGroupMap[k] = true
+			}
 		}
-		if count <= 0 {
+	}
+	var deleteIds []int
+	var addGroup bool
+	//删除所有分组即可
+	if req.GroupName == "" {
+		addGroup=false
+		for _, node := range root.Child {
+			//分组Id
+			deleteIds = append(deleteIds, node.GroupId)
+			//teamId
+			for _, subNode := range node.Child {
+				deleteIds=append(deleteIds, subNode.GroupId)
+			}
+		}
+	}else{
+		addGroup=true
+		for k, v := range existGroupMap {
+			if !v{
+				for _, node := range root.Child {
+					if node.GroupName == k{
+						//分组Id
+						deleteIds = append(deleteIds, node.GroupId)
+						//teamId
+						for _, subNode := range node.Child {
+							deleteIds=append(deleteIds, subNode.GroupId)
+						}
+					}
+				}
+			}
+		}
+	}
+	if addGroup{
+		//新增分组
+		for k, _ := range newGroupMap {
 			item := new(system.SysGroup)
-			item.GroupName = v
+			item.GroupName = k
 			item.DepartmentId = req.DepartmentId
 			item.CreateTime = time.Now()
 			groupId, e := system.AddSysGroup(item)
@@ -62,7 +116,6 @@ func (this *SysGroupController) Add() {
 				br.ErrMsg = "新增失败,Err:" + e.Error()
 				return
 			}
-
 			// 同步分组缓存
 			if utils.BusinessCode == utils.BusinessCodeRelease {
 				var syncData system.SyncGroupData
@@ -72,12 +125,30 @@ func (this *SysGroupController) Add() {
 			}
 			groupIds = append(groupIds, int(groupId))
 		}
+		go eta_forum.GroupSave(groupIds)
+		//删除分组合他的子分组,写死只有2层分组,因此逻辑代码也就写死2层,一层分组,一层team
+	}
+	if len(deleteIds)>0 {
+		err =services.DeleteSysGroupByIds(deleteIds)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+		// 同步分组缓存
+		if utils.BusinessCode == utils.BusinessCodeRelease {
+			for _, groupId := range deleteIds {
+				var syncData system.SyncGroupData
+				syncData.Source = utils.SOURCE_ETA_FLAG
+				syncData.GroupId = groupId
+				_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+				go eta_forum.GroupDelete(groupId)
+			}
+		}
 	}
-
-	go eta_forum.GroupSave(groupIds)
 	br.Ret = 200
 	br.Success = true
-	br.Msg = "新增成功"
+	br.Msg = "保存成功"
 }
 
 // @Title 修改分组

+ 5 - 1
controllers/sys_role.go

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

+ 75 - 12
controllers/sys_team.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
+	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"strings"
 	"time"
@@ -37,21 +38,67 @@ func (this *SysTeamController) Add() {
 		br.ErrMsg = "大组ID不可为空"
 		return
 	}
-	if req.TeamName == "" {
-		br.Msg = "分组名称不能为空"
+	//构建部门的分组树
+	teamList, err := system.GetChildSysGroupByGroupId(req.GroupId)
+	teamNameArr := strings.Split(req.TeamName, ",")
+	if err != nil {
+		br.Msg = "获取数据失败,获取当前分组的team列表失败"
+		br.ErrMsg = "获取当前部门的分组列表失败,Err:" + err.Error()
 		return
 	}
-	teamNameArr := strings.Split(req.TeamName, ",")
+	//现有的分组名称
+	existTeamMap := make(map[string]bool, len(teamList))
+	for _, v := range teamList {
+		existTeamMap[v.GroupName] = false
+	}
+	//传入的分组名称
+	reqTeamMap := make(map[string]int, len(teamNameArr))
 	for _, v := range teamNameArr {
-		count, err := system.GetSysTeamCount(req.GroupId, v)
-		if err != nil {
-			br.Msg = "获取数据失败"
-			br.ErrMsg = "获取数据失败,Err:" + err.Error()
-			return
+		reqTeamMap[v] = 1
+	}
+	//新增分组名称
+	newTeamMap := make(map[string]int)
+	groupIds := make([]int, 0)
+	if len(reqTeamMap) > 0 {
+		for k, _ := range reqTeamMap {
+			if _, ok := existTeamMap[k]; !ok {
+				newTeamMap[k] = 1
+				groupIds = append(groupIds, 0)
+			} else {
+				existTeamMap[k] = true
+			}
+		}
+	}
+	var deleteIds []int
+	var addGroup bool
+	//if req.TeamName == "" {
+	//	br.Msg = "分组名称不能为空"
+	//	return
+	//}
+	//删除所有team即可
+	if req.TeamName == "" {
+		addGroup = false
+		for _, node := range teamList {
+			//teamId
+			deleteIds = append(deleteIds, node.GroupId)
 		}
-		if count <= 0 {
+	} else {
+		addGroup = true
+		for k, v := range existTeamMap {
+			if !v {
+				for _, node := range teamList {
+					if node.GroupName == k {
+						//分组Id
+						deleteIds = append(deleteIds, node.GroupId)
+					}
+				}
+			}
+		}
+	}
+	if addGroup {
+		for k, _ := range newTeamMap {
 			item := new(system.SysGroup)
-			item.GroupName = v
+			item.GroupName = k
 			item.DepartmentId = req.DepartmentId
 			item.ParentId = req.GroupId
 			item.CreateTime = time.Now()
@@ -61,7 +108,6 @@ func (this *SysTeamController) Add() {
 				br.ErrMsg = "新增失败,Err:" + e.Error()
 				return
 			}
-
 			// 同步分组缓存
 			if utils.BusinessCode == utils.BusinessCodeRelease {
 				var syncData system.SyncGroupData
@@ -71,9 +117,26 @@ func (this *SysTeamController) Add() {
 			}
 		}
 	}
+	if len(deleteIds)>0 {
+		err =services.DeleteSysGroupByIds(deleteIds)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "删除失败,Err:" + err.Error()
+			return
+		}
+		// 同步分组缓存
+		if utils.BusinessCode == utils.BusinessCodeRelease {
+			for _, groupId := range deleteIds {
+				var syncData system.SyncGroupData
+				syncData.Source = utils.SOURCE_ETA_FLAG
+				syncData.GroupId = groupId
+				_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+			}
+		}
+	}
 	br.Ret = 200
 	br.Success = true
-	br.Msg = "新增成功"
+	br.Msg = "保存成功"
 }
 
 // @Title 修改分组

+ 1 - 1
controllers/trade_analysis/warehouse_classify.go

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

+ 1 - 0
controllers/user_login.go

@@ -560,6 +560,7 @@ func (this *UserLoginController) Login() {
 	}
 	resp.ProductName = productName
 	resp.Authority = sysUser.Authority
+	resp.Mobile = sysUser.Mobile
 
 	// 设置redis缓存
 	{

+ 1 - 0
go.mod

@@ -40,6 +40,7 @@ require (
 	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/shopspring/decimal v1.3.1
 	github.com/silenceper/wechat/v2 v2.1.6
+	github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72
 	github.com/spf13/viper v1.7.0
 	github.com/tealeg/xlsx v1.0.5
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/asr v1.0.873

+ 1 - 0
go.sum

@@ -570,6 +570,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
 github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=

+ 0 - 2
main.go

@@ -12,7 +12,6 @@ import (
 	_ "eta/eta_api/routers"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
-
 	"github.com/beego/beego/v2/adapter/logs"
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web/context"
@@ -23,7 +22,6 @@ func main() {
 		web.BConfig.WebConfig.DirectoryIndex = true
 		web.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
 	}
-
 	go services.Task()
 	//services.RaiReportInit()
 

+ 24 - 2
models/ai_predict_model/ai_predict_model_index.go

@@ -304,15 +304,37 @@ func (m *AiPredictModelIndex) ImportIndexAndData(createIndexes, updateIndexes []
 				err = fmt.Errorf("update index err: %v", e)
 				return
 			}
+			var hasDaily, hasMonthly bool
 			for _, d := range v.Data {
 				d.AiPredictModelIndexId = v.Index.AiPredictModelIndexId
 				d.IndexCode = v.Index.IndexCode
 				d.DataTimestamp = d.DataTime.UnixNano() / 1e6
+				if d.Source == ModelDataSourceDaily {
+					hasDaily = true
+				}
+				if d.Source == ModelDataSourceMonthly {
+					hasMonthly = true
+				}
+			}
+			// 哪个有数据就先清空然后重新写入,没数据就保留旧数据, 都没就忽略
+			if !hasDaily && !hasMonthly {
+				continue
+			}
+			removeCond := ``
+			removePars := make([]interface{}, 0)
+			removePars = append(removePars, v.Index.AiPredictModelIndexId)
+			if hasDaily && !hasMonthly {
+				removeCond += ` AND source = ?`
+				removePars = append(removePars, ModelDataSourceDaily)
+			}
+			if !hasDaily && hasMonthly {
+				removeCond += ` AND source = ?`
+				removePars = append(removePars, ModelDataSourceMonthly)
 			}
 
 			// 清空指标并新增
-			sql := `DELETE FROM ai_predict_model_data WHERE ai_predict_model_index_id = ?`
-			e = tx.Exec(sql, v.Index.AiPredictModelIndexId).Error
+			sql := fmt.Sprintf(`DELETE FROM ai_predict_model_data WHERE ai_predict_model_index_id = ? %s`, removeCond)
+			e = tx.Exec(sql, removePars...).Error
 			if e != nil {
 				err = fmt.Errorf("clear index data err: %v", e)
 				return

+ 90 - 33
models/business_conf.go

@@ -1,10 +1,12 @@
 package models
 
 import (
+	"encoding/json"
 	"eta/eta_api/global"
 	"eta/eta_api/utils"
 	"fmt"
 	"html"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -14,39 +16,40 @@ var (
 )
 
 const (
-	BusinessConfUseXf                        = "UseXf"
-	BusinessConfXfAppid                      = "XfAppid"
-	BusinessConfXfApiKey                     = "XfApiKey"
-	BusinessConfXfApiSecret                  = "XfApiSecret"
-	BusinessConfXfVcn                        = "XfVcn"
-	BusinessConfEnPptCoverImgs               = "EnPptCoverImgs"
-	BusinessConfIsReportApprove              = "IsReportApprove"
-	BusinessConfReportApproveType            = "ReportApproveType"
-	BusinessConfCompanyName                  = "CompanyName"
-	BusinessConfCompanyWatermark             = "CompanyWatermark"
-	BusinessConfWatermarkChart               = "WatermarkChart"
-	BusinessConfLoginSmsTpId                 = "LoginSmsTpId"
-	BusinessConfLoginSmsGjTpId               = "LoginSmsGjTpId"
-	BusinessConfSmsJhgnAppKey                = "SmsJhgnAppKey"
-	BusinessConfSmsJhgjAppKey                = "SmsJhgjAppKey"
-	BusinessConfLdapHost                     = "LdapHost"
-	BusinessConfLdapBase                     = "LdapBase"
-	BusinessConfLdapPort                     = "LdapPort"
-	BusinessConfEmailClient                  = "EmailClient"
-	BusinessConfEmailServerHost              = "EmailServerHost"
-	BusinessConfEmailServerPort              = "EmailServerPort"
-	BusinessConfEmailSender                  = "EmailSender"
-	BusinessConfEmailSenderUserName          = "EmailSenderUserName"
-	BusinessConfEmailSenderPassword          = "EmailSenderPassword"
-	BusinessConfSmsClient                    = "SmsClient"
-	BusinessConfNanHuaSmsAppKey              = "NanHuaSmsAppKey"
-	BusinessConfNanHuaSmsAppSecret           = "NanHuaSmsAppSecret"
-	BusinessConfNanHuaSmsApiHost             = "NanHuaSmsApiHost"
-	BusinessConfLoginSmsTplContent           = "LoginSmsTplContent"
-	BusinessConfLoginEmailTemplateSubject    = "LoginEmailTemplateSubject"
-	BusinessConfLoginEmailTemplateContent    = "LoginEmailTemplateContent"
-	BusinessConfLdapBindUserSuffix           = "LdapBindUserSuffix"
-	BusinessConfLdapUserFilter               = "LdapUserFilter"
+	BusinessConfUseXf                     = "UseXf"
+	BusinessConfXfAppid                   = "XfAppid"
+	BusinessConfXfApiKey                  = "XfApiKey"
+	BusinessConfXfApiSecret               = "XfApiSecret"
+	BusinessConfXfVcn                     = "XfVcn"
+	BusinessConfEnPptCoverImgs            = "EnPptCoverImgs"
+	BusinessConfIsReportApprove           = "IsReportApprove"
+	BusinessConfReportApproveType         = "ReportApproveType"
+	BusinessConfCompanyName               = "CompanyName"
+	BusinessConfCompanyWatermark          = "CompanyWatermark"
+	BusinessConfWatermarkChart            = "WatermarkChart"
+	BusinessConfLoginSmsTpId              = "LoginSmsTpId"
+	BusinessConfLoginSmsGjTpId            = "LoginSmsGjTpId"
+	BusinessConfSmsJhgnAppKey             = "SmsJhgnAppKey"
+	BusinessConfSmsJhgjAppKey             = "SmsJhgjAppKey"
+	BusinessConfLdapHost                  = "LdapHost"
+	BusinessConfLdapBase                  = "LdapBase"
+	BusinessConfLdapPort                  = "LdapPort"
+	BusinessConfEmailClient               = "EmailClient"
+	BusinessConfEmailServerHost           = "EmailServerHost"
+	BusinessConfEmailServerPort           = "EmailServerPort"
+	BusinessConfEmailSender               = "EmailSender"
+	BusinessConfEmailSenderUserName       = "EmailSenderUserName"
+	BusinessConfEmailSenderPassword       = "EmailSenderPassword"
+	BusinessConfSmsClient                 = "SmsClient"
+	BusinessConfNanHuaSmsAppKey           = "NanHuaSmsAppKey"
+	BusinessConfNanHuaSmsAppSecret        = "NanHuaSmsAppSecret"
+	BusinessConfNanHuaSmsApiHost          = "NanHuaSmsApiHost"
+	BusinessConfLoginSmsTplContent        = "LoginSmsTplContent"
+	BusinessConfLoginEmailTemplateSubject = "LoginEmailTemplateSubject"
+	BusinessConfLoginEmailTemplateContent = "LoginEmailTemplateContent"
+	BusinessConfLdapBindUserSuffix        = "LdapBindUserSuffix"
+	BusinessConfLdapUserFilter            = "LdapUserFilter"
+
 	BusinessConfTencentApiSecretId           = "TencentApiSecretId"           // 腾讯云API-密钥对
 	BusinessConfTencentApiSecretKey          = "TencentApiSecretKey"          // 腾讯云API-密钥对
 	BusinessConfTencentApiRecTaskCallbackUrl = "TencentApiRecTaskCallbackUrl" // 腾讯云API-语音识别回调地址
@@ -56,6 +59,15 @@ const (
 	BusinessConfReportViewUrl                = "ReportViewUrl"                // 报告详情地址
 	BusinessConfEsIndexNameExcel             = "EsIndexNameExcel"             // ES索引名称-表格
 	BusinessConfEsIndexNameDataSource        = "EsIndexNameDataSource"        // ES索引名称-数据源
+	LLMInitConfig                            = "llmInitConfig"
+	KnowledgeBaseName                        = "KnowledgeBaseName"                // 摘要库
+	KnowledgeArticleName                     = "KnowledgeArticleName"             // 原文库
+	BusinessConfEsWechatArticle              = "EsIndexNameWechatArticle"         // ES索引名称-微信文章
+	BusinessConfEsWechatArticleAbstract      = "EsIndexNameWechatArticleAbstract" // ES索引名称-微信文章摘要
+	BusinessConfEsRagQuestion                = "EsIndexNameRagQuestion"           // ES索引名称-知识库问题
+	BusinessConfIsOpenChartExpired           = "IsOpenChartExpired"               // 是否开启图表有效期鉴权/报告禁止复制
+	BusinessConfReportChartExpiredTime       = "ReportChartExpiredTime"           // 图表有效期鉴权时间,单位:分钟
+	BusinessConfOssUrlReplace                = "OssUrlReplace"                    // OSS地址替换-兼容内网客户用
 )
 
 const (
@@ -63,6 +75,7 @@ const (
 	BusinessConfReportApproveTypeOther = "other"
 	BusinessConfClientFlagNanHua       = "nhqh" // 南华标记
 	BusinessConfEmailClientSmtp        = "smtp" // 普通邮箱标记
+
 )
 
 // FromSceneMap 数据源名称与数据源ID的对应关系
@@ -241,6 +254,11 @@ func InitUseMongoConf() {
 	}
 }
 
+type LLMConfig struct {
+	LlmAddress string `json:"llm_server"`
+	LlmModel   string `json:"llm_model"`
+}
+
 func InitBusinessConf() {
 	var e error
 	BusinessConfMap, e = GetBusinessConf()
@@ -254,4 +272,43 @@ func InitBusinessConf() {
 	if BusinessConfMap[BusinessConfEsIndexNameDataSource] != "" {
 		utils.EsDataSourceIndexName = BusinessConfMap[BusinessConfEsIndexNameDataSource]
 	}
+	// ES索引名称
+	if BusinessConfMap[BusinessConfEsWechatArticle] != "" {
+		utils.EsWechatArticleName = BusinessConfMap[BusinessConfEsWechatArticle]
+	}
+	if BusinessConfMap[BusinessConfEsWechatArticleAbstract] != "" {
+		utils.EsWechatArticleAbstractName = BusinessConfMap[BusinessConfEsWechatArticleAbstract]
+	}
+	if BusinessConfMap[BusinessConfEsRagQuestion] != "" {
+		utils.EsRagQuestionName = BusinessConfMap[BusinessConfEsRagQuestion]
+	}
+	confStr := BusinessConfMap[LLMInitConfig]
+	if confStr != "" {
+		var config LLMConfig
+		err := json.Unmarshal([]byte(confStr), &config)
+		if err != nil {
+			utils.FileLog.Error("LLM配置错误")
+		}
+
+		utils.LLM_MODEL = config.LlmModel
+		utils.LLM_SERVER = config.LlmAddress
+	}
+
+	// 图表有效期的过期时间
+	if BusinessConfMap[BusinessConfReportChartExpiredTime] != "" {
+		reportChartExpiredTime, _ := strconv.Atoi(BusinessConfMap[BusinessConfReportChartExpiredTime])
+		if reportChartExpiredTime <= 0 {
+			reportChartExpiredTime = 30
+		}
+		utils.BusinessConfReportChartExpiredTime = time.Duration(reportChartExpiredTime) * time.Minute
+	} else {
+		utils.BusinessConfReportChartExpiredTime = 30 * time.Minute
+	}
+
+}
+
+type OssUrlReplace struct {
+	IsReplace    bool   `description:"是否替换"`
+	OssUrlOrigin string `description:"被替换的资源地址"`
+	OssUrlNew    string `description:"新的资源地址"`
 }

+ 10 - 1
models/cloud_disk_resource.go

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

+ 14 - 12
models/data_manage/base_from_ccf.go

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

+ 292 - 0
models/data_manage/base_from_purang.go

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

+ 139 - 0
models/data_manage/base_from_purang_classify.go

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

+ 309 - 0
models/data_manage/base_from_radish_research_classify.go

@@ -0,0 +1,309 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// BaseFromRadishResearchClassify 萝卜投研原始数据分类表
+type BaseFromRadishResearchClassify struct {
+	BaseFromRadishResearchClassifyId int       `orm:"column(base_from_radish_research_classify_id);pk" gorm:"primaryKey"`
+	ClassifyName                     string    `description:"分类名称"`
+	ParentId                         int       `description:"父级id"`
+	Level                            int       `description:"层级"`
+	Sort                             int       `description:"排序字段,越小越靠前,默认值:10"`
+	SysUserId                        int       `description:"创建人id"`
+	SysUserRealName                  string    `description:"创建人姓名"`
+	LevelPath                        string    `description:"层级路径"`
+	RootId                           int       `description:"顶级分类ID"`
+	UniqueCode                       string    `description:"唯一编码"`
+	ModifyTime                       time.Time `description:"修改时间"`
+	CreateTime                       time.Time `description:"创建时间"`
+}
+
+func (m *BaseFromRadishResearchClassify) TableName() string {
+	return "base_from_radish_research_classify"
+}
+
+type BaseFromRadishResearchClassifyCols struct {
+	PrimaryId       string
+	ClassifyName    string
+	ParentId        string
+	SysUserId       string
+	SysUserRealName string
+	Level           string
+	Sort            string
+	RootId          string
+	LevelPath       string
+	UniqueCode      string
+	CreateTime      string
+	ModifyTime      string
+}
+
+func (m *BaseFromRadishResearchClassify) Cols() BaseFromRadishResearchClassifyCols {
+	return BaseFromRadishResearchClassifyCols{
+		PrimaryId:       "base_from_radish_research_classify_id",
+		ClassifyName:    "classify_name",
+		ParentId:        "parent_id",
+		SysUserId:       "sys_user_id",
+		SysUserRealName: "sys_user_real_name",
+		Level:           "level",
+		Sort:            "sort",
+		RootId:          "root_id",
+		LevelPath:       "level_path",
+		UniqueCode:      "unique_code",
+		CreateTime:      "create_time",
+		ModifyTime:      "modify_time",
+	}
+}
+
+func (m *BaseFromRadishResearchClassify) Create() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Create(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) CreateMulti(items []*BaseFromRadishResearchClassify) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) Update(cols []string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) Remove() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Exec(sql, m.BaseFromRadishResearchClassifyId).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, ids).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetItemById(id int) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *BaseFromRadishResearchClassify) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// GetBaseFromRadishResearchClassify 获取所有分类
+func GetBaseFromRadishResearchClassify() (items []*BaseFromRadishResearchClassify, err error) {
+	sql := ` SELECT * FROM base_from_radish_research_classify ORDER BY parent_id ASC, sort ASC, base_from_radish_research_classify_id ASC`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql).Find(&items).Error
+	return
+}
+
+// BaseFromRadishResearchClassifyItem 萝卜投研数据分类信息
+type BaseFromRadishResearchClassifyItem struct {
+	ClassifyId   int                                   `description:"分类ID"`
+	ClassifyName string                                `description:"分类名称"`
+	ParentId     int                                   `description:"父级id"`
+	Level        int                                   `description:"层级"`
+	Sort         int                                   `description:"排序字段"`
+	LevelPath    string                                `description:"层级路径"`
+	UniqueCode   string                                `description:"唯一编码"`
+	CreateTime   string                                `description:"创建时间"`
+	ModifyTime   string                                `description:"修改时间"`
+	Children     []*BaseFromRadishResearchClassifyItem `description:"子分类"`
+}
+
+func (m *BaseFromRadishResearchClassify) Format2Item() (item *BaseFromRadishResearchClassifyItem) {
+	item = new(BaseFromRadishResearchClassifyItem)
+	item.ClassifyId = m.BaseFromRadishResearchClassifyId
+	item.ClassifyName = m.ClassifyName
+	item.ParentId = m.ParentId
+	item.Level = m.Level
+	item.Sort = m.Sort
+	item.LevelPath = m.LevelPath
+	item.UniqueCode = m.UniqueCode
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+type RadishResearchClassifyAddReq struct {
+	ClassifyName string `description:"分类名称"`
+	ParentId     int    `description:"父级ID, 第一级传0"`
+	Level        int    `description:"层级, 第一级传0, 其余传上一级的层级"`
+}
+
+type RadishResearchClassifyEditReq struct {
+	ClassifyId   int    `description:"分类ID"`
+	ClassifyName string `description:"分类名称"`
+}
+
+func (m *BaseFromRadishResearchClassify) GetSortMax(parentId int) (sort int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COALESCE(Max(%s), 0) AS sort FROM %s WHERE %s = ?`, m.Cols().Sort, m.TableName(), m.Cols().ParentId)
+	err = o.Raw(sql, parentId).Scan(&sort).Error
+	return
+}
+
+type RadishResearchClassifyRemoveReq struct {
+	ClassifyId int `description:"分类ID"`
+}
+
+type BaseFromRadishResearchClassifyListItem struct {
+	NodeType     int                                       `description:"节点类型: 0-分类; 1-指标"`
+	ClassifyId   int                                       `description:"分类ID"`
+	ClassifyName string                                    `description:"分类名称"`
+	IndexId      int                                       `description:"指标ID"`
+	IndexCode    string                                    `description:"指标编码"`
+	IndexName    string                                    `description:"指标名称"`
+	ParentId     int                                       `description:"父级ID"`
+	Level        int                                       `description:"层级"`
+	Sort         int                                       `description:"排序"`
+	UniqueCode   string                                    `description:"唯一编码, 指标的话用indexCode"`
+	Children     []*BaseFromRadishResearchClassifyListItem `description:"子分类"`
+}
+
+type BaseFromRadishResearchClassifyMoveReq struct {
+	ClassifyId       int `description:"分类ID"`
+	ParentClassifyId int `description:"父级分类ID"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类ID"`
+	NextClassifyId   int `description:"下一个兄弟节点分类ID"`
+	ItemId           int `description:"指标ID, 如果指标ID有值,则移动对象为指标,否则认为移动对象为分类"`
+	PrevItemId       int `description:"上一个指标ID"`
+	NextItemId       int `description:"下一个指标ID"`
+}
+
+func GetRadishResearchClassifyById(classifyId int) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT * FROM base_from_radish_research_classify WHERE base_from_radish_research_classify_id = ?`
+	err = o.Raw(sql, classifyId).First(&item).Error
+	return
+}
+
+func GetRadishResearchClassifyByRootIdLevel(rootId int, orderStr string) (items []*BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_classify WHERE root_id = ? `
+	if orderStr != "" {
+		sql += orderStr
+	} else {
+		sql += ` order by level desc, sort asc, base_from_radish_research_classify_id asc`
+	}
+	err = o.Raw(sql, rootId).Find(&items).Error
+	return
+}
+
+// UpdateRadishResearchClassifySortByParentId 根据父类id更新排序
+func UpdateRadishResearchClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` update base_from_radish_research_classify set sort = ` + updateSort + ` WHERE parent_id = ? AND sort > ? `
+	if classifyId > 0 {
+		sql += ` or ( base_from_radish_research_classify_id > ` + fmt.Sprint(classifyId) + ` and sort = ` + fmt.Sprint(nowSort) + `)`
+	}
+	err = o.Exec(sql, parentId, nowSort).Error
+	return
+}
+
+// GetFirstRadishResearchClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
+func GetFirstRadishResearchClassifyByParentId(parentId int) (item *BaseFromRadishResearchClassify, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_classify WHERE parent_id = ? ORDER BY sort ASC,base_from_radish_research_classify_id ASC LIMIT 1`
+	err = o.Raw(sql, parentId).First(&item).Error
+	return
+}
+
+func UpdateRadishResearchClassifyChildByParentClassifyId(classifyIds []int, rootId int, levelStep int) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	var pars []interface{}
+	pars = append(pars, rootId, levelStep)
+	pars = append(pars, classifyIds)
+	// 更新相关联的二级分类的parentId,和classify_name_second
+	sql := `update base_from_radish_research_classify SET root_id = ?, level = level+? where base_from_radish_research_classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+// UpdateLevelPath 更新层级路径
+func (m *BaseFromRadishResearchClassify) UpdateLevelPath(classifyId int, levelPath string) (err error) {
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ? WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().LevelPath, m.Cols().PrimaryId)
+	err = global.DbMap[utils.DbNameIndex].Exec(sql, levelPath, classifyId).Error
+	return
+}
+
+// GetRadishResearchClassifyByParentIdAndName 根据父级ID和名称获取分类
+func GetRadishResearchClassifyByParentIdAndName(parentId int, classifyName string, classifyId int) (item *EdbClassify, err error) {
+	sql := `SELECT * FROM base_from_radish_research_classify WHERE parent_id = ? AND classify_name = ? AND base_from_radish_research_classify_id <> ? LIMIT 1`
+	err = global.DbMap[utils.DbNameIndex].Raw(sql, parentId, classifyName, classifyId).First(&item).Error
+	return
+}

+ 192 - 0
models/data_manage/base_from_radish_research_data.go

@@ -0,0 +1,192 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+)
+
+// BaseFromRadishResearchData 萝卜投研-指标数据
+type BaseFromRadishResearchData struct {
+	BaseFromRadishResearchDataId  int       `orm:"column(base_from_radish_research_data_id);pk" gorm:"primaryKey"`
+	BaseFromRadishResearchIndexId int       `description:"指标ID"`
+	IndexCode                     string    `description:"指标编码"`
+	DataTime                      time.Time `description:"数据日期"`
+	Value                         float64   `description:"数据值"`
+	CreateTime                    time.Time `description:"创建时间"`
+	ModifyTime                    time.Time `description:"修改时间"`
+	DataTimestamp                 int64     `description:"数据日期时间戳"`
+}
+
+func (m *BaseFromRadishResearchData) TableName() string {
+	return "base_from_radish_research_data"
+}
+
+type BaseFromRadishResearchDataCols struct {
+	PrimaryId                     string
+	BaseFromRadishResearchIndexId string
+	IndexCode                     string
+	DataTime                      string
+	Value                         string
+	UniqueCode                    string
+	CreateTime                    string
+	ModifyTime                    string
+	DataTimestamp                 string
+}
+
+func (m *BaseFromRadishResearchData) Cols() BaseFromRadishResearchDataCols {
+	return BaseFromRadishResearchDataCols{
+		PrimaryId:                     "base_from_radish_research_data_id",
+		BaseFromRadishResearchIndexId: "base_from_radish_research_index_id",
+		IndexCode:                     "index_code",
+		DataTime:                      "data_time",
+		Value:                         "value",
+		UniqueCode:                    "unique_code",
+		CreateTime:                    "create_time",
+		ModifyTime:                    "modify_time",
+		DataTimestamp:                 "data_timestamp",
+	}
+}
+
+func (m *BaseFromRadishResearchData) Create() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Create(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) CreateMulti(items []*BaseFromRadishResearchData) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) Update(cols []string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) Remove() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Exec(sql, m.BaseFromRadishResearchDataId).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, ids).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetItemById(id int) (item *BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromRadishResearchData, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// BaseFromRadishResearchDataItem 萝卜投研数据信息
+type BaseFromRadishResearchDataItem struct {
+	DataId        int     `description:"数据ID"`
+	IndexId       int     `description:"指标ID"`
+	IndexCode     string  `description:"指标编码"`
+	DataTime      string  `description:"数据日期"`
+	Value         float64 `description:"数据值"`
+	DataTimestamp int64   `description:"数据日期时间戳"`
+}
+
+func (m *BaseFromRadishResearchData) Format2Item() (item *BaseFromRadishResearchDataItem) {
+	item = new(BaseFromRadishResearchDataItem)
+	item.DataId = m.BaseFromRadishResearchDataId
+	item.IndexId = m.BaseFromRadishResearchIndexId
+	item.IndexCode = m.IndexCode
+	item.DataTime = utils.TimeTransferString(utils.FormatDate, m.DataTime)
+	item.Value = m.Value
+	item.DataTimestamp = m.DataTimestamp
+	return
+}
+
+func (m *BaseFromRadishResearchData) GetDataTimeByIndexIds(indexIds []int) (items []string, err error) {
+	if len(indexIds) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT DISTINCT %s FROM %s WHERE %s IN ? ORDER BY %s DESC`, m.Cols().DataTime, m.TableName(), m.Cols().BaseFromRadishResearchIndexId, m.Cols().DataTime)
+	err = o.Raw(sql, indexIds).Find(&items).Error
+	if err != nil {
+		return
+	}
+	for i, v := range items {
+		items[i] = utils.GormDateStrToDateStr(v)
+	}
+	return
+}

+ 531 - 0
models/data_manage/base_from_radish_research_index.go

@@ -0,0 +1,531 @@
+package data_manage
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+// BaseFromRadishResearchIndex 萝卜投研数据
+type BaseFromRadishResearchIndex struct {
+	BaseFromRadishResearchIndexId int       `orm:"column(base_from_radish_research_index_id);pk" gorm:"primaryKey"`
+	IndexCode                     string    `description:"指标编码"`
+	IndexName                     string    `description:"指标名称"`
+	ClassifyId                    int       `description:"分类ID"`
+	Unit                          string    `description:"单位"`
+	Source                        string    `description:"数据来源"`
+	Frequency                     string    `description:"频度"`
+	StartDate                     time.Time `description:"开始日期"`
+	EndDate                       time.Time `description:"结束日期"`
+	Sort                          int       `description:"排序"`
+	IsStop                        int       `description:"是否停更:0-否;1-停更"`
+	TerminalCode                  string    `description:"所属终端编码"`
+	EdbExist                      int       `description:"指标库是否已添加指标:0-否;1-是"`
+	FilePath                      string    `description:"文件存储路径"`
+	LatestValue                   float64   `description:"数据最新值"`
+	CreateTime                    time.Time `description:"创建时间"`
+	ModifyTime                    time.Time `description:"修改时间"`
+}
+
+func (m *BaseFromRadishResearchIndex) TableName() string {
+	return "base_from_radish_research_index"
+}
+
+type BaseFromRadishResearchIndexCols struct {
+	PrimaryId    string
+	IndexCode    string
+	IndexName    string
+	ClassifyId   string
+	Unit         string
+	Source       string
+	Frequency    string
+	StartDate    string
+	EndDate      string
+	Sort         string
+	IsStop       string
+	TerminalCode string
+	EdbExist     string
+	FilePath     string
+	LatestValue  string
+	CreateTime   string
+	ModifyTime   string
+}
+
+func (m *BaseFromRadishResearchIndex) Cols() BaseFromRadishResearchIndexCols {
+	return BaseFromRadishResearchIndexCols{
+		PrimaryId:    "base_from_radish_research_index_id",
+		IndexCode:    "index_code",
+		IndexName:    "index_name",
+		ClassifyId:   "classify_id",
+		Unit:         "unit",
+		Source:       "source",
+		Frequency:    "frequency",
+		StartDate:    "start_date",
+		EndDate:      "end_date",
+		Sort:         "sort",
+		IsStop:       "is_stop",
+		EdbExist:     "edb_exist",
+		TerminalCode: "terminal_code",
+		FilePath:     "file_path",
+		LatestValue:  "latest_value",
+		CreateTime:   "create_time",
+		ModifyTime:   "modify_time",
+	}
+}
+
+func (m *BaseFromRadishResearchIndex) Create() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Create(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) CreateMulti(items []*BaseFromRadishResearchIndex) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.CreateInBatches(items, utils.MultiAddNum).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) Update(cols []string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	err = o.Select(cols).Updates(m).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) Remove() (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Exec(sql, m.BaseFromRadishResearchIndexId).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) MultiRemove(ids []int) (err error) {
+	if len(ids) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, ids).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) RemoveByCondition(condition string, pars []interface{}) (err error) {
+	if condition == "" {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s`, m.TableName(), condition)
+	err = o.Exec(sql, pars...).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetItemById(id int) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().PrimaryId)
+	err = o.Raw(sql, id).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	order := ``
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).First(&item).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars...).Scan(&count).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+func (m *BaseFromRadishResearchIndex) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := fmt.Sprintf(`ORDER BY %s DESC`, m.Cols().CreateTime)
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	pars = append(pars, startSize, pageSize)
+	err = o.Raw(sql, pars...).Find(&items).Error
+	return
+}
+
+// BaseFromRadishResearchIndexItem 萝卜投研数据信息
+type BaseFromRadishResearchIndexItem struct {
+	IndexId     int     `description:"指标ID"`
+	ClassifyId  int     `description:"分类ID"`
+	IndexCode   string  `description:"指标编码"`
+	IndexName   string  `description:"指标名称"`
+	Unit        string  `description:"单位"`
+	Source      string  `description:"数据来源"`
+	Frequency   string  `description:"频度"`
+	StartDate   string  `description:"开始日期"`
+	EndDate     string  `description:"结束日期"`
+	Sort        int     `description:"排序"`
+	LatestValue float64 `description:"最新值"`
+	EdbExist    int     `description:"是否加入指标:0-未加入;1-已加入"`
+	CreateTime  string  `description:"创建时间"`
+	ModifyTime  string  `description:"修改时间"`
+}
+
+func (m *BaseFromRadishResearchIndex) Format2Item() (item *BaseFromRadishResearchIndexItem) {
+	item = new(BaseFromRadishResearchIndexItem)
+	item.ClassifyId = m.ClassifyId
+	item.IndexId = m.BaseFromRadishResearchIndexId
+	item.IndexCode = m.IndexCode
+	item.IndexName = m.IndexName
+	item.Unit = m.Unit
+	item.Source = m.Source
+	item.Frequency = m.Frequency
+	item.StartDate = utils.TimeTransferString(utils.FormatDate, m.StartDate)
+	item.EndDate = utils.TimeTransferString(utils.FormatDate, m.EndDate)
+	item.Sort = m.Sort
+	item.LatestValue = m.LatestValue
+	item.EdbExist = m.EdbExist
+	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, m.CreateTime)
+	item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, m.ModifyTime)
+	return
+}
+
+// BaseFromRadishResearchIndexDetail 指标详情
+type BaseFromRadishResearchIndexDetail struct {
+	*BaseFromRadishResearchIndexItem
+	DataList []*BaseFromRadishResearchDataItem
+}
+
+func (m *BaseFromRadishResearchIndex) UpdateClassifyMulti(ids []int, classifyId int) (err error) {
+	if len(ids) == 0 || classifyId <= 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ?, %s = NOW() WHERE %s IN (%s)`, m.TableName(), m.Cols().ClassifyId, m.Cols().ModifyTime, m.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
+	err = o.Exec(sql, classifyId, ids).Error
+	return
+}
+
+// RadishResearchSearchEdbReq 搜索指标请求体
+//type RadishResearchSearchEdbReq struct {
+//	StockCode string `form:"StockCode" description:"证券代码" `
+//	EdbCode   string `form:"EdbCode" description:"指标代码"`
+//	StartTime string `form:"StartTime" description:"每日数据开始时间"`
+//	EndTime   string `form:"EndTime" description:"每日数据结束时间"`
+//	Interval  int    `form:"Interval" description:"时间周期"`
+//	Fill      string `form:"Fill" description:"非交易间隔处理"`
+//	CPS       string `form:"CPS" description:"复权方式"`
+//	BaseDate  string `form:"BaseDate" description:"复权基点"`
+//}
+
+// RadishResearchExistCheckResp 指标存在校验响应
+//type RadishResearchExistCheckResp struct {
+//	ExistAll   bool                            `description:"指标是否全部存在: true-是; false-否"`
+//	ExistIndex []RadishResearchExistCheckIndex `description:"已存在的指标信息"`
+//}
+
+// RadishResearchExistCheckIndex 指标存在校验-指标信息
+//type RadishResearchExistCheckIndex struct {
+//	IndexId   int    `description:"指标ID"`
+//	IndexCode string `description:"指标编码"`
+//	IndexName string `description:"指标名称"`
+//}
+
+// RadishResearchSearchEdbResp 响应体
+//type RadishResearchSearchEdbResp struct {
+//	StockCode string                        `description:"证券代码" `
+//	EdbCode   string                        `description:"指标代码"`
+//	IndexName string                        `description:"指标名称"`
+//	Frequency int                           `description:"频度: 1-60"`
+//	IndexData []RadishResearchSearchEdbData `description:"指标数据"`
+//}
+
+//type RadishResearchSearchEdbData struct {
+//	DataTime string
+//	Value    string
+//}
+
+// RadishResearchIndexWithData 萝卜投研指标
+//type RadishResearchIndexWithData struct {
+//	StockCode string                     `description:"证券代码"`
+//	EdbCode   string                     `description:"指标代码"`
+//	IndexData []*RadishResearchIndexData `description:"指标数据"`
+//}
+
+// RadishResearchIndexData 萝卜投研指标数据
+//type RadishResearchIndexData struct {
+//	DataTime time.Time `description:"数据时间"`
+//	Value    float64   `description:"数据值"`
+//}
+
+// RadishResearchIndexDataLibResp 萝卜投研指标-指标库响应
+//type RadishResearchIndexDataLibResp struct {
+//	Ret     int
+//	Msg     string
+//	ErrMsg  string
+//	ErrCode string
+//	Data    []*RadishResearchIndexWithData
+//	Success bool `description:"true 执行成功,false 执行失败"`
+//}
+
+// RadishResearchAddEdbReq 新增指标请求体
+//type RadishResearchAddEdbReq struct {
+//	StartTime string                            `description:"每日数据开始时间"`
+//	EndTime   string                            `description:"每日数据结束时间"`
+//	Interval  int                               `description:"时间周期"`
+//	Fill      string                            `description:"非交易间隔处理"`
+//	CPS       string                            `description:"复权方式"`
+//	BaseDate  string                            `description:"复权基点"`
+//	IndexList []*RadishResearchBaseAddIndexItem `description:"指标信息"`
+//}
+
+//type RadishResearchBaseAddIndexItem struct {
+//	ClassifyId int    `description:"分类ID"`
+//	Unit       string `description:"单位"`
+//	IndexName  string `description:"指标名称"`
+//	Frequency  string `description:"频度"`
+//	StockCode  string `description:"证券代码"`
+//	EdbCode    string `description:"指标代码"`
+//}
+
+//type RadishResearchBaseAddReq struct {
+//	StartTime                      string `description:"每日数据开始时间"`
+//	EndTime                        string `description:"每日数据结束时间"`
+//	Interval                       int    `description:"时间周期"`
+//	Fill                           string `description:"非交易间隔处理"`
+//	CPS                            string `description:"复权方式"`
+//	BaseDate                       string `description:"复权基点"`
+//	SysAdminId                     int    `description:"创建人ID"`
+//	SysAdminName                   string `description:"创建人姓名"`
+//	RadishResearchBaseAddIndexItem `description:"指标信息"`
+//}
+
+// RadishResearchIndexEditReq 编辑指标请求
+type RadishResearchIndexEditReq struct {
+	IndexId    int `description:"指标ID"`
+	ClassifyId int `description:"分类ID"`
+}
+
+// RadishResearchIndexRemoveReq 删除指标请求
+type RadishResearchIndexRemoveReq struct {
+	IndexId int `description:"指标ID"`
+}
+
+// RadishResearchIndexListChoiceReq 指标列表选择请求
+type RadishResearchIndexListChoiceReq struct {
+	ClassifyId   string `form:"ClassifyId" description:"分类ID(多选)"`
+	IncludeChild bool   `form:"IncludeChild" description:"是否包含子分类"`
+	Frequency    string `form:"Frequency" description:"频度(多选)"`
+	SysAdminId   string `form:"SysAdminId" description:"创建人ID(多选)"`
+	Keywords     string `form:"Keywords" description:"关键词: 指标ID/指标名称"`
+	ListIds      string `form:"ListIds" description:"列表选择项/排除项(全选为true时为排除项)"`
+	SelectAll    bool   `form:"SelectAll" description:"是否全选: true/false"`
+}
+
+// RadishResearchIndexListChoiceItem 指标列表选择响应
+type RadishResearchIndexListChoiceItem struct {
+	IndexId   int    `description:"指标ID"`
+	IndexCode string `description:"指标编码"`
+	IndexName string `description:"指标名称"`
+}
+
+// RadishResearchIndexMultiOptReq 指标批量操作请求
+type RadishResearchIndexMultiOptReq struct {
+	IndexIds       []int `description:"指标IDs"`
+	OptType        int   `description:"操作类型: 1-移动分类; 2-删除; 3-刷新"`
+	MoveClassifyId int   `description:"移动至分类ID"`
+	RefreshType    int   `description:"刷新类型: 1-最近6小时; 2-全部刷新"`
+}
+
+// RadishResearchIndexListForm 指标列表表单
+type RadishResearchIndexListForm struct {
+	PageSize       int    `form:"PageSize" description:"每页数据量"`
+	CurrentIndex   int    `form:"CurrentIndex" description:"页码"`
+	ClassifyId     int    `form:"ClassifyId" description:"分类ID(查询结果包含所有子分类指标)"`
+	ClassifyIds    string `form:"ClassifyIds" description:"分类IDs"`
+	IgnoreEdbExist bool   `form:"IgnoreEdbExist" description:"忽略已加入指标库的"`
+	Frequencies    string `form:"Frequencies" description:"频度(多选)"`
+	Keyword        string `form:"Keyword" description:"关键词-指标ID/指标名称"`
+}
+
+type RadishResearchIndexPageListResp struct {
+	List   []*BaseFromRadishResearchIndexItem
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+func GetRadishResearchIndexById(indexId int) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_index WHERE base_from_radish_research_index_id = ?`
+	err = o.Raw(sql, indexId).First(&item).Error
+	return
+}
+
+// UpdateRadishResearchIndexSortByClassifyId 根据分类id更新排序
+func UpdateRadishResearchIndexSortByClassifyId(classifyId, nowSort int, prevEdbInfoId int, updateSort string) (err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` update base_from_radish_research_index set sort = ` + updateSort + ` WHERE classify_id=?`
+	if prevEdbInfoId > 0 {
+		sql += ` AND ( sort > ? or ( base_from_radish_research_index_id > ` + fmt.Sprint(prevEdbInfoId) + ` and sort=` + fmt.Sprint(nowSort) + ` )) `
+	} else {
+		sql += ` AND ( sort > ? )`
+	}
+	err = o.Exec(sql, classifyId, nowSort).Error
+	return
+}
+
+// GetRadishResearchIndexMaxSortByClassifyId 获取分类下指标的最大的排序数
+func GetRadishResearchIndexMaxSortByClassifyId(classifyId int) (sort int, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := `SELECT COALESCE(MAX(sort), 0) AS sort FROM base_from_radish_research_index WHERE classify_id = ?`
+	err = o.Raw(sql, classifyId).Scan(&sort).Error
+	return
+}
+
+// GetFirstRadishResearchIndexByClassifyId 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstRadishResearchIndexByClassifyId(classifyId int) (item *BaseFromRadishResearchIndex, err error) {
+	o := global.DbMap[utils.DbNameIndex]
+	sql := ` SELECT * FROM base_from_radish_research_index WHERE classify_id = ? ORDER BY sort ASC,base_from_radish_research_index_id ASC LIMIT 1`
+	err = o.Raw(sql, classifyId).First(&item).Error
+	return
+}
+
+type RadishResearchIndexConvert2EdbRule struct {
+	ConvertType  int `description:"转换类型: 1-指定时间值; 2-区间计算值"`
+	ConvertFixed struct {
+		FixedDay  int    `description:"指定时间值日期: 1-当日; 2-前一日"`
+		FixedTime string `description:"指定时间值时点(HH:mm:ss)"`
+	} `description:"指定时间值"`
+	ConvertArea struct {
+		StartDay      int    `description:"起始时间日期: 1-当日; 2-前一日"`
+		StartTime     string `description:"起始时间时点(HH:mm:ss)"`
+		EndDay        int    `description:"截止时间日期: 1-当日; 2-前一日"`
+		EndTime       string `description:"截止时间时点(HH:mm:ss)"`
+		CalculateType int    `description:"计算类型: 1-区间均值; 2-最大值; 3-最小值"`
+	} `description:"区间计算值"`
+}
+
+// RadishResearchIndexMultiSave2EdbPreReq 批量添加指标库请求
+type RadishResearchIndexMultiSave2EdbPreReq struct {
+	ConvertRule RadishResearchIndexConvert2EdbRule
+	IndexIds    []int `description:"指标IDs"`
+}
+
+type RadishResearchIndexMultiSave2EdbReq struct {
+	ConvertRule RadishResearchIndexConvert2EdbRule
+	NewIndexes  []*RadishResearchIndexMultiSave2EdbPreItem `description:"新增指标"`
+}
+
+type RadishResearchIndexMultiSave2EdbLibReq struct {
+	ConvertRule RadishResearchIndexConvert2EdbRule
+	NewIndex    *RadishResearchIndexMultiSave2EdbPreItem `description:"新增指标"`
+}
+
+// RadishResearchIndexMultiSave2EdbPreItem 批量新增指标库信息
+type RadishResearchIndexMultiSave2EdbPreItem struct {
+	IndexId      int    `description:"指标ID"`
+	IndexCode    string `description:"指标编码"`
+	IndexName    string `description:"原指标名称"`
+	NewIndexName string `description:"新指标名称"`
+	StockCode    string `description:"证券代码"`
+	EdbCode      string `description:"指标代码"`
+	Unit         string `description:"单位"`
+	Frequency    string `description:"原频度"`
+	NewFrequency string `description:"新频度(固定日度)"`
+	ClassifyId   int    `description:"指标库分类ID"`
+	SysAdminId   int    `description:"创建人ID"`
+	SysAdminName string `description:"创建人姓名"`
+	Tips         string `description:"提示信息"`
+	ErrMsg       string `description:"错误信息"`
+}
+
+type RadishResearchIndexMultiSave2EdbResp struct {
+	Exist   []*RadishResearchIndexMultiSave2EdbPreItem `description:"已存在的指标"`
+	Success []*RadishResearchIndexMultiSave2EdbPreItem `description:"添加成功的指标"`
+	Fail    []*RadishResearchIndexMultiSave2EdbPreItem `description:"添加失败的指标"`
+}
+
+type RadishResearchIndexMultiOptResp struct {
+	Success []*RadishResearchIndexBaseInfo `description:"操作成功的指标"`
+	Fail    []*RadishResearchIndexBaseInfo `description:"操作失败的指标"`
+}
+
+type RadishResearchIndexBaseInfo struct {
+	IndexId   int    `description:"指标ID"`
+	IndexCode string `description:"指标编码"`
+	IndexName string `description:"指标名称"`
+}
+
+func (m *BaseFromRadishResearchIndex) RemoveIndexAndData(indexCode string) (err error) {
+	tx := global.DbMap[utils.DbNameIndex].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+			return
+		}
+		_ = tx.Commit()
+	}()
+
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.Cols().IndexCode)
+	e := tx.Exec(sql, indexCode).Error
+	if e != nil {
+		err = fmt.Errorf("delete index err: %s", e.Error())
+		return
+	}
+
+	dataOb := new(BaseFromRadishResearchData)
+	sql = fmt.Sprintf(`DELETE FROM %s WHERE %s = ?`, dataOb.TableName(), dataOb.Cols().IndexCode)
+	e = tx.Exec(sql, indexCode).Error
+	if e != nil {
+		err = fmt.Errorf("delete index data err: %s", e.Error())
+		return
+	}
+	return
+}
+
+// RadishResearchIndexSelectReq 批量加入指标-选择指标
+type RadishResearchIndexSelectReq struct {
+	ClassifyIds []int    `description:"分类IDs"`
+	Frequencies []string `description:"频度"`
+	Keyword     string   `description:"关键词-指标ID/指标名称"`
+	IndexCodes  []string `description:"SelectAll-true: IndexCodes为排除的指标, SelectAll-false: IndexCodes为选择的指标"`
+	SelectAll   bool     `description:"列表全选"`
+}
+
+// UpdateEdbExists 更新是否加入指标库字段
+func (m *BaseFromRadishResearchIndex) UpdateEdbExists(exist int, indexCodes []string) (err error) {
+	if len(indexCodes) == 0 {
+		return
+	}
+	o := global.DbMap[utils.DbNameIndex]
+	sql := fmt.Sprintf(`UPDATE %s SET %s = ? WHERE %s IN ?`, m.TableName(), m.Cols().EdbExist, m.Cols().IndexCode)
+	err = o.Exec(sql, exist, indexCodes).Error
+	return
+}

+ 27 - 0
models/data_manage/base_from_sci_hq_classify.go

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

+ 10 - 4
models/data_manage/base_from_sci_hq_index.go

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

+ 88 - 10
models/data_manage/chart_classify.go

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

+ 81 - 0
models/data_manage/chart_description.go

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

+ 6 - 0
models/data_manage/chart_edb_mapping.go

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

+ 7 - 4
models/data_manage/chart_info.go

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

+ 1 - 1
models/data_manage/edb_data_base.go

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

+ 1 - 2
models/data_manage/edb_info_relation.go

@@ -3,10 +3,9 @@ package data_manage
 import (
 	"eta/eta_api/global"
 	"eta/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"gorm.io/gorm"
 	"time"
-
-	"github.com/rdlucklib/rdluck_tools/paging"
 )
 
 type EdbInfoRelation struct {

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

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

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

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

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

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

+ 48 - 0
models/data_manage/forum_chart_edb_mapping.go

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

+ 51 - 13
models/data_manage/mysteel_chemical_index.go

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

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

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

+ 20 - 0
models/english_report.go

@@ -51,6 +51,8 @@ type EnglishReport struct {
 	ApproveId          int       `description:"审批ID"`
 	DetailImgUrl       string    `description:"报告详情长图地址"`
 	DetailPdfUrl       string    `description:"报告详情PDF地址"`
+	DetailImgUrlMobile string    `description:"报告详情长图地址-手机端"`
+	DetailPdfUrlMobile string    `description:"报告详情PDF地址-手机端"`
 	EmailHasFail       int       `description:"是否存在邮件发送失败的记录: 0-否; 1-是"`
 }
 
@@ -324,6 +326,8 @@ type EnglishReportList struct {
 	ApproveTime        string    `description:"审批时间"`
 	DetailImgUrl       string    `description:"报告详情长图地址"`
 	DetailPdfUrl       string    `description:"报告详情PDF地址"`
+	DetailImgUrlMobile string    `description:"报告详情长图地址-手机端"`
+	DetailPdfUrlMobile string    `description:"报告详情PDF地址-手机端"`
 }
 
 type EnglishReportListResp struct {
@@ -1043,6 +1047,20 @@ func ModifyEnglishReportImgUrl(reportId int, detailImgUrl string) (err error) {
 	return
 }
 
+func ModifyEnglishReportPdfUrlMobile(reportId int, detailPdfUrlMobile string) (err error) {
+	o := global.DbMap[utils.DbNameReport]
+	sql := `UPDATE english_report SET detail_pdf_url_mobile=? WHERE id=? `
+	err = o.Exec(sql, detailPdfUrlMobile, reportId).Error
+	return
+}
+
+func ModifyEnglishReportImgUrlMobile(reportId int, detailImgUrlMobile string) (err error) {
+	o := global.DbMap[utils.DbNameReport]
+	sql := `UPDATE english_report SET detail_img_url_mobile=? WHERE id=? `
+	err = o.Exec(sql, detailImgUrlMobile, reportId).Error
+	return
+}
+
 func FormatEnglishReport2ListItem(origin *EnglishReport) (item *EnglishReportList) {
 	if origin == nil {
 		return
@@ -1087,6 +1105,8 @@ func FormatEnglishReport2ListItem(origin *EnglishReport) (item *EnglishReportLis
 	item.ApproveTime = utils.TimeTransferString(utils.FormatDateTime, origin.ApproveTime)
 	item.DetailImgUrl = origin.DetailImgUrl
 	item.DetailPdfUrl = origin.DetailPdfUrl
+	item.DetailImgUrlMobile = origin.DetailImgUrlMobile
+	item.DetailPdfUrlMobile = origin.DetailPdfUrlMobile
 	return
 }
 

+ 40 - 0
models/llm/user_chat_record.go

@@ -0,0 +1,40 @@
+package llm
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"time"
+)
+
+// UserChatRecord 定义用户聊天记录结构体
+type UserChatRecord struct {
+	Id           int       `gorm:"primaryKey;autoIncrement;comment:主键"`
+	ChatId       int       `gorm:"chat_id;comment:会话id"`
+	ChatUserType string    `gorm:"type:enum('user','assistant');comment:用户方"`
+	Content      string    `gorm:"content:内容"`
+	SendTime     time.Time `gorm:"comment:发送时间"`
+	CreatedTime  time.Time `gorm:"comment:创建时间"`
+	UpdateTime   time.Time `gorm:"autoUpdateTime;comment:更新时间"`
+}
+type UserChatRecordRedis struct {
+	Id           int
+	ChatId       int
+	ChatUserType string
+	Content      string
+	SendTime     string
+}
+
+func (u *UserChatRecord) TableName() string {
+	return "user_chat_record"
+}
+
+func BatchInsertRecords(list []*UserChatRecord) (err error) {
+	o := global.DbMap[utils.DbNameAI]
+	err = o.Clauses(clause.OnConflict{
+		Columns:   []clause.Column{{Name: "chat_id"}, {Name: "chat_user_type"}, {Name: "send_time"}},
+		DoUpdates: clause.Assignments(map[string]interface{}{"update_time": gorm.Expr("VALUES(update_time)")}),
+	}).CreateInBatches(list, utils.MultiAddNum).Error
+	return
+}

+ 73 - 0
models/llm/user_llm_chat.go

@@ -0,0 +1,73 @@
+package llm
+
+import (
+	"errors"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"time"
+)
+
+type UserLlmChat struct {
+	Id          int       `gorm:"primaryKey;autoIncrement;comment:会话主键"`
+	UserId      int       `gorm:"comment:用户id"`
+	ChatTitle   string    `gorm:"comment:会话标题"`
+	IsDeleted   int       `gorm:"comment:是否删除"`
+	CreatedTime time.Time `gorm:"comment:创建时间"`
+	UpdateTime  time.Time `gorm:"autoUpdateTime;comment:更新时间"`
+}
+
+
+type UserLlmChatListViewItem struct {
+	Id          int    `gorm:"primaryKey;autoIncrement;comment:会话主键"`
+	UserId      int    `gorm:"comment:用户id"`
+	ChatTitle   string `gorm:"comment:会话标题"`
+	CreatedTime string `gorm:"comment:创建时间"`
+	RecordCount int    `gorm:"comment:会话记录数"`
+}
+
+func CovertItemToView(item UserLlmChat) UserLlmChatListViewItem {
+	return UserLlmChatListViewItem{
+		Id:          item.Id,
+		UserId:      item.UserId,
+		ChatTitle:   item.ChatTitle,
+		CreatedTime: item.CreatedTime.Format(utils.FormatDateTime),
+	}
+
+}
+func (u *UserLlmChat) TableName() string {
+	return "user_llm_chat"
+}
+func (u *UserLlmChat) CreateChatSession() (chatId int, err error) {
+	o := global.DbMap[utils.DbNameAI]
+	err = o.Create(u).Error
+	if err != nil {
+		return
+	}
+	chatId = u.Id
+	return
+}
+func (u *UserLlmChat) RenameChatSession() (err error) {
+	o := global.DbMap[utils.DbNameAI]
+	var exists bool
+	err = o.Model(&u).Select("1").Where("id = ?", u.Id).Scan(&exists).Error
+	if err != nil {
+		return
+	}
+	if !exists {
+		err = errors.New("当前会话不存在")
+		return
+	}
+	err = o.Select("chat_title").Updates(u).Error
+	return
+}
+func (u *UserLlmChat) DeleteChatSession() (err error) {
+	o := global.DbMap[utils.DbNameAI]
+	err = o.Select("is_deleted").Updates(u).Error
+	return
+}
+func GetUserChatList(userId int, monDay, toDay string) (chatList []UserLlmChat, err error) {
+	o := global.DbMap[utils.DbNameAI]
+	sql := `select ulc.id AS id ,ulc.user_id as user_id,ulc.chat_title as chat_title,ulc.created_time from user_llm_chat ulc  where ulc.user_id=? and ` + utils.GenerateQuerySql(utils.ToDate, &utils.QueryParam{Column: "ulc.created_time"}) + ` BETWEEN ? and ? AND is_deleted=0 GROUP BY ulc.id order by ulc.created_time desc`
+	err = o.Raw(sql, userId, monDay, toDay).Find(&chatList).Error
+	return
+}

+ 1 - 1
models/ppt_english/ppt_english_group_mapping.go

@@ -68,7 +68,7 @@ func GetPptMappingByPptId(pptId int64) (item *PptEnglishGroupMapping, err error)
 	return
 }
 
-// GetPptMappingListByGroupId 查询目录下,ppt列表, 降序排列
+// GetPptMappingListByGroupIdDesc 查询目录下,ppt列表, 降序排列
 func GetPptMappingListByGroupIdDesc(groupId int64) (list []*PptEnglishGroupMapping, err error) {
 	o := global.DbMap[utils.DbNameReport]
 	sql := `select group_ppt_id, group_id, ppt_id, ppt_sort, admin_id, admin_real_name, create_time from ppt_english_group_mapping where group_id=? order by ppt_sort desc, group_ppt_id desc `

+ 37 - 0
models/rag/article_kb_mapping.go

@@ -0,0 +1,37 @@
+package rag
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"gorm.io/gorm/clause"
+	"time"
+)
+
+type ArticleKbMapping struct {
+	Id              int `gorm:"id;primaryKey"`
+	WechatArticleId int
+	KbId            string
+	CreatedTime     time.Time
+	UpdateTime      time.Time
+}
+
+func (a *ArticleKbMapping) TableName() string {
+	return "article_kb_mapping"
+}
+
+func GetArticleKbMapping(articleId int) (articleKbMapping *ArticleKbMapping, err error) {
+	err = global.DbMap[utils.DbNameAI].Where("wechat_article_id = ?", articleId).First(&articleKbMapping).Error
+	return
+}
+
+func CreateArticleKbMapping(articleKbMapping ArticleKbMapping) (err error) {
+	db := global.DbMap[utils.DbNameAI]
+	db.Clauses(
+		clause.OnConflict{
+			DoNothing: true,
+			Columns:   []clause.Column{{Name: "wechat_article_id"}},
+		},
+	)
+	err = global.DbMap[utils.DbNameAI].Create(&articleKbMapping).Error
+	return
+}

+ 68 - 0
models/rag/promote_train_record.go

@@ -0,0 +1,68 @@
+package rag
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"time"
+)
+
+type PromoteTrainRecord struct {
+	Id              int       `gorm:"id;primaryKey"`
+	Title           string    `gorm:"title"`
+	Llm             string    `gorm:"llm"`
+	WechatArticleId int       `gorm:"wechat_article_id"`
+	TemplatePromote string    `gorm:"template_promote"`
+	PromoteSendTime time.Time `gorm:"promote_send_time"`
+	AigcContent     string    `gorm:"aigc_content"`
+	AigcSendTime    time.Time `gorm:"aigc_send_time"`
+	IsDeleted       bool      `gorm:"is_deleted"`
+	CreatedTime     time.Time `gorm:"created_time"`
+	UpdateTime      time.Time `gorm:"update_time"`
+}
+
+func (p *PromoteTrainRecord) ToView() *PromoteTrainRecordView {
+	return &PromoteTrainRecordView{
+		Id:              p.Id,
+		Title:           p.Title,
+		Llm:             p.Llm,
+		WechatArticleId: p.WechatArticleId,
+		TemplatePromote: p.TemplatePromote,
+		PromoteSendTime: p.PromoteSendTime.Format(utils.FormatDateTime),
+		AigcContent:     p.AigcContent,
+		AigcSendTime:    p.AigcSendTime.Format(utils.FormatDateTime),
+	}
+}
+
+type PromoteTrainRecordView struct {
+	Id              int
+	Title           string
+	Llm             string
+	WechatArticleId int
+	TemplatePromote string
+	PromoteSendTime string
+	AigcContent     string
+	AigcSendTime    string
+}
+
+func (p *PromoteTrainRecord) TableName() string {
+	return "promote_train_record"
+}
+
+func (p *PromoteTrainRecord) SaveContent() error {
+	return global.DbMap[utils.DbNameAI].Create(p).Error
+}
+func DeleteContent(id int) error {
+	return global.DbMap[utils.DbNameAI].Model(&PromoteTrainRecord{}).Where("id = ?", id).Update("is_deleted", true).Error
+}
+
+func GetRecordList(wechatArticleId int) (list []*PromoteTrainRecordView, err error) {
+	var ormList []PromoteTrainRecord
+	err = global.DbMap[utils.DbNameAI].Model(&PromoteTrainRecord{}).Where("wechat_article_id = ? and is_deleted=?", wechatArticleId, false).Order(`created_time DESC`).Find(&ormList).Error
+	if err != nil {
+		return
+	}
+	for _, item := range ormList {
+		list = append(list, item.ToView())
+	}
+	return
+}

+ 161 - 0
models/rag/question.go

@@ -0,0 +1,161 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type Question struct {
+	QuestionId      int       `gorm:"column:question_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"question_id"`
+	QuestionTitle   string    `gorm:"column:question_title;type:varchar(255);comment:问题标题;" description:"问题标题"`
+	QuestionContent string    `gorm:"column:question_content;type:varchar(255);comment:问题内容;" description:"问题内容"`
+	Sort            int       `gorm:"column:sort;type:int(11);comment:排序;default:0;" description:"排序"`
+	SysUserId       int       `gorm:"column:sys_user_id;type:int(11);comment:添加人id;default:0;" description:"添加人id"`
+	SysUserRealName string    `gorm:"column:sys_user_real_name;type:varchar(255);comment:添加人真实名称;" description:"添加人真实名称"`
+	ModifyTime      time.Time `gorm:"column:modify_time;type:datetime;default:NULL;" description:"modify_time"`
+	CreateTime      time.Time `gorm:"column:create_time;type:datetime;default:NULL;" description:"create_time"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *Question) TableName() string {
+	return "question"
+}
+
+// QuestionColumns get sql column name.获取数据库列名
+var QuestionColumns = struct {
+	QuestionID      string
+	QuestionTitle   string
+	QuestionContent string
+	Sort            string
+	ModifyTime      string
+	CreateTime      string
+}{
+	QuestionID:      "question_id",
+	QuestionTitle:   "question_title",
+	QuestionContent: "question_content",
+	Sort:            "sort",
+	ModifyTime:      "modify_time",
+	CreateTime:      "create_time",
+}
+
+func (m *Question) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *Question) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *Question) Del() (err error) {
+	err = global.DbMap[utils.DbNameAI].Delete(&m).Error
+
+	return
+}
+
+type QuestionView struct {
+	QuestionId      int    `gorm:"column:question_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"question_id"`
+	QuestionTitle   string `gorm:"column:question_title;type:varchar(255);comment:问题标题;" description:"问题标题"`
+	QuestionContent string `gorm:"column:question_content;type:varchar(255);comment:问题内容;" description:"问题内容"`
+	Sort            int    `gorm:"column:sort;type:int(11);comment:排序;default:0;" description:"排序"`
+	SysUserId       int    `gorm:"column:sys_user_id;type:int(11);comment:添加人id;default:0;" description:"添加人id"`
+	SysUserRealName string `gorm:"column:sys_user_real_name;type:varchar(255);comment:添加人真实名称;" description:"添加人真实名称"`
+	ModifyTime      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"`
+}
+
+func (m *Question) ToView() QuestionView {
+	var modifyTime, createTime string
+
+	if !m.CreateTime.IsZero() {
+		createTime = m.CreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.ModifyTime.IsZero() {
+		modifyTime = m.ModifyTime.Format(utils.FormatDateTime)
+	}
+	return QuestionView{
+		QuestionId:      m.QuestionId,
+		QuestionTitle:   m.QuestionTitle,
+		QuestionContent: m.QuestionContent,
+		Sort:            m.Sort,
+		SysUserId:       m.SysUserId,
+		SysUserRealName: m.SysUserRealName,
+		ModifyTime:      modifyTime,
+		CreateTime:      createTime,
+	}
+}
+
+func (m *Question) ListToViewList(list []*Question) (wechatArticleViewList []QuestionView) {
+	wechatArticleViewList = make([]QuestionView, 0)
+
+	for _, v := range list {
+		wechatArticleViewList = append(wechatArticleViewList, v.ToView())
+	}
+	return
+}
+
+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
+
+	return
+}
+
+func (m *Question) GetByCondition(condition string, pars []interface{}) (item *Question, 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 *Question) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*Question, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s order by question_id desc LIMIT ?,?`, field, m.TableName(), condition)
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
+func (m *Question) 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
+}
+
+func (m *Question) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*Question, err error) {
+
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(``, condition, pars, startSize, pageSize)
+	}
+
+	return
+}
+
+func (m *Question) GetTitlePageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*Question, err error) {
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(`question_id,question_title,sort`, condition, pars, startSize, pageSize)
+	}
+
+	return
+}

+ 33 - 0
models/rag/request/wechat_platform.go

@@ -0,0 +1,33 @@
+package request
+
+type AddWechatPlatformReq struct {
+	Name      string `description:"公众号名称"`
+	Link      string `description:"公众号文章链接"`
+	TagIdList []int  `description:"标签列表"`
+}
+
+type OpWechatPlatformReq struct {
+	Status           int `description:"0:禁用,1:启用"`
+	WechatPlatformId int `description:"公众号id"`
+}
+
+type RefreshWechatPlatformReq struct {
+	WechatPlatformId int `description:"公众号id"`
+}
+
+type AddQuestionReq struct {
+	Content string `description:"公众号名称"`
+}
+
+type EditQuestionReq struct {
+	QuestionId int    `description:"问题id"`
+	Content    string `description:"公众号名称"`
+}
+
+type BeachOpAbstractReq struct {
+	WechatArticleAbstractIdList    []int  `description:"摘要id"`
+	NotWechatArticleAbstractIdList []int  `description:"不需要的摘要id"`
+	KeyWord                        string `description:"关键字"`
+	TagId                          int    `description:"标签id"`
+	IsSelectAll                    bool   `description:"是否选择所有摘要"`
+}

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

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

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

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

+ 30 - 0
models/rag/response/wechat_platform.go

@@ -0,0 +1,30 @@
+package response
+
+import (
+	"eta/eta_api/models/rag"
+	"github.com/rdlucklib/rdluck_tools/paging"
+)
+
+type WechatPlatformListResp struct {
+	List   []*rag.WechatPlatform
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type WechatPlatformPublicListResp struct {
+	UserId int    `description:"用户id"`
+	Name   string `description:"研究员名称"`
+	List   []*rag.UserFollowWechatPlatform
+	//Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type TagListResp struct {
+	List   []*rag.Tag
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+type WechatArticleListListResp struct {
+	UserId int    `description:"用户id"`
+	Name   string `description:"研究员名称"`
+	List   []rag.WechatArticleView
+	Paging *paging.PagingItem `description:"分页数据"`
+}

+ 83 - 0
models/rag/tag.go

@@ -0,0 +1,83 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// Tag 品种标签
+type Tag struct {
+	TagId      int       `gorm:"column:tag_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"tag_id"`
+	TagName    string    `gorm:"column:tag_name;type:varchar(255);comment:标签名称;" description:"tag_name"`                // 标签名称
+	Sort       int       `gorm:"column:sort;type:int(9);comment:排序字段;default:0;" description:"sort"`                    // 排序字段
+	ModifyTime time.Time `gorm:"column:modify_time;type:datetime;comment:修改时间;default:NULL;" description:"modify_time"` // 修改时间
+	CreateTime time.Time `gorm:"column:create_time;type:datetime;comment:添加时间;default:NULL;" description:"create_time"` // 添加时间
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *Tag) TableName() string {
+	return "tag"
+}
+
+// TagColumns get sql column name.获取数据库列名
+var TagColumns = struct {
+	TagID      string
+	TagName    string
+	Sort       string
+	ModifyTime string
+	CreateTime string
+}{
+	TagID:      "tag_id",
+	TagName:    "tag_name",
+	Sort:       "sort",
+	ModifyTime: "modify_time",
+	CreateTime: "create_time",
+}
+
+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
+
+	return
+}
+
+func (m *Tag) GetByCondition(condition string, pars []interface{}) (item *Tag, 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 *Tag) GetListByCondition(condition string, pars []interface{}, startSize, pageSize int) (items []*Tag, err error) {
+	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT ?,?`, m.TableName(), condition)
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
+func (m *Tag) 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
+}
+
+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
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(condition, pars, startSize, pageSize)
+	}
+
+	return
+}

+ 307 - 0
models/rag/wechat_article.go

@@ -0,0 +1,307 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type WechatArticle struct {
+	WechatArticleId   int       `gorm:"column:wechat_article_id;type:int(10) UNSIGNED;primaryKey;not null;" description:""`
+	WechatPlatformId  int       `gorm:"column:wechat_platform_id;type:int(11);comment:归属公众号id;default:0;" description:"归属公众号id"`
+	FakeId            string    `gorm:"column:fake_id;type:varchar(255);comment:公众号唯一id;" description:"公众号唯一id"`
+	Title             string    `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
+	Link              string    `gorm:"column:link;type:varchar(255);comment:链接;" description:"链接"`
+	CoverUrl          string    `gorm:"column:cover_url;type:varchar(255);comment:公众号封面;" description:"公众号封面"`
+	Description       string    `gorm:"column:description;type:varchar(255);comment:描述;" description:"描述"`
+	Content           string    `gorm:"column:content;type:longtext;comment:报告详情;" description:"报告详情"`
+	TextContent       string    `gorm:"column:text_content;type:text;comment:文本内容;" description:"文本内容"`
+	AbstractStatus    int       `gorm:"column:abstract_status;type:tinyint(4);comment:摘要生成情况,-1:生成失败,0:待生成,1:已生成;default:0;" description:"摘要生成情况,-1:生成失败,0:待生成,1:已生成"`
+	Country           string    `gorm:"column:country;type:varchar(255);comment:国家;" description:"国家"`
+	Province          string    `gorm:"column:province;type:varchar(255);comment:省;" description:"省"`
+	City              string    `gorm:"column:city;type:varchar(255);comment:市;" description:"市"`
+	ArticleCreateTime time.Time `gorm:"column:article_create_time;type:datetime;comment:报告创建时间;default:NULL;" description:"报告创建时间"`
+	VectorKey         string    `gorm:"column:vector_key;type:varchar(255);comment:向量key标识;" description:"向量key标识"`
+	IsDeleted         int       `gorm:"column:is_deleted;type:tinyint(4);comment:是否删除,0:未删除,1: 已删除;default:0;" description:"是否删除,0:未删除,1: 已删除"`
+	ModifyTime        time.Time `gorm:"column:modify_time;type:datetime;comment:修改时间;default:NULL;" description:"修改时间"`
+	CreateTime        time.Time `gorm:"column:create_time;type:datetime;comment:入库时间;default:NULL;" description:"入库时间"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *WechatArticle) TableName() string {
+	return "wechat_article"
+}
+
+// WechatArticleColumns get sql column name.获取数据库列名
+var WechatArticleColumns = struct {
+	WechatArticleID   string
+	WechatPlatformID  string
+	FakeID            string
+	Title             string
+	Link              string
+	CoverURL          string
+	Description       string
+	Content           string
+	TextContent       string
+	AbstractStatus    string
+	Country           string
+	Province          string
+	City              string
+	ArticleCreateTime string
+	IsDeleted         string
+	ModifyTime        string
+	CreateTime        string
+}{
+	WechatArticleID:   "wechat_article_id",
+	WechatPlatformID:  "wechat_platform_id",
+	FakeID:            "fake_id",
+	Title:             "title",
+	Link:              "link",
+	CoverURL:          "cover_url",
+	Description:       "description",
+	Content:           "content",
+	TextContent:       "text_content",
+	AbstractStatus:    "abstract_status",
+	Country:           "country",
+	Province:          "province",
+	City:              "city",
+	ArticleCreateTime: "article_create_time",
+	IsDeleted:         "is_deleted",
+	ModifyTime:        "modify_time",
+	CreateTime:        "create_time",
+}
+
+type WechatArticleView struct {
+	WechatArticleId            int    `gorm:"column:wechat_article_id;type:int(10) UNSIGNED;primaryKey;not null;" description:""`
+	WechatPlatformId           int    `gorm:"column:wechat_platform_id;type:int(11);comment:归属公众号id;default:0;" description:"归属公众号id"`
+	FakeId                     string `gorm:"column:fake_id;type:varchar(255);comment:公众号唯一id;" description:"公众号唯一id"`
+	Title                      string `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
+	Link                       string `gorm:"column:link;type:varchar(255);comment:链接;" description:"链接"`
+	CoverUrl                   string `gorm:"column:cover_url;type:varchar(255);comment:公众号封面;" description:"公众号封面"`
+	Description                string `gorm:"column:description;type:varchar(255);comment:描述;" description:"描述"`
+	Content                    string `gorm:"column:content;type:longtext;comment:报告详情;" description:"报告详情"`
+	TextContent                string `gorm:"column:text_content;type:text;comment:文本内容;" description:"文本内容"`
+	AbstractStatus             int    `gorm:"column:abstract_status;type:tinyint(4);comment:摘要生成情况,-1:生成失败,0:待生成,1:已生成;default:0;" description:"摘要生成情况,-1:生成失败,0:待生成,1:已生成"`
+	Abstract                   string `gorm:"column:abstract;type:text;comment:摘要;" description:"摘要"`
+	Country                    string `gorm:"column:country;type:varchar(255);comment:国家;" description:"国家"`
+	Province                   string `gorm:"column:province;type:varchar(255);comment:省;" description:"省"`
+	City                       string `gorm:"column:city;type:varchar(255);comment:市;" description:"市"`
+	ArticleCreateTime          string `gorm:"column:article_create_time;type:datetime;comment:报告创建时间;default:NULL;" description:"报告创建时间"`
+	ModifyTime                 string `gorm:"column:modify_time;type:datetime;comment:修改时间;default:NULL;" description:"修改时间"`
+	CreateTime                 string `gorm:"column:create_time;type:datetime;comment:入库时间;default:NULL;" description:"入库时间"`
+	WechatPlatformName         string `gorm:"column:title;type:varchar(255);comment:标题;" description:"微信公众号名称"`
+	WechatPlatformRoundHeadImg string `gorm:"column:round_head_img;type:varchar(255);comment:头像;" description:"微信公众号头像"`
+	TagName                    string `gorm:"column:tag_name;type:varchar(255);comment:标签名称;" description:"标签名称"`
+	TagId                      int    `gorm:"column:tag_id;type:varchar(255);comment:标签id;" description:"标签id"`
+}
+
+func (m *WechatArticle) ToView() WechatArticleView {
+	var articleCreateTime, modifyTime, createTime string
+
+	if !m.ArticleCreateTime.IsZero() {
+		articleCreateTime = m.ArticleCreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.CreateTime.IsZero() {
+		createTime = m.CreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.ModifyTime.IsZero() {
+		modifyTime = m.ModifyTime.Format(utils.FormatDateTime)
+	}
+	return WechatArticleView{
+		WechatArticleId:            m.WechatArticleId,
+		WechatPlatformId:           m.WechatPlatformId,
+		FakeId:                     m.FakeId,
+		Title:                      m.Title,
+		Link:                       m.Link,
+		CoverUrl:                   m.CoverUrl,
+		Description:                m.Description,
+		Content:                    m.Content,
+		TextContent:                m.TextContent,
+		AbstractStatus:             m.AbstractStatus,
+		Country:                    m.Country,
+		Province:                   m.Province,
+		City:                       m.City,
+		ArticleCreateTime:          articleCreateTime,
+		ModifyTime:                 modifyTime,
+		CreateTime:                 createTime,
+		WechatPlatformName:         "",
+		WechatPlatformRoundHeadImg: "",
+	}
+}
+
+func (m *WechatArticle) ListToViewList(list []*WechatArticle) (wechatArticleViewList []WechatArticleView) {
+	wechatArticleViewList = make([]WechatArticleView, 0)
+
+	for _, v := range list {
+		wechatArticleViewList = append(wechatArticleViewList, v.ToView())
+	}
+	return
+}
+
+func (m *WechatArticle) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *WechatArticle) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *WechatArticle) GetById(id int) (item *WechatArticle, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatArticleColumns.WechatArticleID), id).First(&item).Error
+
+	return
+}
+func GetArticleById(id int) (item *WechatArticle, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatArticleColumns.WechatArticleID), id).First(&item).Error
+	return
+}
+func (m *WechatArticle) GetByLink(link string) (item *WechatArticle, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatArticleColumns.Link), link).First(&item).Error
+
+	return
+}
+
+func (m *WechatArticle) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticle, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 AND is_deleted=0 %s  order by article_create_time desc,wechat_article_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 *WechatArticle) GetCountByCondition(condition string, pars []interface{}) (total int, err error) {
+	var intNull sql.NullInt64
+	sqlStr := fmt.Sprintf(`SELECT COUNT(1) total FROM %s WHERE 1=1 AND is_deleted=0 %s`, m.TableName(), condition)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Scan(&intNull).Error
+	if err == nil && intNull.Valid {
+		total = int(intNull.Int64)
+	}
+
+	return
+}
+
+func (m *WechatArticle) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatArticle, err error) {
+
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(`wechat_article_id,wechat_platform_id,fake_id,title,link,cover_url,description,country,province,city,article_create_time,modify_time,create_time`, condition, pars, startSize, pageSize)
+	}
+
+	return
+}
+
+type WechatArticleAndPlatform struct {
+	WechatArticleId   int       `gorm:"column:wechat_article_id;type:int(10) UNSIGNED;primaryKey;not null;" description:""`
+	WechatPlatformId  int       `gorm:"column:wechat_platform_id;type:int(11);comment:归属公众号id;default:0;" description:"归属公众号id"`
+	FakeId            string    `gorm:"column:fake_id;type:varchar(255);comment:公众号唯一id;" description:"公众号唯一id"`
+	Title             string    `gorm:"column:title;type:varchar(255);comment:标题;" description:"标题"`
+	Link              string    `gorm:"column:link;type:varchar(255);comment:链接;" description:"链接"`
+	CoverUrl          string    `gorm:"column:cover_url;type:varchar(255);comment:公众号封面;" description:"公众号封面"`
+	Description       string    `gorm:"column:description;type:varchar(255);comment:描述;" description:"描述"`
+	Content           string    `gorm:"column:content;type:longtext;comment:报告详情;" description:"报告详情"`
+	TextContent       string    `gorm:"column:text_content;type:text;comment:文本内容;" description:"文本内容"`
+	Abstract          string    `gorm:"column:abstract;type:text;comment:摘要;" description:"摘要"`
+	Country           string    `gorm:"column:country;type:varchar(255);comment:国家;" description:"国家"`
+	Province          string    `gorm:"column:province;type:varchar(255);comment:省;" description:"省"`
+	City              string    `gorm:"column:city;type:varchar(255);comment:市;" description:"市"`
+	ArticleCreateTime time.Time `gorm:"column:article_create_time;type:datetime;comment:报告创建时间;default:NULL;" description:"报告创建时间"`
+	IsDeleted         int       `gorm:"column:is_deleted;type:tinyint(4);comment:是否删除,0:未删除,1: 已删除;default:0;" description:"是否删除,0:未删除,1: 已删除"`
+	ModifyTime        time.Time `gorm:"column:modify_time;type:datetime;comment:修改时间;default:NULL;" description:"修改时间"`
+	CreateTime        time.Time `gorm:"column:create_time;type:datetime;comment:入库时间;default:NULL;" description:"入库时间"`
+	Nickname          string    `gorm:"column:nickname;type:varchar(255);comment:公众号名称;" description:"nickname"`          // 公众号名称
+	Alias             string    `gorm:"column:alias;type:varchar(255);comment:别名;" description:"alias"`                   // 别名
+	RoundHeadImg      string    `gorm:"column:round_head_img;type:varchar(255);comment:头像;" description:"round_head_img"` // 头像
+}
+
+func (m *WechatArticleAndPlatform) ToView() WechatArticleView {
+	var articleCreateTime, modifyTime, createTime string
+
+	if !m.ArticleCreateTime.IsZero() {
+		articleCreateTime = m.ArticleCreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.CreateTime.IsZero() {
+		createTime = m.CreateTime.Format(utils.FormatDateTime)
+	}
+	if !m.ModifyTime.IsZero() {
+		modifyTime = m.ModifyTime.Format(utils.FormatDateTime)
+	}
+	return WechatArticleView{
+		WechatArticleId:            m.WechatArticleId,
+		WechatPlatformId:           m.WechatPlatformId,
+		FakeId:                     m.FakeId,
+		Title:                      m.Title,
+		Link:                       m.Link,
+		CoverUrl:                   m.CoverUrl,
+		Description:                m.Description,
+		Content:                    m.Content,
+		TextContent:                m.TextContent,
+		Abstract:                   m.Abstract,
+		Country:                    m.Country,
+		Province:                   m.Province,
+		City:                       m.City,
+		ArticleCreateTime:          articleCreateTime,
+		ModifyTime:                 modifyTime,
+		CreateTime:                 createTime,
+		WechatPlatformName:         m.Nickname,
+		WechatPlatformRoundHeadImg: m.RoundHeadImg,
+	}
+}
+
+func (m *WechatArticle) ArticleAndPlatformListToViewList(list []*WechatArticleAndPlatform) (wechatArticleViewList []WechatArticleView) {
+	wechatArticleViewList = make([]WechatArticleView, 0)
+
+	for _, v := range list {
+		wechatArticleViewList = append(wechatArticleViewList, v.ToView())
+	}
+	return
+}
+
+func (m *WechatArticle) GetListByPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAndPlatform, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s AS a 
+          JOIN wechat_platform AS b ON a.wechat_platform_id=b.wechat_platform_id
+          WHERE 1=1 AND a.is_deleted=0 %s  order by a.article_create_time DESC,a.wechat_article_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 *WechatArticle) GetCountByPlatformCondition(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_platform AS b ON a.wechat_platform_id=b.wechat_platform_id 
+          WHERE 1=1 AND a.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 *WechatArticle) GetPageListByPlatformCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatArticleAndPlatform, err error) {
+	total, err = m.GetCountByPlatformCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByPlatformCondition(`a.wechat_article_id,a.wechat_platform_id,a.fake_id,a.title,a.link,a.cover_url,a.description,a.country,a.province,a.city,a.article_create_time,a.modify_time,a.create_time,b.nickname,b.round_head_img`, condition, pars, startSize, pageSize)
+	}
+
+	return
+}

+ 255 - 0
models/rag/wechat_article_abstract.go

@@ -0,0 +1,255 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+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"` // 摘要内容
+	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"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *WechatArticleAbstract) TableName() string {
+	return "wechat_article_abstract"
+}
+
+// WechatArticleAbstractColumns get sql column name.获取数据库列名
+var WechatArticleAbstractColumns = struct {
+	WechatArticleAbstractID string
+	WechatArticleID         string
+	Content                 string
+	Version                 string
+	ModifyTime              string
+	CreateTime              string
+}{
+	WechatArticleAbstractID: "wechat_article_abstract_id",
+	WechatArticleID:         "wechat_article_id",
+	Content:                 "content",
+	Version:                 "version",
+	ModifyTime:              "modify_time",
+	CreateTime:              "create_time",
+}
+
+func (m *WechatArticleAbstract) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *WechatArticleAbstract) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *WechatArticleAbstract) Del() (err error) {
+	err = global.DbMap[utils.DbNameAI].Delete(&m).Error
+
+	return
+}
+
+func (m *WechatArticleAbstract) GetById(id int) (item *WechatArticleAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatArticleAbstractColumns.WechatArticleAbstractID), id).First(&item).Error
+
+	return
+}
+
+func (m *WechatArticleAbstract) GetByIdList(idList []int) (items []*WechatArticleAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s in (?) ", WechatArticleAbstractColumns.WechatArticleAbstractID), idList).Find(&items).Error
+
+	return
+}
+
+func (m *WechatArticleAbstract) GetListByCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAbstract, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s  order by wechat_article_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 *WechatArticleAbstract) DelByIdList(idList []int) (err error) {
+	if len(idList) <= 0 {
+		return
+	}
+	sqlStr := fmt.Sprintf(`delete from %s where %s in (?)`, m.TableName(), WechatArticleAbstractColumns.WechatArticleAbstractID)
+	err = global.DbMap[utils.DbNameAI].Exec(sqlStr, idList).Error
+
+	return
+}
+
+// GetByWechatArticleId
+// @Description: 根据报告id获取摘要
+// @author: Roc
+// @receiver m
+// @datetime 2025-03-07 10:00:59
+// @param id int
+// @return item *WechatArticleAbstract
+// @return err error
+func (m *WechatArticleAbstract) GetByWechatArticleId(id int) (item *WechatArticleAbstract, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatArticleAbstractColumns.WechatArticleID), id).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"`
+	WechatPlatformId        int    `gorm:"column:wechat_platform_id;type:int(11);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              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:"标题"`
+	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"`
+}
+
+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:"摘要内容"` //
+	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"`
+}
+
+func (m *WechatArticleAbstractItem) 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,
+	}
+}
+
+func (m *WechatArticleAbstract) WechatArticleAbstractItem(list []*WechatArticleAbstractItem) (wechatArticleViewList []WechatArticleAbstractView) {
+	wechatArticleViewList = make([]WechatArticleAbstractView, 0)
+
+	for _, v := range list {
+		wechatArticleViewList = append(wechatArticleViewList, v.ToView())
+	}
+	return
+}
+
+func (m *WechatArticleAbstract) GetListByPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAbstractItem, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s AS a 
+          JOIN wechat_article AS b ON a.wechat_article_id=b.wechat_article_id
+          JOIN wechat_platform AS c ON b.wechat_platform_id=c.wechat_platform_id
+          WHERE 1=1 AND b.is_deleted=0 %s  order by a.modify_time DESC,a.wechat_article_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 *WechatArticleAbstract) GetCountByPlatformCondition(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.wechat_article_id=b.wechat_article_id
+          JOIN wechat_platform AS c ON b.wechat_platform_id=c.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 *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)
+	}
+
+	return
+}
+
+func (m *WechatArticleAbstract) GetListByTagAndPlatformCondition(field, condition string, pars []interface{}, startSize, pageSize int) (items []*WechatArticleAbstractItem, err error) {
+	if field == "" {
+		field = "*"
+	}
+	sqlStr := fmt.Sprintf(`SELECT %s FROM %s AS a 
+          JOIN wechat_article AS b ON a.wechat_article_id=b.wechat_article_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.wechat_article_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 *WechatArticleAbstract) 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.wechat_article_id=b.wechat_article_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 *WechatArticleAbstract) GetPageListByTagAndPlatformCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatArticleAbstractItem, err error) {
+
+	total, err = m.GetCountByTagAndPlatformCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByTagAndPlatformCondition(`a.wechat_article_abstract_id,a.wechat_article_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 wechatArticleAbstractIdList []int
+// @return err error
+func (m *WechatArticleAbstract) DelVectorKey(wechatArticleAbstractIdList []int) (err error) {
+	sqlStr := fmt.Sprintf(`UPDATE %s set vector_key = '' WHERE wechat_article_abstract_id IN (?)`, m.TableName())
+	err = global.DbMap[utils.DbNameAI].Exec(sqlStr, wechatArticleAbstractIdList).Error
+
+	return
+}

+ 66 - 0
models/rag/wechat_article_chat_record.go

@@ -0,0 +1,66 @@
+package rag
+
+import (
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type WechatArticleChatRecord struct {
+	WechatArticleChatRecordId int       `gorm:"column:wechat_article_chat_record_id;type:int(11);comment:主键;primaryKey;not null;" json:"wechat_article_chat_record_id"` // 主键
+	WechatArticleId           int       `gorm:"column:wechat_article_id;type:int(11);comment:文章id;default:NULL;" json:"wechat_article_id"`                              // 文章id
+	ChatUserType              string    `gorm:"column:chat_user_type;type:enum('user', 'assistant');comment:用户方;default:NULL;" json:"chat_user_type"`                   // 用户方
+	Content                   string    `gorm:"column:content;type:longtext;comment:对话内容;" json:"content"`                                                              // 对话内容
+	SendTime                  time.Time `gorm:"column:send_time;type:datetime;comment:发送时间;default:NULL;" json:"send_time"`                                             // 发送时间
+	CreatedTime               time.Time `gorm:"column:created_time;type:datetime;comment:创建时间;default:NULL;" json:"created_time"`                                       // 创建时间
+	UpdateTime                time.Time `gorm:"column:update_time;type:datetime;comment:更新时间;default:NULL;" json:"update_time"`                                         // 更新时间
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *WechatArticleChatRecord) TableName() string {
+	return "wechat_article_chat_record"
+}
+
+// WechatArticleChatRecordColumns get sql column name.获取数据库列名
+var WechatArticleChatRecordColumns = struct {
+	WechatArticleChatRecordID string
+	WechatArticleID           string
+	ChatUserType              string
+	Content                   string
+	SendTime                  string
+	CreatedTime               string
+	UpdateTime                string
+}{
+	WechatArticleChatRecordID: "wechat_article_chat_record_id",
+	WechatArticleID:           "wechat_article_id",
+	ChatUserType:              "chat_user_type",
+	Content:                   "content",
+	SendTime:                  "send_time",
+	CreatedTime:               "created_time",
+	UpdateTime:                "update_time",
+}
+
+func (m *WechatArticleChatRecord) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *WechatArticleChatRecord) CreateInBatches(items []*WechatArticleChatRecord) (err error) {
+	err = global.DbMap[utils.DbNameAI].CreateInBatches(items, utils.MultiAddNum).Error
+
+	return
+}
+
+func (m *WechatArticleChatRecord) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *WechatArticleChatRecord) GetById(id int) (item *WechatArticle, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatArticleChatRecordColumns.WechatArticleChatRecordID), id).First(&item).Error
+
+	return
+}

+ 184 - 0
models/rag/wechat_platform.go

@@ -0,0 +1,184 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type WechatPlatform struct {
+	WechatPlatformId int       `gorm:"column:wechat_platform_id;type:int(10) UNSIGNED;primaryKey;not null;" description:"wechat_platform_id"`
+	FakeId           string    `gorm:"column:fake_id;type:varchar(255);comment:公众号唯一id;" description:"fake_id"`                             // 公众号唯一id
+	Nickname         string    `gorm:"column:nickname;type:varchar(255);comment:公众号名称;" description:"nickname"`                             // 公众号名称
+	Alias            string    `gorm:"column:alias;type:varchar(255);comment:别名;" description:"alias"`                                      // 别名
+	RoundHeadImg     string    `gorm:"column:round_head_img;type:varchar(255);comment:头像;" description:"round_head_img"`                    // 头像
+	ServiceType      int       `gorm:"column:service_type;type:int(11);comment:类型;default:0;" description:"service_type"`                   // 类型
+	Signature        string    `gorm:"column:signature;type:varchar(255);comment:签名;" description:"signature"`                              // 签名
+	Verified         int       `gorm:"column:verified;type:int(11);comment:是否认证,0:未认证,1:已认证;这个我不确定,再核实下;default:0;" description:"verified"` // 是否认证,0:未认证,1:已认证;这个我不确定,再核实下
+	ArticleLink      string    `gorm:"column:article_link;type:varchar(255);comment:添加公众时的文章链接;" description:"article_link"`                // 添加公众时的文章链接
+	Enabled          int       `gorm:"column:enabled;type:tinyint(9);comment:是否启用,0:禁用,1:启用;default:1;" description:"enabled"`              // 是否启用,0:禁用,1:启用
+	SysUserId        int       `gorm:"column:sys_user_id;type:int(9) UNSIGNED;comment:用户id;default:0;" description:"sys_user_id"`           // 用户id
+	ModifyTime       time.Time `gorm:"column:modify_time;type:datetime;comment:最后一次修改时间;default:NULL;" description:"modify_time"`           // 最后一次修改时间
+	CreateTime       time.Time `gorm:"column:create_time;type:datetime;comment:添加时间;default:NULL;" description:"create_time"`               // 添加时间
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *WechatPlatform) TableName() string {
+	return "wechat_platform"
+}
+
+// WechatPlatformColumns get sql column name.获取数据库列名
+var WechatPlatformColumns = struct {
+	WechatPlatformID string
+	FakeID           string
+	Nickname         string
+	Alias            string
+	RoundHeadImg     string
+	ServiceType      string
+	Signature        string
+	Verified         string
+	ArticleLink      string
+	Enabled          string
+	SysUserID        string
+	ModifyTime       string
+	CreateTime       string
+}{
+	WechatPlatformID: "wechat_platform_id",
+	FakeID:           "fake_id",
+	Nickname:         "nickname",
+	Alias:            "alias",
+	RoundHeadImg:     "round_head_img",
+	ServiceType:      "service_type",
+	Signature:        "signature",
+	Verified:         "verified",
+	ArticleLink:      "article_link",
+	Enabled:          "enabled",
+	SysUserID:        "sys_user_id",
+	ModifyTime:       "modify_time",
+	CreateTime:       "create_time",
+}
+
+func (m *WechatPlatform) Create() (err error) {
+	err = global.DbMap[utils.DbNameAI].Create(&m).Error
+
+	return
+}
+
+func (m *WechatPlatform) Update(updateCols []string) (err error) {
+	err = global.DbMap[utils.DbNameAI].Select(updateCols).Updates(&m).Error
+
+	return
+}
+
+func (m *WechatPlatform) Del() (err error) {
+	err = global.DbMap[utils.DbNameAI].Delete(&m).Error
+
+	return
+}
+
+func (m *WechatPlatform) GetById(wechatPlatformId int) (item *WechatPlatform, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatPlatformColumns.WechatPlatformID), wechatPlatformId).First(&item).Error
+
+	return
+}
+
+func (m *WechatPlatform) GetByCondition(condition string, pars []interface{}) (item *WechatPlatform, 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 *WechatPlatform) GetListByCondition(condition string, pars []interface{}, startSize, pageSize int) (items []*WechatPlatform, err error) {
+	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT ?,?`, m.TableName(), condition)
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
+func (m *WechatPlatform) 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
+}
+
+func (m *WechatPlatform) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatPlatform, err error) {
+
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(condition, pars, startSize, pageSize)
+	}
+
+	return
+}
+
+func (m *WechatPlatform) GetByFakeID(fakeId string) (item *WechatPlatform, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatPlatformColumns.FakeID), fakeId).First(&item).Error
+
+	return
+}
+
+// Add
+// @Description: 添加一个新的公众号
+// @author: Roc
+// @receiver m
+// @datetime 2025-03-04 17:48:30
+// @param tagIdList []int
+// @return err error
+func (m *WechatPlatform) Add(tagIdList []int) (err error) {
+	tx := global.DbMap[utils.DbNameAI].Begin()
+	defer func() {
+		if err != nil {
+			_ = tx.Rollback()
+		} else {
+			_ = tx.Commit()
+		}
+	}()
+
+	err = tx.Create(&m).Error
+	if err != nil {
+		return
+	}
+
+	// 标签与公众号关系
+	if len(tagIdList) > 0 {
+		addTagMappingList := make([]*WechatPlatformTagMapping, 0)
+
+		for _, tagId := range tagIdList {
+			addTagMappingList = append(addTagMappingList, &WechatPlatformTagMapping{
+				WechatPlatformTagMappingId: 0,
+				WechatPlatformId:           m.WechatPlatformId,
+				TagId:                      tagId,
+				ModifyTime:                 time.Now(),
+				CreateTime:                 time.Now(),
+			})
+		}
+		err = tx.CreateInBatches(addTagMappingList, utils.MultiAddNum).Error
+		if err != nil {
+			return
+		}
+	}
+
+	// 用户与公众号关系
+	userMapping := &WechatPlatformUserMapping{
+		WechatPlatformUserMappingId: 0,
+		WechatPlatformId:            m.WechatPlatformId,
+		SysUserId:                   m.SysUserId,
+		ModifyTime:                  time.Now(),
+		CreateTime:                  time.Now(),
+	}
+	err = tx.Create(userMapping).Error
+
+	return
+}

+ 100 - 0
models/rag/wechat_platform_tag_mapping.go

@@ -0,0 +1,100 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type WechatPlatformTagMapping struct {
+	WechatPlatformTagMappingId int       `gorm:"column:wechat_platform_tag_mapping_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"wechat_platform_tag_mapping_id"`
+	WechatPlatformId           int       `gorm:"column:wechat_platform_id;type:int(9) UNSIGNED;comment:微信公众号id;default:0;" description:"wechat_platform_id"` // 微信公众号id
+	TagId                      int       `gorm:"column:tag_id;type:int(9) UNSIGNED;comment:品种id;default:0;" description:"tag_id"`                            // 品种id
+	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"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *WechatPlatformTagMapping) TableName() string {
+	return "wechat_platform_tag_mapping"
+}
+
+// WechatPlatformTagMappingColumns get sql column name.获取数据库列名
+var WechatPlatformTagMappingColumns = struct {
+	WechatPlatformTagMappingID string
+	WechatPlatformID           string
+	TagID                      string
+	ModifyTime                 string
+	CreateTime                 string
+}{
+	WechatPlatformTagMappingID: "wechat_platform_tag_mapping_id",
+	WechatPlatformID:           "wechat_platform_id",
+	TagID:                      "tag_id",
+	ModifyTime:                 "modify_time",
+	CreateTime:                 "create_time",
+}
+
+func (m *WechatPlatformTagMapping) GetByID(WechatPlatformTagMappingColumnsId int) (item *WechatPlatformTagMapping, err error) {
+	err = global.DbMap[utils.DbNameAI].Where(fmt.Sprintf("%s = ?", WechatPlatformTagMappingColumns.WechatPlatformTagMappingID), WechatPlatformTagMappingColumnsId).First(&item).Error
+
+	return
+}
+
+func (m *WechatPlatformTagMapping) GetByCondition(condition string, pars []interface{}) (item *WechatPlatformTagMapping, err error) {
+	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Where(condition, pars).First(&item).Error
+
+	return
+}
+
+func (m *WechatPlatformTagMapping) GetListByCondition(condition string, pars []interface{}, startSize, pageSize int) (items []*WechatPlatformTagMapping, err error) {
+	sqlStr := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT ?,?`, m.TableName(), condition)
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
+func (m *WechatPlatformTagMapping) 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
+}
+
+func (m *WechatPlatformTagMapping) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*WechatPlatformTagMapping, err error) {
+
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(condition, pars, startSize, pageSize)
+	}
+
+	return
+}
+
+type WechatPlatformTagItem struct {
+	TagId            int    `json:"tag_id"`
+	TagName          string `json:"tag_name"`
+	WechatPlatformId int    `json:"tag_article"`
+}
+
+func (m *WechatPlatformTagMapping) GetWechatPlatformTagListByWechatPlatformIdList(wechatPlatformIdList []int) (items []*WechatPlatformTagItem, err error) {
+	num := len(wechatPlatformIdList)
+	if num <= 0 {
+		return
+	}
+	sqlStr := `SELECT a.wechat_platform_id,b.tag_id,b.tag_name FROM wechat_platform_tag_mapping AS a 
+    JOIN tag AS b on a.tag_id = b.tag_id  WHERE a.wechat_platform_id IN (?) GROUP BY a.wechat_platform_id `
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, wechatPlatformIdList).Find(&items).Error
+
+	return
+}

+ 88 - 0
models/rag/wechat_platform_user_mapping.go

@@ -0,0 +1,88 @@
+package rag
+
+import (
+	"database/sql"
+	"eta/eta_api/global"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+type WechatPlatformUserMapping struct {
+	WechatPlatformUserMappingId int       `gorm:"column:wechat_platform_user_mapping_id;type:int(9) UNSIGNED;primaryKey;not null;" description:"wechat_platform_user_mapping_id"`
+	WechatPlatformId            int       `gorm:"column:wechat_platform_id;type:int(9) UNSIGNED;comment:微信公众号id;default:0;" description:"wechat_platform_id"` // 微信公众号id
+	SysUserId                   int       `gorm:"column:sys_user_id;type:int(9) UNSIGNED;comment:用户id;default:0;" description:"sys_user_id"`                  // 用户id
+	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"`
+}
+
+// TableName get sql table name.获取数据库表名
+func (m *WechatPlatformUserMapping) TableName() string {
+	return "wechat_platform_user_mapping"
+}
+
+// WechatPlatformUserMappingColumns get sql column name.获取数据库列名
+var WechatPlatformUserMappingColumns = struct {
+	WechatPlatformUserMappingID string
+	WechatPlatformID            string
+	SysUserID                   string
+	ModifyTime                  string
+	CreateTime                  string
+}{
+	WechatPlatformUserMappingID: "wechat_platform_user_mapping_id",
+	WechatPlatformID:            "wechat_platform_id",
+	SysUserID:                   "sys_user_id",
+	ModifyTime:                  "modify_time",
+	CreateTime:                  "create_time",
+}
+
+type UserFollowWechatPlatform struct {
+	WechatPlatformId int       `gorm:"column:wechat_platform_id;type:int(10) UNSIGNED;primaryKey;not null;" description:"wechat_platform_id"`
+	FakeId           string    `gorm:"column:fake_id;type:varchar(255);comment:公众号唯一id;" description:"公众号唯一id"`
+	Nickname         string    `gorm:"column:nickname;type:varchar(255);comment:公众号名称;" description:"公众号名称"`
+	Alias            string    `gorm:"column:alias;type:varchar(255);comment:别名;" description:"别名"`
+	RoundHeadImg     string    `gorm:"column:round_head_img;type:varchar(255);comment:头像;" description:"头像"`
+	ServiceType      int       `gorm:"column:service_type;type:int(11);comment:类型;default:0;" description:"类型"`
+	Signature        string    `gorm:"column:signature;type:varchar(255);comment:签名;" description:"签名"`
+	Verified         int       `gorm:"column:verified;type:int(11);comment:是否认证,0:未认证,1:已认证;这个我不确定,再核实下;default:0;" description:"是否认证,0:未认证,1:已认证;这个我不确定,再核实下"`
+	ArticleLink      string    `gorm:"column:article_link;type:varchar(255);comment:添加公众时的文章链接;" description:"添加公众时的文章链接"`
+	Enabled          int       `gorm:"column:enabled;type:tinyint(9);comment:是否启用,0:禁用,1:启用;default:1;" description:"是否启用,0:禁用,1:启用"`
+	SysUserId        int       `gorm:"column:sys_user_id;type:int(9) UNSIGNED;comment:用户id;default:0;" description:"用户id"`
+	ModifyTime       time.Time `gorm:"column:modify_time;type:datetime;comment:最后一次修改时间;default:NULL;" description:"最后一次修改时间"`
+	CreateTime       time.Time `gorm:"column:create_time;type:datetime;comment:添加时间;default:NULL;" description:"添加时间"`
+	FollowUserId     int       `gorm:"column:follow_user_id;type:int(9) UNSIGNED;comment:关注的用户id;default:0;" description:"关注的用户id"`
+}
+
+func (m *WechatPlatformUserMapping) GetListByCondition(condition string, pars []interface{}, startSize, pageSize int) (items []*UserFollowWechatPlatform, err error) {
+	sqlStr := fmt.Sprintf(`SELECT a.wechat_platform_id,a.fake_id,a.nickname,a.alias,a.round_head_img,a.service_type,a.signature,a.verified,a.article_link,a.enabled,a.sys_user_id,a.modify_time,a.create_time,b.sys_user_id as follow_user_id FROM wechat_platform a 
+    JOIN wechat_platform_user_mapping b on a.wechat_platform_id=b.wechat_platform_id 
+                                                                                                                                                                                                                   WHERE 1=1 %s LIMIT ?,?`, condition)
+	pars = append(pars, startSize, pageSize)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Find(&items).Error
+
+	return
+}
+
+func (m *WechatPlatformUserMapping) GetCountByCondition(condition string, pars []interface{}) (total int, err error) {
+	var intNull sql.NullInt64
+	sqlStr := fmt.Sprintf(`SELECT COUNT(1) total FROM wechat_platform a JOIN wechat_platform_user_mapping b on a.wechat_platform_id=b.wechat_platform_id WHERE 1=1 `, condition)
+	err = global.DbMap[utils.DbNameAI].Raw(sqlStr, pars...).Scan(&intNull).Error
+	if err == nil && intNull.Valid {
+		total = int(intNull.Int64)
+	}
+
+	return
+}
+
+func (m *WechatPlatformUserMapping) GetPageListByCondition(condition string, pars []interface{}, startSize, pageSize int) (total int, items []*UserFollowWechatPlatform, err error) {
+
+	total, err = m.GetCountByCondition(condition, pars)
+	if err != nil {
+		return
+	}
+	if total > 0 {
+		items, err = m.GetListByCondition(condition, pars, startSize, pageSize)
+	}
+
+	return
+}

+ 65 - 39
models/report.go

@@ -6,11 +6,10 @@ import (
 	"eta/eta_api/global"
 	"eta/eta_api/utils"
 	"fmt"
-	"strings"
-	"time"
-
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"gorm.io/gorm"
+	"strings"
+	"time"
 )
 
 // 报告状态
@@ -34,42 +33,43 @@ const (
 )
 
 type Report struct {
-	Id                 int       `gorm:"column:id;primaryKey;autoIncrement" description:"报告Id"`
-	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
-	ClassifyIdFirst    int       `description:"一级分类id"`
-	ClassifyNameFirst  string    `description:"一级分类名称"`
-	ClassifyIdSecond   int       `description:"二级分类id"`
-	ClassifyNameSecond string    `description:"二级分类名称"`
-	Title              string    `description:"标题"`
-	Abstract           string    `description:"摘要"`
-	Author             string    `description:"作者"`
-	Frequency          string    `description:"频度"`
-	CreateTime         string    `description:"创建时间"`
-	ModifyTime         time.Time `description:"修改时间"`
-	State              int       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
-	PublishTime        time.Time `description:"发布时间"`
-	Stage              int       `description:"期数"`
-	MsgIsSend          int       `description:"消息是否已发送,0:否,1:是"`
-	ThsMsgIsSend       int       `description:"客户群消息是否已发送,0:否,1:是"`
-	Content            string    `description:"内容"`
-	VideoUrl           string    `description:"音频文件URL"`
-	VideoName          string    `description:"音频文件名称"`
-	VideoPlaySeconds   string    `description:"音频播放时长"`
-	VideoSize          string    `description:"音频文件大小,单位M"`
-	ContentSub         string    `description:"内容前两个章节"`
-	ReportCode         string    `description:"报告唯一编码"`
-	ReportVersion      int       `description:"1:旧版,2:新版"`
-	HasChapter         int       `description:"是否有章节 0-否 1-是"`
-	ChapterType        string    `description:"章节类型 day-晨报 week-周报"`
-	OldReportId        int       `description:"research_report表ID, 大于0则表示该报告为老后台同步过来的"`
-	MsgSendTime        time.Time `description:"模版消息发送时间"`
-	AdminId            int       `description:"创建者账号"`
-	AdminRealName      string    `description:"创建者姓名"`
-	ApproveTime        time.Time `description:"审批时间"`
-	ApproveId          int       `description:"审批ID"`
-	DetailImgUrl       string    `description:"报告详情长图地址"`
-	DetailPdfUrl       string    `description:"报告详情PDF地址"`
-
+	Id                  int       `orm:"column(id)" description:"报告Id"`
+	AddType             int       `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst     int       `description:"一级分类id"`
+	ClassifyNameFirst   string    `description:"一级分类名称"`
+	ClassifyIdSecond    int       `description:"二级分类id"`
+	ClassifyNameSecond  string    `description:"二级分类名称"`
+	Title               string    `description:"标题"`
+	Abstract            string    `description:"摘要"`
+	Author              string    `description:"作者"`
+	Frequency           string    `description:"频度"`
+	CreateTime          string    `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+	State               int       `description:"1:未发布;2:已发布;3-待提交;4-待审批;5-已驳回;6-已通过"`
+	PublishTime         time.Time `description:"发布时间"`
+	Stage               int       `description:"期数"`
+	MsgIsSend           int       `description:"消息是否已发送,0:否,1:是"`
+	ThsMsgIsSend        int       `description:"客户群消息是否已发送,0:否,1:是"`
+	Content             string    `description:"内容"`
+	VideoUrl            string    `description:"音频文件URL"`
+	VideoName           string    `description:"音频文件名称"`
+	VideoPlaySeconds    string    `description:"音频播放时长"`
+	VideoSize           string    `description:"音频文件大小,单位M"`
+	ContentSub          string    `description:"内容前两个章节"`
+	ReportCode          string    `description:"报告唯一编码"`
+	ReportVersion       int       `description:"1:旧版,2:新版"`
+	HasChapter          int       `description:"是否有章节 0-否 1-是"`
+	ChapterType         string    `description:"章节类型 day-晨报 week-周报"`
+	OldReportId         int       `description:"research_report表ID, 大于0则表示该报告为老后台同步过来的"`
+	MsgSendTime         time.Time `description:"模版消息发送时间"`
+	AdminId             int       `description:"创建者账号"`
+	AdminRealName       string    `description:"创建者姓名"`
+	ApproveTime         time.Time `description:"审批时间"`
+	ApproveId           int       `description:"审批ID"`
+	DetailImgUrl        string    `description:"报告详情长图地址"`
+	DetailPdfUrl        string    `description:"报告详情PDF地址"`
+	DetailImgUrlMobile  string    `description:"报告详情长图地址-手机端"`
+	DetailPdfUrlMobile  string    `description:"报告详情PDF地址-手机端"`
 	ContentStruct       string    `description:"内容组件"`
 	LastModifyAdminId   int       `description:"最后更新人ID"`
 	LastModifyAdminName string    `description:"最后更新人姓名"`
@@ -148,6 +148,8 @@ type ReportList struct {
 	ApproveTime        string                    `description:"审批时间"`
 	DetailImgUrl       string                    `description:"报告详情长图地址"`
 	DetailPdfUrl       string                    `description:"报告详情PDF地址"`
+	DetailImgUrlMobile string                    `description:"报告详情长图地址-手机端"`
+	DetailPdfUrlMobile string                    `description:"报告详情PDF地址-手机端"`
 
 	CollaborateType     int8      `description:"协作方式,1:个人,2:多人协作。默认:1"`
 	ReportLayout        int8      `description:"报告布局,1:常规布局,2:智能布局。默认:1"`
@@ -998,6 +1000,7 @@ type ElasticReportDetail struct {
 	BodyContent        string `description:"内容"`
 	PublishTime        string `description:"发布时间"`
 	PublishState       int    `description:"发布状态 1-未发布 2-已发布"`
+	IsPublicPublish    int8   `description:"是否公开发布,1:是,2:否"`
 	Author             string `description:"作者"`
 	ClassifyIdFirst    int    `description:"一级分类ID"`
 	ClassifyNameFirst  string `description:"一级分类名称"`
@@ -1573,6 +1576,20 @@ func ModifyReportImgUrl(reportId int, detailImgUrl string) (err error) {
 	return
 }
 
+func ModifyReportPdfUrlMobile(reportId int, detailPdfUrlMobile string) (err error) {
+	o := global.DbMap[utils.DbNameReport]
+	sql := `UPDATE report SET detail_pdf_url_mobile=? WHERE id=? `
+	err = o.Exec(sql, detailPdfUrlMobile, reportId).Error
+	return
+}
+
+func ModifyReportImgUrlMobile(reportId int, detailImgUrlMobile string) (err error) {
+	o := global.DbMap[utils.DbNameReport]
+	sql := `UPDATE report SET detail_img_url_mobile=? WHERE id=? `
+	err = o.Exec(sql, detailImgUrlMobile, reportId).Error
+	return
+}
+
 // UpdatePdfUrlReportById 清空pdf相关字段
 func UpdatePdfUrlReportById(reportId int) (err error) {
 	o := global.DbMap[utils.DbNameReport]
@@ -1667,3 +1684,12 @@ func FindReportListByCondition(condition string, pars []interface{}) (items []*R
 	err = o.Raw(sql, pars...).Find(&items).Error
 	return
 }
+
+type ReportShartUrlReq struct {
+	Url      string `description:"分享链接"`
+	ReportId int    `description:"报告ID"`
+}
+
+type ReportShartUrlResp struct {
+	UrlToken string `description:"分享链接token"`
+}

+ 2 - 0
models/report_approve/report_approve.go

@@ -200,6 +200,8 @@ type ReportApproveItem struct {
 	ModifyTime            string `description:"修改时间"`
 	DetailImgUrl          string `description:"报告详情长图地址"`
 	DetailPdfUrl          string `description:"报告详情PDF地址"`
+	DetailImgUrlMobile    string `description:"报告详情长图地址-手机端"`
+	DetailPdfUrlMobile    string `description:"报告详情PDF地址-手机端"`
 }
 
 // FormatReportApproveOrm2Item 格式化报告审批

+ 2 - 0
models/smart_report/smart_report.go

@@ -51,6 +51,8 @@ type SmartReport struct {
 	MsgSendTime         time.Time `description:"模版消息发送时间"`
 	DetailImgUrl        string    `description:"报告详情长图地址"`
 	DetailPdfUrl        string    `description:"报告详情PDF地址"`
+	DetailImgUrlMobile  string    `description:"报告详情长图地址-手机端"`
+	DetailPdfUrlMobile  string    `description:"报告详情PDF地址-手机端"`
 	CreateTime          time.Time `description:"创建时间"`
 	ModifyTime          time.Time `description:"修改时间"`
 	HeadImg             string    `description:"报告头图地址"`

+ 4 - 2
models/system/sys_admin.go

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

+ 13 - 2
models/system/sys_group.go

@@ -3,6 +3,7 @@ package system
 import (
 	"eta/eta_api/global"
 	"eta/eta_api/utils"
+	"gorm.io/gorm"
 	"time"
 )
 
@@ -84,7 +85,12 @@ func DeleteSysGroup(groupId int) (err error) {
 	err = o.Exec(sql, groupId).Error
 	return
 }
-
+func DeleteSysGroupByIds(tx *gorm.DB,groupIds []int) (err error) {
+	sql := `DELETE FROM sys_group WHERE group_id in ?`
+	//o := global.DbMap[utils.DbNameMaster]
+	err = tx.Exec(sql, groupIds).Error
+	return
+}
 // 因前端显示需要,TopId字段用来当做一级部门id,DepartmentId为当前分组id
 type SysGroupList struct {
 	GroupId      int            `orm:"column(group_id);pk" gorm:"primaryKey" json:"DepartmentId" description:"分组ID"`
@@ -118,7 +124,12 @@ func ClearSysUserGroup(groupId int) (err error) {
 	err = o.Exec(sql, groupId).Error
 	return
 }
-
+func ClearSysUserGroupByIds(tx *gorm.DB,groupIds []int) (err error) {
+	sql := `UPDATE admin SET group_id=0,group_name='' WHERE group_id in ? `
+	sql = utils.ReplaceDriverKeywords("", sql)
+	err = tx.Exec(sql, groupIds).Error
+	return
+}
 func GetSysGroupByDirectorId(directorId int) (items []*SysGroupList, err error) {
 	sql := `SELECT * FROM sys_group WHERE group_id=? ORDER BY sort ASC, create_time ASC`
 	o := global.DbMap[utils.DbNameMaster]

+ 1 - 0
models/system/sys_user.go

@@ -23,6 +23,7 @@ type LoginResp struct {
 	SysRoleTypeCode string `description:"角色类型编码"`
 	AdminId         int    `description:"系统用户id"`
 	ProductName     string `description:"产品名称:admin,ficc,权益"`
+	Mobile          string `description:"手机号"`
 	Authority       int    `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,4:ficc销售组长"`
 }
 

+ 576 - 0
routers/commentsRouter.go

@@ -2914,6 +2914,231 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangClassify",
+            Router: `/purang/classify`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangIndexList",
+            Router: `/purang/classify/index/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangEdbInfoAdd",
+            Router: `/purang/edb_info/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangAddCheck",
+            Router: `/purang/edb_info/add_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangNameCheck",
+            Router: `/purang/edb_info/name_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "ExportPurangList",
+            Router: `/purang/export`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "GetFrequency",
+            Router: `/purang/frequency`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangIndexData",
+            Router: `/purang/index/data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangSearchList",
+            Router: `/purang/search_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromPurangController"],
+        beego.ControllerComments{
+            Method: "PurangSingleData",
+            Router: `/purang/single_data`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyAdd",
+            Router: `/radish_research/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyEdit",
+            Router: `/radish_research/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyList",
+            Router: `/radish_research/classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyMove",
+            Router: `/radish_research/classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyRemove",
+            Router: `/radish_research/classify/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "ClassifyTree",
+            Router: `/radish_research/classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "EdbAdd",
+            Router: `/radish_research/edb/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "EdbMultiAdd",
+            Router: `/radish_research/edb/multi_add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "EdbNameCheck",
+            Router: `/radish_research/edb/name_check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexDetail",
+            Router: `/radish_research/index/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexEdit",
+            Router: `/radish_research/index/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexExport",
+            Router: `/radish_research/index/export`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexPageList",
+            Router: `/radish_research/index/page_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexRemove",
+            Router: `/radish_research/index/remove`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRadishResearchController"],
+        beego.ControllerComments{
+            Method: "IndexSelect",
+            Router: `/radish_research/index/select`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRzdIndexController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:BaseFromRzdIndexController"],
         beego.ControllerComments{
             Method: "RzdClassify",
@@ -3373,6 +3598,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "UpdateChartClassifyResource",
+            Router: `/chart_classify/forum_chart/update`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
         beego.ControllerComments{
             Method: "ChartClassifyItems",
@@ -3400,6 +3634,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "SetChartClassifyResourceStatus",
+            Router: `/chart_classify/resource_status`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartClassifyController"],
         beego.ControllerComments{
             Method: "AddManualClassify",
@@ -3553,6 +3796,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "GeneralChartToken",
+            Router: `/chart_info/common/general_token`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
         beego.ControllerComments{
             Method: "ChartInfoConvertDetail",
@@ -3571,6 +3823,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "AddChartDescription",
+            Router: `/chart_info/description/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "GetChartDescriptionList",
+            Router: `/chart_info/description/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:ChartInfoController"],
         beego.ControllerComments{
             Method: "ChartInfoDetail",
@@ -8026,6 +8296,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"],
+        beego.ControllerComments{
+            Method: "GetShareUrl",
+            Router: `/share_url`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportEmailCallBackController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportEmailCallBackController"],
         beego.ControllerComments{
             Method: "SendCallBack",
@@ -8350,6 +8629,285 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"],
+        beego.ControllerComments{
+            Method: "Del",
+            Router: `/abstract/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"],
+        beego.ControllerComments{
+            Method: "List",
+            Router: `/abstract/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"],
+        beego.ControllerComments{
+            Method: "AddVector",
+            Router: `/abstract/vector/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:AbstractController"],
+        beego.ControllerComments{
+            Method: "VectorDel",
+            Router: `/abstract/vector/del`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:ChatWsController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:ChatWsController"],
+        beego.ControllerComments{
+            Method: "ChatConnect",
+            Router: `/chat/connect`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:KbController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:KbController"],
+        beego.ControllerComments{
+            Method: "SearchDocs",
+            Router: `/knowledge_base/searchDocs`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"],
+        beego.ControllerComments{
+            Method: "PromoteContentList",
+            Router: `/promote/content_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"],
+        beego.ControllerComments{
+            Method: "DeletePromoteContent",
+            Router: `/promote/delete_content`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"],
+        beego.ControllerComments{
+            Method: "GenerateContent",
+            Router: `/promote/generate_content`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:PromoteController"],
+        beego.ControllerComments{
+            Method: "SavePromoteContent",
+            Router: `/promote/save_content`,
+            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",
+            Router: `/question/add`,
+            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",
+            Router: `/question/del`,
+            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: "Detail",
+            Router: `/question/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:QuestionController"],
+        beego.ControllerComments{
+            Method: "Edit",
+            Router: `/question/edit`,
+            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: "List",
+            Router: `/question/list`,
+            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",
+            Router: `/question/title/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
+        beego.ControllerComments{
+            Method: "ChatRecordList",
+            Router: `/chat/chat_record_list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
+        beego.ControllerComments{
+            Method: "ChatRecordAdd",
+            Router: `/chat/chat_record_save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
+        beego.ControllerComments{
+            Method: "DeleteChat",
+            Router: `/chat/delete_chat`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
+        beego.ControllerComments{
+            Method: "NewChat",
+            Router: `/chat/new_chat`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
+        beego.ControllerComments{
+            Method: "RenameChat",
+            Router: `/chat/rename_chat`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/llm:UserChatController"],
+        beego.ControllerComments{
+            Method: "GetUserChatList",
+            Router: `/chat/user_chat_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",
+            Router: `/tag/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: "Add",
+            Router: `/wechat_platform/add`,
+            AllowHTTPMethods: []string{"post"},
+            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",
+            Router: `/wechat_platform/article/del`,
+            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: "ArticleDetail",
+            Router: `/wechat_platform/article/detail`,
+            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: "ArticleList",
+            Router: `/wechat_platform/article/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: "FollowList",
+            Router: `/wechat_platform/list/follow`,
+            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: "PublicList",
+            Router: `/wechat_platform/list/public`,
+            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: "Op",
+            Router: `/wechat_platform/op`,
+            AllowHTTPMethods: []string{"post"},
+            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: "Refresh",
+            Router: `/wechat_platform/refresh`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/material:MaterialController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/material:MaterialController"],
         beego.ControllerComments{
             Method: "BatchAdd",
@@ -11266,6 +11824,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:ReportCommonController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportCommonController"],
+        beego.ControllerComments{
+            Method: "ShareTransform",
+            Router: `/share/link`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
         beego.ControllerComments{
             Method: "CheckDayWeekReportChapterVideo",
@@ -11689,6 +12256,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "ShareGenerate",
+            Router: `/share/generate`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
         beego.ControllerComments{
             Method: "ThsSendTemplateMsg",

+ 17 - 0
routers/router.go

@@ -30,6 +30,7 @@ import (
 	"eta/eta_api/controllers/eta_forum"
 	"eta/eta_api/controllers/eta_trial"
 	"eta/eta_api/controllers/fe_calendar"
+	"eta/eta_api/controllers/llm"
 	"eta/eta_api/controllers/material"
 	"eta/eta_api/controllers/report_approve"
 	"eta/eta_api/controllers/residual_analysis"
@@ -39,6 +40,8 @@ import (
 	"eta/eta_api/controllers/smart_report"
 	"eta/eta_api/controllers/speech_recognition"
 	"eta/eta_api/controllers/trade_analysis"
+	"eta/eta_api/services"
+
 	"github.com/beego/beego/v2/server/web"
 	"github.com/beego/beego/v2/server/web/filter/cors"
 )
@@ -52,6 +55,7 @@ func init() {
 		ExposeHeaders:    []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"},
 		AllowCredentials: true,
 	}))
+	web.InsertFilter("/adminapi/share/*", web.BeforeRouter, services.FilterShareUrl())
 	ns := web.NewNamespace("/adminapi",
 		web.NSNamespace("/sysuser",
 			web.NSInclude(
@@ -66,6 +70,17 @@ func init() {
 				&controllers.ClassifyController{},
 			),
 		),
+		web.NSNamespace("/llm",
+			web.NSInclude(
+				&llm.ChatWsController{},
+				&llm.UserChatController{},
+				&llm.KbController{},
+				&llm.WechatPlatformController{},
+				&llm.QuestionController{},
+				&llm.AbstractController{},
+				&llm.PromoteController{},
+			),
+		),
 		web.NSNamespace("/banner",
 			web.NSInclude(
 				&controllers.BannerController{},
@@ -189,6 +204,8 @@ func init() {
 				&data_manage.BaseFromRzdIndexController{},
 				&data_manage.ClarksonsDataController{},
 				&data_manage.BaseFromGprRiskController{},
+				&data_manage.BaseFromPurangController{},
+				&data_manage.BaseFromRadishResearchController{},
 			),
 		),
 		web.NSNamespace("/my_chart",

+ 49 - 0
services/data/base_from_purang.go

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

+ 611 - 0
services/data/base_from_radish_research_classify.go

@@ -0,0 +1,611 @@
+package data
+
+import (
+	"errors"
+	"eta/eta_api/models/data_manage"
+	dataSourceModel "eta/eta_api/models/data_source"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services/elastic"
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// GetRadishResearchClassifyTreeRecursive 递归获取分类树形结构
+func GetRadishResearchClassifyTreeRecursive(list []*data_manage.BaseFromRadishResearchClassifyItem, parentId int) []*data_manage.BaseFromRadishResearchClassifyItem {
+	res := make([]*data_manage.BaseFromRadishResearchClassifyItem, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			t := GetRadishResearchClassifyTreeRecursive(list, v.ClassifyId)
+			v.Children = nil // 这一步是方便前端组件判断null...
+			if len(t) > 0 {
+				v.Children = t
+			}
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+// RadishResearchMoveClassify 移动指标分类
+func RadishResearchMoveClassify(req data_manage.BaseFromRadishResearchClassifyMoveReq, sysUser *system.Admin) (err error, errMsg string) {
+	// req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId
+	classifyId := req.ClassifyId
+	parentClassifyId := req.ParentClassifyId
+	prevClassifyId := req.PrevClassifyId
+	nextClassifyId := req.NextClassifyId
+
+	itemId := req.ItemId
+	prevItemId := req.PrevItemId
+	nextItemId := req.NextItemId
+
+	//首先确定移动的对象是分类还是指标
+	//判断上一个节点是分类还是指标
+	//判断下一个节点是分类还是指标
+	//同时更新分类目录下的分类sort和指标sort
+	//更新当前移动的分类或者指标sort
+
+	var parentEdbClassifyInfo *data_manage.BaseFromRadishResearchClassify
+	if parentClassifyId > 0 {
+		parentEdbClassifyInfo, err = data_manage.GetRadishResearchClassifyById(parentClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上级分类信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		edbClassifyInfo *data_manage.BaseFromRadishResearchClassify
+		prevClassify    *data_manage.BaseFromRadishResearchClassify
+		nextClassify    *data_manage.BaseFromRadishResearchClassify
+
+		edbInfo     *data_manage.BaseFromRadishResearchIndex
+		prevEdbInfo *data_manage.BaseFromRadishResearchIndex
+		nextEdbInfo *data_manage.BaseFromRadishResearchIndex
+		prevSort    int
+		nextSort    int
+	)
+
+	// 移动对象为分类, 判断权限
+	if itemId == 0 {
+		edbClassifyInfo, err = data_manage.GetRadishResearchClassifyById(classifyId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				errMsg = "当前分类不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if parentClassifyId > 0 && parentEdbClassifyInfo.Level == 6 {
+			errMsg = "最高只支持添加6级分类"
+			err = errors.New(errMsg)
+			return
+		}
+		// 如果是移动目录, 那么校验一下父级目录下是否有重名目录
+		exists, e := data_manage.GetRadishResearchClassifyByParentIdAndName(parentClassifyId, edbClassifyInfo.ClassifyName, classifyId)
+		if e != nil && !utils.IsErrNoRow(e) {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取父级分类下的同名分类失败, Err: %s", e.Error())
+			return
+		}
+		if exists != nil && exists.ClassifyId > 0 {
+			errMsg = "移动失败,分类名称已存在"
+			return
+		}
+
+	} else {
+		edbInfo, err = data_manage.GetRadishResearchIndexById(req.ItemId)
+		if err != nil {
+			if utils.IsErrNoRow(err) {
+				errMsg = "当前指标不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if edbInfo != nil && edbInfo.BaseFromRadishResearchIndexId <= 0 {
+			errMsg = "当前指标不存在"
+			err = errors.New("获取指标信息失败, Err:" + err.Error())
+			return
+		}
+		if parentClassifyId == 0 {
+			errMsg = "移动失败,指标必须挂在分类下"
+			err = errors.New(errMsg)
+			return
+		}
+
+		//// 移动权限校验
+		//button := GetEdbOpButton(sysUser, edbInfo.SysUserId, edbInfo.EdbType, edbInfo.EdbInfoType, haveOperaAuth)
+		//if !button.MoveButton {
+		//	errMsg = "无操作权限"
+		//	err = errors.New(errMsg)
+		//	return
+		//}
+	}
+
+	if prevClassifyId > 0 {
+		prevClassify, err = data_manage.GetRadishResearchClassifyById(prevClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevClassify.Sort
+	} else if prevItemId > 0 {
+		prevEdbInfo, err = data_manage.GetRadishResearchIndexById(prevItemId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevEdbInfo.Sort
+	}
+
+	if nextClassifyId > 0 {
+		//下一个兄弟节点
+		nextClassify, err = data_manage.GetRadishResearchClassifyById(nextClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextClassify.Sort
+	} else if nextItemId > 0 {
+		//下一个兄弟节点
+		nextEdbInfo, err = data_manage.GetRadishResearchIndexById(nextItemId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextEdbInfo.Sort
+	}
+
+	err, errMsg = radishResearchMoveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify, edbInfo, prevEdbInfo, nextEdbInfo, parentClassifyId, prevSort, nextSort)
+	return
+}
+
+// radishResearchMoveEdbClassify 移动指标分类
+func radishResearchMoveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify *data_manage.BaseFromRadishResearchClassify, edbInfo, prevEdbInfo, nextEdbInfo *data_manage.BaseFromRadishResearchIndex, parentClassifyId int, prevSort, nextSort int) (err error, errMsg string) {
+	updateCol := make([]string, 0)
+	var moveParent bool  // 是否移动分类的父级目录
+	var moveParentId int // 被改变父级分类的目录ID
+
+	indexOb := new(data_manage.BaseFromRadishResearchIndex)
+
+	// 移动对象为分类, 判断分类是否存在
+	if edbClassifyInfo != nil {
+		oldParentId := edbClassifyInfo.ParentId
+		oldLevel := edbClassifyInfo.Level
+		var classifyIds []int
+		if oldParentId != parentClassifyId {
+			//更新子分类对应的level
+			childList, e, m := GetRadishResearchChildClassifyByClassifyId(edbClassifyInfo.BaseFromRadishResearchClassifyId)
+			if e != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询子分类失败,Err:" + e.Error() + m)
+				return
+			}
+
+			if len(childList) > 0 {
+				for _, v := range childList {
+					if v.BaseFromRadishResearchClassifyId == edbClassifyInfo.BaseFromRadishResearchClassifyId {
+						continue
+					}
+					classifyIds = append(classifyIds, v.BaseFromRadishResearchClassifyId)
+				}
+			}
+		}
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId != 0 {
+			if edbClassifyInfo.Level != parentEdbClassifyInfo.Level+1 { //禁止层级调整
+				errMsg = "移动失败"
+				err = errors.New("不支持目录层级变更")
+				return
+			}
+			edbClassifyInfo.ParentId = parentEdbClassifyInfo.BaseFromRadishResearchClassifyId
+			edbClassifyInfo.RootId = parentEdbClassifyInfo.RootId
+			edbClassifyInfo.Level = parentEdbClassifyInfo.Level + 1
+			edbClassifyInfo.LevelPath = fmt.Sprintf("%s,%d", parentEdbClassifyInfo.LevelPath, edbClassifyInfo.BaseFromRadishResearchClassifyId) // 注意更新层级路径
+			edbClassifyInfo.ModifyTime = time.Now()
+			// 更改层级路径
+			edbClassifyInfo.LevelPath = fmt.Sprintf("%s,%d", parentEdbClassifyInfo.LevelPath, edbClassifyInfo.BaseFromRadishResearchClassifyId)
+			updateCol = append(updateCol, "ParentId", "RootId", "Level", "LevelPath", "ModifyTime", "LevelPath")
+			moveParent = true
+			moveParentId = edbClassifyInfo.BaseFromRadishResearchClassifyId
+		} else if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId == 0 {
+			errMsg = "移动失败"
+			err = errors.New("不支持目录层级变更")
+			return
+		}
+
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbClassifyInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+
+					}
+				}
+			}
+
+			edbClassifyInfo.Sort = prevSort + 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevEdbInfo == nil && nextEdbInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = GetRadishResearchClassifyMaxSort(parentClassifyId)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			edbClassifyInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstRadishResearchClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && !utils.IsErrNoRow(tmpErr) {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, firstClassify.BaseFromRadishResearchClassifyId-1, 0, updateSortStr)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstRadishResearchIndexByClassifyId(parentClassifyId)
+				if tErr != nil && !utils.IsErrNoRow(tErr) {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, firstEdb.BaseFromRadishResearchIndexId-1, updateSortStr)
+					_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
+				}
+			}
+
+			edbClassifyInfo.Sort = 0 //那就是排在第一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = edbClassifyInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+			//更新对应分类的root_id和层级
+			if oldParentId != parentClassifyId {
+				if len(classifyIds) > 0 {
+					levelStep := edbClassifyInfo.Level - oldLevel
+					err = data_manage.UpdateRadishResearchClassifyChildByParentClassifyId(classifyIds, edbClassifyInfo.RootId, levelStep)
+					if err != nil {
+						errMsg = "移动失败"
+						err = errors.New("更新子分类失败,Err:" + err.Error())
+						return
+					}
+				}
+			}
+		}
+	} else {
+		if edbInfo == nil {
+			errMsg = "当前指标不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		//如果改变了分类,那么移动该指标数据
+		if edbInfo.ClassifyId != parentClassifyId {
+			edbInfo.ClassifyId = parentClassifyId
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().ClassifyId, indexOb.Cols().ModifyTime)
+		}
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevClassify.Sort, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+					}
+
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, prevClassify.BaseFromRadishResearchClassifyId, prevSort, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.BaseFromRadishResearchIndexId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+					}
+				}
+			}
+
+			edbInfo.Sort = prevSort + 1
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().Sort, indexOb.Cols().ModifyTime)
+		} else if prevClassify == nil && nextClassify == nil && prevEdbInfo == nil && nextEdbInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+			maxSort, err = classifyOb.GetSortMax(parentClassifyId)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			edbInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().Sort, indexOb.Cols().ModifyTime)
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstRadishResearchClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && !utils.IsErrNoRow(tmpErr) {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, firstClassify.BaseFromRadishResearchClassifyId-1, 0, updateSortStr)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstRadishResearchIndexByClassifyId(parentClassifyId)
+				if tErr != nil && !utils.IsErrNoRow(tErr) {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateRadishResearchIndexSortByClassifyId(parentClassifyId, 0, firstEdb.BaseFromRadishResearchIndexId-1, updateSortStr)
+					_ = data_manage.UpdateRadishResearchClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr)
+				}
+			}
+
+			edbInfo.Sort = 0 //那就是排在第一位
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, indexOb.Cols().Sort, indexOb.Cols().ModifyTime)
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = edbInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+
+			// 更新ES
+			go func() {
+				indexItem := new(dataSourceModel.SearchDataSource)
+				indexItem.PrimaryId = edbInfo.BaseFromRadishResearchIndexId
+				indexItem.IndexCode = edbInfo.IndexCode
+				indexItem.IndexName = edbInfo.IndexName
+				indexItem.ClassifyId = edbInfo.ClassifyId
+				indexItem.Unit = edbInfo.Unit
+				indexItem.Frequency = edbInfo.Frequency
+				indexItem.StartDate = edbInfo.StartDate.Format(utils.FormatDate)
+				indexItem.EndDate = edbInfo.EndDate.Format(utils.FormatDate)
+				indexItem.LatestValue = fmt.Sprint(edbInfo.LatestValue)
+				indexItem.Source = utils.DATA_SOURCE_RADISH_RESEARCH
+				indexItem.SourceName = utils.DATA_SOURCE_NAME_RADISH_RESEARCH
+				indexItem.IsDeleted = 0
+				indexItem.CreateTime = edbInfo.CreateTime.Format(utils.FormatDateTime)
+				indexItem.ModifyTime = edbInfo.ModifyTime.Format(utils.FormatDateTime)
+
+				docId := fmt.Sprintf("%d-%d", utils.DATA_SOURCE_RADISH_RESEARCH, edbInfo.BaseFromRadishResearchIndexId)
+				if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
+					utils.FileLog.Warning("RadishResearch-写入指标ES失败, %v", e)
+					return
+				}
+			}()
+		}
+	}
+
+	// 更新子分类的LevelPath
+	if moveParent {
+		UpdateRadishResearchClassifyLevelPathRecursive(moveParentId)
+	}
+	return
+}
+
+func GetRadishResearchChildClassifyByClassifyId(targetClassifyId int) (targetList []*data_manage.BaseFromRadishResearchClassify, err error, errMsg string) {
+	//判断是否是挂在顶级目录下
+	targetClassify, err := data_manage.GetRadishResearchClassifyById(targetClassifyId)
+	if err != nil {
+		if utils.IsErrNoRow(err) {
+			errMsg = "当前分类不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		errMsg = "获取失败"
+		err = errors.New("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	orderStr := ` order by level asc, sort asc, base_from_radish_research_classify_id asc`
+	tmpList, err := data_manage.GetRadishResearchClassifyByRootIdLevel(targetClassify.RootId, orderStr)
+	if err != nil && !utils.IsErrNoRow(err) {
+		errMsg = "获取失败"
+		err = errors.New("获取数据失败,Err:" + err.Error())
+		return
+	}
+	idMap := make(map[int]struct{})
+	if len(tmpList) > 0 {
+		for _, v := range tmpList {
+			if v.BaseFromRadishResearchClassifyId == targetClassify.BaseFromRadishResearchClassifyId {
+				idMap[v.BaseFromRadishResearchClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ParentId]; ok {
+				idMap[v.BaseFromRadishResearchClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.BaseFromRadishResearchClassifyId]; ok {
+				targetItem := new(data_manage.BaseFromRadishResearchClassify)
+				targetItem.BaseFromRadishResearchClassifyId = v.BaseFromRadishResearchClassifyId
+				targetItem.ParentId = v.ParentId
+				targetItem.RootId = v.RootId
+				//targetItem.UniqueCode = v.UniqueCode
+				targetItem.Level = v.Level
+				targetItem.ClassifyName = v.ClassifyName
+				//targetItem.IsJoinPermission = v.IsJoinPermission
+				targetList = append(targetList, targetItem)
+			}
+		}
+	}
+
+	return
+}
+
+func GetRadishResearchClassifyMaxSort(parentId int) (maxSort int, err error) {
+	//获取该层级下最大的排序数
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	classifyMaxSort, err := classifyOb.GetSortMax(parentId)
+	if err != nil {
+		return
+	}
+	maxSort = classifyMaxSort
+	edbMaxSort, err := data_manage.GetRadishResearchIndexMaxSortByClassifyId(parentId)
+	if err != nil {
+		return
+	}
+	if maxSort < edbMaxSort {
+		maxSort = edbMaxSort
+	}
+	return
+}
+
+// UpdateRadishResearchClassifyLevelPathRecursive 分类父级ID变更后, 递归更新分类LevelPath
+func UpdateRadishResearchClassifyLevelPathRecursive(parentId int) {
+	fmt.Println("ParentId", parentId)
+	classifyOb := new(data_manage.BaseFromRadishResearchClassify)
+	cond := fmt.Sprintf(` AND %s = ?`, classifyOb.Cols().ParentId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, parentId)
+	classifies, e := classifyOb.GetItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		utils.FileLog.Warning("UpdateRadishResearchClassifyLevelPathRecursive, errMsg: %v", e)
+		return
+	}
+	if len(classifies) == 0 {
+		return
+	}
+
+	// 批量更新子分类的LevelPath
+	parentClassify, e := classifyOb.GetItemById(parentId)
+	if e != nil {
+		if utils.IsErrNoRow(e) {
+			return
+		}
+		utils.FileLog.Warning("UpdateRadishResearchClassifyLevelPathRecursive, parent errMsg: %v", e)
+		return
+	}
+	if parentClassify != nil && parentClassify.BaseFromRadishResearchClassifyId <= 0 {
+		return
+	}
+
+	// 继续递归处理
+	for _, v := range classifies {
+		e = classifyOb.UpdateLevelPath(v.BaseFromRadishResearchClassifyId, fmt.Sprintf("%s,%d", parentClassify.LevelPath, v.BaseFromRadishResearchClassifyId))
+		if e != nil {
+			utils.FileLog.Warning("UpdateRadishResearchClassifyLevelPathRecursive, update errMsg: %v", e)
+			continue
+		}
+		UpdateRadishResearchClassifyLevelPathRecursive(v.BaseFromRadishResearchClassifyId)
+	}
+	return
+}

+ 88 - 15
services/data/base_from_sci_hq.go

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

+ 157 - 15
services/data/chart_classify.go

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

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